diff options
492 files changed, 38238 insertions, 4732 deletions
diff --git a/app-admin/apache-tools/apache-tools-2.4.69.ebuild b/app-admin/apache-tools/apache-tools-2.4.69.ebuild index a6ac248cc099..e0b32ac004e1 100644 --- a/app-admin/apache-tools/apache-tools-2.4.69.ebuild +++ b/app-admin/apache-tools/apache-tools-2.4.69.ebuild @@ -16,7 +16,7 @@ S="${WORKDIR}/httpd-${PV}" LICENSE="Apache-2.0" SLOT="0" -KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-macos ~x64-solaris" +KEYWORDS="~alpha amd64 arm arm64 ~hppa ~loong ~mips ppc ppc64 ~riscv ~s390 ~sparc x86 ~x64-macos ~x64-solaris" IUSE="ssl" RDEPEND=" diff --git a/app-admin/keepassxc/Manifest b/app-admin/keepassxc/Manifest index 06f99e1d9877..fdf03a034cf2 100644 --- a/app-admin/keepassxc/Manifest +++ b/app-admin/keepassxc/Manifest @@ -1,2 +1,3 @@ +DIST keepassxc-2.8.0_beta1.gh.tar.gz 14174746 BLAKE2B 2c71308a71010edab72171aca525178572c0c7ef4d81281db8e88870d5861392821db10f59728da2502ce5d2a47d531c114c8eef7ff746a40ce378f55e29b16a SHA512 7da0314ece6f7d53d483e635389f582e1932b038a368a8304c167cccce0b18f18feffe862d5f3efcb8a570bcd07eb31c58054621900e10abd0d7bfc947696e3b DIST keepassxc-2.8.0_pre20260504.tar.gz 13908020 BLAKE2B 739d25c24291c2daf833541c11447c9983c031926843976299a6a5e1d4be5a681e13aa551ffeecd03aa0d610d96275bba23f679be4ecd30b2d269ffda76143db SHA512 20fefe6d7363d80965c119f83afb8c73e4c9f2b7075b39f0501d6d42d7d1f35df0c8388b6aa4a3c8ed851f92472ec668aefd0d20de6a1dfc80e2a2e5858350fe DIST keepassxc-2.8.0_pre20260629.tar.gz 13931714 BLAKE2B 0df94fc7d7040da805f681f8c8c7f46faf260dfbaabf616f2e3375f59ff4ae5c39b15ac9a291371bf3e1fad11344c68937d1dbf813351f22bd6c92f77ddf9005 SHA512 5e811b6cbe30466b5b2e6129510b8f9f49910081db421a02494a1920dadbf5fbd39c5a524ab6f48e2bc01a0703c2fc2edf77a8fa0b1773f8b1f91ea67cecaf42 diff --git a/app-admin/keepassxc/keepassxc-2.8.0_beta1.ebuild b/app-admin/keepassxc/keepassxc-2.8.0_beta1.ebuild new file mode 100644 index 000000000000..6afc98e9b748 --- /dev/null +++ b/app-admin/keepassxc/keepassxc-2.8.0_beta1.ebuild @@ -0,0 +1,109 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake xdg + +DESCRIPTION="KeePassXC - KeePass Cross-platform Community Edition" +HOMEPAGE="https://keepassxc.org" + +if [[ "${PV}" = *9999* ]] ; then + inherit git-r3 + + EGIT_BRANCH="develop" + EGIT_REPO_URI="https://github.com/keepassxreboot/${PN}" +else + if [[ "${PV}" == *_beta* ]] ; then + SRC_URI="https://github.com/keepassxreboot/${PN}/archive/${PV/_/-}.tar.gz + -> ${P}.gh.tar.gz" + S="${WORKDIR}/${P/_/-}" + else + SRC_URI="https://github.com/keepassxreboot/${PN}/archive/${PV}.tar.gz + -> ${P}.gh.tar.gz" + fi + + KEYWORDS="~amd64 ~arm64 ~ppc64 ~riscv ~x86" +fi + +# COPYING order +LICENSE="|| ( GPL-2 GPL-3 ) BSD LGPL-2.1 MIT LGPL-2 CC0-1.0 Apache-2.0 GPL-2+ BSD-2" +SLOT="0" +IUSE="X browser doc +keyring +network +ssh-agent test" + +RESTRICT="!test? ( test )" + +RDEPEND=" + dev-libs/botan:3= + dev-libs/libusb:1 + >=dev-libs/zxcvbn-c-2.6 + dev-qt/qtbase:6[concurrent,dbus,network] + dev-qt/qtsvg:6 + media-gfx/qrencode:= + sys-apps/pcsc-lite + sys-apps/keyutils:= + sys-libs/readline:= + virtual/minizip:= + virtual/zlib:= + X? ( + dev-qt/qtbase:6[X] + x11-libs/libXext + x11-libs/libX11 + x11-libs/libXi + x11-libs/libXtst + ) +" +DEPEND=" + ${RDEPEND} +" +BDEPEND=" + dev-qt/qttools:6[linguist] + doc? ( + dev-ruby/asciidoctor + ) +" + +PATCHES=( + "${FILESDIR}/${PN}-2.8.0-cmake_minimum.patch" +) + +CMAKE_SKIP_TESTS=( + testcli +) + +src_prepare() { + if ! [[ "${PV}" =~ _beta|9999 ]]; then + echo "${PV}" > .version || die + fi + + # Unbundle zxcvbn, bug 958062 + rm -r ./src/thirdparty/zxcvbn || die + + cmake_src_prepare +} + +src_configure() { + local -a mycmakeargs=( + # Gentoo users enable ccache via e.g. FEATURES=ccache or + # other means. We don't want the build system to enable it for us. + -DWITH_CCACHE="OFF" + -DWITH_GUI_TESTS="OFF" + -DKPXC_FEATURE_UPDATES="OFF" + + -DWITH_TESTS="$(usex test)" + -DKPXC_FEATURE_BROWSER="$(usex browser)" + -DKPXC_FEATURE_DOCS="$(usex doc)" + -DKPXC_FEATURE_FDOSECRETS="$(usex keyring)" + -DKPXC_FEATURE_NETWORK="$(usex network)" + -DKPXC_FEATURE_SSHAGENT="$(usex ssh-agent)" + -DWITH_X11="$(usex X)" + ) + + if [[ "${PV}" == *_beta* ]] ; then + mycmakeargs+=( + -DOVERRIDE_VERSION="${PV/_/-}" + ) + fi + + cmake_src_configure +} diff --git a/app-admin/supervisor/Manifest b/app-admin/supervisor/Manifest index 32b8d996e02d..be880b03877f 100644 --- a/app-admin/supervisor/Manifest +++ b/app-admin/supervisor/Manifest @@ -1,3 +1 @@ -DIST supervisor-4.2.5.tar.gz 466073 BLAKE2B 54b45c824a1ad2960b04ca4ca9e54337f8c4da1a13008b559103f9efb9043588e72b3ee97c41218eb9252606d717575ad2615d12136047734f83d843d0a63bfa SHA512 ea80c8c91356646deccf20735e065fd9b341f3be6d56838d333989297a912b0a59142338925b2eb08690e038f0617814e03447673701a19093aa483432ce6d41 -DIST supervisor-4.2.5_p20250125.gh.tar.gz 446525 BLAKE2B 33372d7424a8d5b500a8dcbcc25f9d2e678155f570936c9be337b443fee65243b2c632c0b429c4fd2b9133243a864cb7297f1640c5d843d6439bf1fb1c06a3c4 SHA512 53e2d798ef9663f471ed05b76dad23b96ed65c7f4b40d7ab837586dd58fda1dcb471f7cfbcdf014c6a830ddb2c7457aa6a5569a1ffe1cfa576e2f9c683149478 DIST supervisor-4.3.0.tar.gz 468912 BLAKE2B 16fe4f09b1fdbc9b18c67ba1d68425772fbfadc5417b39926dbbad55aca833c082566c924baa5fa9d1accc8ce518da23751d69fcd2c685ead5be57f42108a633 SHA512 1f781533f95f856baccf0959af47a4c62cc197f0b2cb8ccc35bca8f1406fdb0b5f996262e5d31442f3bb73fd31847621361ffc5b9008cb6b7226c7c3adc4ff9f diff --git a/app-admin/supervisor/files/supervisor-4.3.0-fix-qa-warnings.patch b/app-admin/supervisor/files/supervisor-4.3.0-fix-qa-warnings.patch deleted file mode 100644 index 0b8769e02295..000000000000 --- a/app-admin/supervisor/files/supervisor-4.3.0-fix-qa-warnings.patch +++ /dev/null @@ -1,45 +0,0 @@ -https://github.com/Supervisor/supervisor/issues/1560 -https://github.com/Supervisor/supervisor/pull/1675 -upstream will not merge soon as it's py3 only (find_namespace_packages) ---- a/setup.py -+++ b/setup.py -@@ -34,7 +34,7 @@ - 'pytest-cov', - ] - --from setuptools import setup, find_packages -+from setuptools import setup, find_namespace_packages - here = os.path.abspath(os.path.dirname(__file__)) - try: - with open(os.path.join(here, 'README.rst'), 'r') as f: -@@ -91,15 +91,19 @@ - classifiers=CLASSIFIERS, - author="Chris McDonough", - author_email="chrism@plope.com", -- packages=find_packages(), -+ packages=find_namespace_packages(exclude=["docs","supervisor.tests","supervisor.tests.*"]), -+ package_dir={"": "."}, -+ package_data={ -+ "supervisor": ["version.txt"], -+ "supervisor.ui": ["**/*"], -+ "supervisor.skel": ["*"] -+ }, - install_requires=requires, - extras_require={ - 'testing': testing_extras, - }, -- tests_require=tests_require, - include_package_data=True, - zip_safe=False, -- test_suite="supervisor.tests", - entry_points={ - 'console_scripts': [ - 'supervisord = supervisor.supervisord:main', ---- a/setup.cfg -+++ b/setup.cfg -@@ -2,4 +2,4 @@ - zip_ok = false - - [bdist_wheel] --universal = 1 -+universal = 0 diff --git a/app-admin/supervisor/supervisor-4.2.5-r1.ebuild b/app-admin/supervisor/supervisor-4.2.5-r1.ebuild deleted file mode 100644 index b375d11588ad..000000000000 --- a/app-admin/supervisor/supervisor-4.2.5-r1.ebuild +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright 1999-2025 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -DISTUTILS_USE_PEP517=setuptools -PYTHON_COMPAT=( python3_{13..14} ) -# xml.etree.ElementTree module required. -PYTHON_REQ_USE="xml(+)" - -inherit distutils-r1 systemd pypi - -DESCRIPTION="A system for controlling process state under UNIX" -HOMEPAGE="https://supervisord.org/ https://pypi.org/project/supervisor/" - -LICENSE="repoze ZPL BSD HPND GPL-2" -SLOT="0" -KEYWORDS="amd64 ~arm64 ~riscv x86" - -# setuptools is required because the package uses pkg_resources -RDEPEND=" - acct-group/supervisor - dev-python/setuptools[${PYTHON_USEDEP}] -" - -distutils_enable_sphinx docs -distutils_enable_tests pytest - -python_install_all() { - distutils-r1_python_install_all - newinitd "${FILESDIR}/init.d-r2" supervisord - newconfd "${FILESDIR}/conf.d-r1" supervisord - dodoc supervisor/skel/sample.conf - keepdir /etc/supervisord.d - insinto /etc - doins "${FILESDIR}/supervisord.conf" - keepdir /var/log/supervisor - systemd_dounit "${FILESDIR}/supervisord.service" -} - -pkg_preinst() { - fowners :supervisor /var/log/supervisor - fperms 750 /var/log/supervisor -} - -pkg_postinst() { - if [[ -z "${REPLACING_VERSIONS}" ]]; then - # This is a new installation - elog "You may install your configuration files in ${EROOT}/etc/supervisord.d" - elog "For config examples, see ${EROOT}/usr/share/doc/${PF}/sample.conf.bz2" - elog "" - elog "By default, only members of the supervisor group can run supervisorctl." - fi -} diff --git a/app-admin/supervisor/supervisor-4.2.5_p20250125.ebuild b/app-admin/supervisor/supervisor-4.2.5_p20250125.ebuild deleted file mode 100644 index f40c9ceda3b3..000000000000 --- a/app-admin/supervisor/supervisor-4.2.5_p20250125.ebuild +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 1999-2025 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -DISTUTILS_USE_PEP517=setuptools -PYTHON_COMPAT=( python3_{13..14} ) -# xml.etree.ElementTree module required. -PYTHON_REQ_USE="xml(+)" - -inherit distutils-r1 systemd - -COMMIT="4bf1e57cbf292ce988dc128e0d2c8917f18da9be" -DESCRIPTION="A system for controlling process state under UNIX" -HOMEPAGE="https://supervisord.org/ https://pypi.org/project/supervisor/" -SRC_URI="https://github.com/Supervisor/supervisor/archive/${COMMIT}.tar.gz -> ${P}.gh.tar.gz" -S="${WORKDIR}/${PN}-${COMMIT}" - -LICENSE="repoze ZPL BSD HPND GPL-2" -SLOT="0" -KEYWORDS="amd64 ~arm64 ~riscv x86" - -RDEPEND="acct-group/supervisor" - -PATCHES=( - # https://github.com/Supervisor/supervisor/issues/1560 - "${FILESDIR}"/${PN}-4.3.0-fix-qa-warnings.patch -) - -distutils_enable_sphinx docs -distutils_enable_tests pytest - -python_install_all() { - distutils-r1_python_install_all - newinitd "${FILESDIR}/init.d-r2" supervisord - newconfd "${FILESDIR}/conf.d-r1" supervisord - dodoc supervisor/skel/sample.conf - keepdir /etc/supervisord.d - insinto /etc - doins "${FILESDIR}/supervisord.conf" - keepdir /var/log/supervisor - systemd_dounit "${FILESDIR}/supervisord.service" -} - -pkg_preinst() { - fowners :supervisor /var/log/supervisor - fperms 750 /var/log/supervisor -} - -pkg_postinst() { - if [[ -z "${REPLACING_VERSIONS}" ]]; then - # This is a new installation - elog "You may install your configuration files in ${EROOT}/etc/supervisord.d" - elog "For config examples, see ${EROOT}/usr/share/doc/${PF}/sample.conf.bz2" - elog "" - elog "By default, only members of the supervisor group can run supervisorctl." - fi -} diff --git a/app-backup/rdiff-backup/Manifest b/app-backup/rdiff-backup/Manifest index dda020305ef0..392722e3f2e2 100644 --- a/app-backup/rdiff-backup/Manifest +++ b/app-backup/rdiff-backup/Manifest @@ -1 +1,2 @@ DIST rdiff-backup-2.2.6.tar.gz 944414 BLAKE2B f30bc570f8e20d950e87654d195f29848b6ca39953fc418b9ff391da649fdd308bebcd0379b047a1875eb1fa9b85c294df6a0528f06d4ba27627df4e12a771a4 SHA512 7370dcbb71f087252f8c599b0fbf58260ec7e026e023648bfab63c39b062120862ab8b60173e2bf2352e16d404bb561c72fe19aa9aed9d49cff56225b91f5c63 +DIST rdiff_backup-2.2.7.tar.gz 950776 BLAKE2B aba298c9e6b550b3af74675468d8cb10b5e36e6266c310c979d6f4ef55754254169e82af8243d9b9bead72732e39545c6d7e933d7f2963cefc6a42a01e40f5ca SHA512 822b86550ca49f4a0dde8bbcfac49152f3eb5f3554fdf8dad2311e0b4323985e75dcd1a689a93046f61a88beb6267d08f03e20cd576d7ff584bc15c9c45b9875 diff --git a/app-backup/rdiff-backup/rdiff-backup-2.2.7.ebuild b/app-backup/rdiff-backup/rdiff-backup-2.2.7.ebuild new file mode 100644 index 000000000000..3d461c4a8006 --- /dev/null +++ b/app-backup/rdiff-backup/rdiff-backup-2.2.7.ebuild @@ -0,0 +1,30 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +PYTHON_COMPAT=( python3_{13..14} ) +DISTUTILS_EXT=1 +DISTUTILS_USE_PEP517=setuptools +inherit distutils-r1 pypi + +DESCRIPTION="Local/remote mirroring+incremental backup" +HOMEPAGE="https://rdiff-backup.net/ https://github.com/rdiff-backup/rdiff-backup" + +LICENSE="GPL-2+" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x64-macos" +# Tests use a tox environment and separate steps for test env preparation +RESTRICT="test" + +RDEPEND="dev-python/pylibacl[${PYTHON_USEDEP}] + dev-python/pyxattr[${PYTHON_USEDEP}] + dev-python/pyyaml[${PYTHON_USEDEP}] + >=net-libs/librsync-1.0:0=" +DEPEND="${RDEPEND} + dev-python/setuptools-scm[${PYTHON_USEDEP}]" + +src_prepare() { + sed -e "s#share/doc/${PN}#share/doc/${PF}#" -i setup.py || die + default +} diff --git a/app-containers/buildah/Manifest b/app-containers/buildah/Manifest index 02c2894f8a5f..b8d76a29b5d4 100644 --- a/app-containers/buildah/Manifest +++ b/app-containers/buildah/Manifest @@ -1,4 +1,3 @@ DIST buildah-1.41.0.tar.gz 11971274 BLAKE2B af197271bf519f84d1e7d08cbaab6a4aef3d1bdc234eaf8127f33e1fdd0c3f66c09262bf1aef9776d8d3fdfd853ae23cccd4ae6fcd06b2f86b7769d82f71617a SHA512 50be5c400a5063c12d5b97dc285d4969e50e88fa21de537f1c2317df36a1ccabf26ba70ece9e3e5c891e76ee8d85ab946029dc240de40ec88c86ad678e963823 -DIST buildah-1.44.0.tar.gz 10888725 BLAKE2B 409046d73dbc4ffb835a37e26c28111f6c1f28c165ab6c979738fc6459b81edfdcfe468337b39a2edce018cb568df32b2868e6c46a5b414eb005cfb4d2546b8c SHA512 660f29ff9b8c8f7f3776e96391bb803feb54622c92aaf78f5ac37783bdc77573353acd5f692502b61169ad58f6b89a8e8268ec41c0cb080dfec3f8466dd6419e -DIST buildah-1.44.1.tar.gz 10812896 BLAKE2B 5d36c37828b355fa25f0911584cbd19aab2bf63a7dccfd55dcf0802efddf34cda389fc0c118d9b210b73840da443785b33ca1035367e68e03878b43fd5205750 SHA512 ece4a05bfae24e997a4520974f396d77728387bdd2d996dbcef27f491b44d97c916b8b2d5afa272929b658d13d924992c8c73ecd7d944d08259504a390cfacbc DIST buildah-1.45.0.tar.gz 11415484 BLAKE2B 6e3e585b0f75d60f129cd25ab87da8162cadf510c2e180a0470bf116beb2c21a795abced8b3c925e61a2300db56bb13d812fef987679d72829491a1efa22e408 SHA512 570b68c0b6791e2e832b6e39906373da058308c017f6b460a658466e281d3b9eac5b5b842488e658438bb41c9926c160a90c956ba12ba57afcef88f523354ffe +DIST buildah-1.46.0.tar.gz 11885884 BLAKE2B 18a1b95093371f158543ebfc4a11968c6700b5d3940cc565bd14093d9d9237ebdadb04342b297710c453f5f6b17b8332dffabdef0e5342da40e8cd4d3df07acf SHA512 965d70e8d77f83267c0b6ecc12ccc5849939e802a35a26d99d8d6418e936ad2a505ebadc9d26be073e23c8c2d964f7902ce9bb496327aca40b72a9c9e52b4aa1 diff --git a/app-containers/buildah/buildah-1.44.0-r1.ebuild b/app-containers/buildah/buildah-1.44.0-r1.ebuild deleted file mode 100644 index fe5148fe96d1..000000000000 --- a/app-containers/buildah/buildah-1.44.0-r1.ebuild +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -inherit go-module linux-info toolchain-funcs - -DESCRIPTION="A tool that facilitates building OCI images" -HOMEPAGE="https://github.com/containers/buildah" - -# main pkg -LICENSE="Apache-2.0" -# deps -LICENSE+=" BSD BSD-2 CC-BY-SA-4.0 ISC MIT MPL-2.0" - -SLOT="0" -IUSE="apparmor btrfs +seccomp selinux systemd test" -RESTRICT="test" - -if [[ ${PV} == 9999* ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/containers/buildah.git" -else - SRC_URI="https://github.com/containers/buildah/archive/v${PV}.tar.gz -> ${P}.tar.gz" - KEYWORDS="~amd64 ~arm64" -fi - -RDEPEND=" - >=app-containers/container-libs-0.68.0[extra(-)] - app-crypt/gpgme:= - dev-db/sqlite:3= - dev-libs/libgpg-error:= - dev-libs/libassuan:= - sys-apps/shadow:= - apparmor? ( sys-libs/libapparmor:= ) - btrfs? ( sys-fs/btrfs-progs ) - seccomp? ( sys-libs/libseccomp:= ) - selinux? ( sec-policy/selinux-podman sys-libs/libselinux:= ) - systemd? ( sys-apps/systemd ) -" -DEPEND="${RDEPEND}" -BDEPEND=" - dev-go/go-md2man - >=dev-lang/go-1.25.6 -" - -DOCS=( CHANGELOG.md troubleshooting.md docs/tutorials ) - -pkg_pretend() { - local CONFIG_CHECK="" - use btrfs && CONFIG_CHECK+=" ~BTRFS_FS" - check_extra_config - - linux_config_exists || ewarn "Cannot determine configuration of your kernel." -} - -src_prepare() { - default - - # ensure all necessary files are there - local file - for file in docs/Makefile hack/libsubid_tag.sh hack/apparmor_tag.sh \ - hack/systemd_tag.sh btrfs_installed_tag.sh; do - [[ -f "${file}" ]] || die - done - - sed -i -e "s|/usr/local|/usr|g" Makefile docs/Makefile || die - echo -e '#!/usr/bin/env bash\necho libsubid' > hack/libsubid_tag.sh || die - - cat <<-EOF > hack/apparmor_tag.sh || die - #!/usr/bin/env bash - $(usex apparmor 'echo apparmor' echo) - EOF - - use seccomp || eapply "${FILESDIR}/${PN}-1.37.5-disable-seccomp.patch" - - cat <<-EOF > hack/systemd_tag.sh || die - #!/usr/bin/env bash - $(usex systemd 'echo systemd' echo) - EOF - - cat <<-EOF > btrfs_installed_tag.sh || die - #!/usr/bin/env bash - $(usex btrfs echo 'echo exclude_graphdriver_btrfs') - EOF - - # hardcode using system sqlite instead of bundled one - [[ -f hack/sqlite_tag.sh ]] && echo -e '#!/usr/bin/env bash\necho libsqlite3' > hack/sqlite_tag.sh || die - - use test || eapply "${FILESDIR}/${PN}-1.44.0-disable-tests.patch" -} - -src_compile() { - # For non-live versions, prevent git operations which causes sandbox violations - # https://github.com/gentoo/gentoo/pull/33531#issuecomment-1786107493 - [[ ${PV} != 9999* ]] && export COMMIT_NO="" GIT_COMMIT="" - - tc-export AS LD STRIP - export GOMD2MAN="$(command -v go-md2man)" - export SELINUXOPT= - default -} - -src_test() { - emake test-unit -} - -src_install() { - emake DESTDIR="${ED}" SELINUXOPT= install install.completions - einstalldocs -} diff --git a/app-containers/buildah/buildah-1.44.1.ebuild b/app-containers/buildah/buildah-1.46.0.ebuild index 418d5505dac2..a51349da7f9e 100644 --- a/app-containers/buildah/buildah-1.44.1.ebuild +++ b/app-containers/buildah/buildah-1.46.0.ebuild @@ -35,17 +35,16 @@ RDEPEND=" apparmor? ( sys-libs/libapparmor:= ) btrfs? ( sys-fs/btrfs-progs ) seccomp? ( sys-libs/libseccomp:= ) - selinux? ( sec-policy/selinux-podman sys-libs/libselinux:= ) + selinux? ( sec-policy/selinux-podman ) systemd? ( sys-apps/systemd ) " DEPEND="${RDEPEND}" BDEPEND=" dev-go/go-md2man - >=dev-lang/go-1.25.6 + >=dev-lang/go-1.26.8 " DOCS=( CHANGELOG.md troubleshooting.md docs/tutorials ) -PATCHES=( "${FILESDIR}/buildah-1.44.1-pr6984-dontbuild-on-install.patch" ) pkg_pretend() { if use btrfs; then @@ -88,7 +87,7 @@ src_prepare() { # hardcode using system sqlite instead of bundled one [[ -f hack/sqlite_tag.sh ]] && echo -e '#!/usr/bin/env bash\necho libsqlite3' > hack/sqlite_tag.sh || die - use test || eapply "${FILESDIR}/${PN}-1.44.1-disable-tests.patch" + use test || eapply "${FILESDIR}/${PN}-1.45.0-disable-tests.patch" } src_compile() { @@ -98,7 +97,7 @@ src_compile() { tc-export AS LD STRIP export GOMD2MAN="$(command -v go-md2man)" - emake binaries + emake SELINUXOPT= binaries emake -C docs } diff --git a/app-containers/buildah/buildah-9999.ebuild b/app-containers/buildah/buildah-9999.ebuild index a7658b95c855..a51349da7f9e 100644 --- a/app-containers/buildah/buildah-9999.ebuild +++ b/app-containers/buildah/buildah-9999.ebuild @@ -41,11 +41,10 @@ RDEPEND=" DEPEND="${RDEPEND}" BDEPEND=" dev-go/go-md2man - >=dev-lang/go-1.25.9 + >=dev-lang/go-1.26.8 " DOCS=( CHANGELOG.md troubleshooting.md docs/tutorials ) -PATCHES=( "${FILESDIR}/buildah-1.44.1-pr6984-dontbuild-on-install.patch" ) pkg_pretend() { if use btrfs; then diff --git a/app-containers/buildah/files/buildah-1.44.0-disable-tests.patch b/app-containers/buildah/files/buildah-1.44.0-disable-tests.patch deleted file mode 100644 index 7c3eb88316e7..000000000000 --- a/app-containers/buildah/files/buildah-1.44.0-disable-tests.patch +++ /dev/null @@ -1,8 +0,0 @@ ---- a/Makefile -+++ b/Makefile -@@ -62,1 +62,1 @@ --all: bin/buildah bin/imgtype bin/copy bin/inet bin/tutorial bin/dumpspec bin/passwd bin/crash bin/wait bin/grpcnoop docs -+all: bin/buildah docs -@@ -122 +122 @@ --docs: install.tools ## build the docs on the host -+docs: ## build the docs on the host diff --git a/app-containers/buildah/files/buildah-1.44.1-disable-tests.patch b/app-containers/buildah/files/buildah-1.44.1-disable-tests.patch deleted file mode 100644 index 2f2be6968287..000000000000 --- a/app-containers/buildah/files/buildah-1.44.1-disable-tests.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/Makefile b/Makefile -index c024f0e..a911373 100644 ---- a/Makefile -+++ b/Makefile -@@ -60,7 +60,7 @@ export GOLANGCI_LINT_VERSION := 2.12.2 - # use source debugging tools like delve. - all: binaries docs - --binaries: bin/buildah bin/imgtype bin/copy bin/inet bin/tutorial bin/dumpspec bin/passwd bin/crash bin/wait bin/grpcnoop -+binaries: bin/buildah - - bin/buildah: $(SOURCES) internal/mkcw/embed/entrypoint_amd64.gz - $(GO_BUILD) $(BUILDAH_LDFLAGS) $(GO_GCFLAGS) "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah diff --git a/app-containers/container-libs/Manifest b/app-containers/container-libs/Manifest index f2f0d154bded..c9db245bd23e 100644 --- a/app-containers/container-libs/Manifest +++ b/app-containers/container-libs/Manifest @@ -1,2 +1,3 @@ DIST container-libs-0.68.0.tar.gz 13304245 BLAKE2B 73a578c45cd2fcf5d1481a17406c2b15472d2ae51d34154e847750fc32a5a8b6631404ef2bd261dec1d35a65fbe8b38d0d0053c6d1b0ecf31197025955d16173 SHA512 2725689c41904886c22de3d70a0fdcc93f36e588f2a49970d5ad94dfbcc2029581c3ef824ca7f9f01a52935a9703e41fa184132c61e1daac3f221966326bea40 DIST container-libs-0.69.0.tar.gz 13694913 BLAKE2B 85c79693473d67f1d6fddb2f9aa1c4b8e10863f28e4cf83d9d467cf69c306fcac3b444d4aaabb312ce694dd71581ff2ce17c4d2ff9c9c133932826f69eb00a39 SHA512 db2a199a702291b035826c0a27eacdfe2aa6d8a75de52d1c4c76718b1843b5960980ad9735d5d9e18f7f5ca0a5159a54316e8c74f0f17395ef94fd59f43dcaa6 +DIST container-libs-0.70.0.tar.gz 14154969 BLAKE2B b3630a60de9a5bc06c4797a20883b0c61f106c9d31a0e54b34edb84f0061e65242b578d2420303cb6e1c4bb7e3e7c559521227f109d799324b2baa1fa5feab58 SHA512 9f42e25bdce8ffd4d377da4d4e028eda24f939f2dd56319375fee01e3c971b9a778da852579d0e216bd0f7773588ed5be8ad537218e42e624865a767185f0ec8 diff --git a/app-containers/container-libs/container-libs-0.68.0.ebuild b/app-containers/container-libs/container-libs-0.70.0.ebuild index 02be3352887e..51b45a5d6973 100644 --- a/app-containers/container-libs/container-libs-0.68.0.ebuild +++ b/app-containers/container-libs/container-libs-0.70.0.ebuild @@ -13,24 +13,29 @@ S=${WORKDIR}/${PN}-common-v${PV} LICENSE="Apache-2.0" SLOT="0" KEYWORDS="~amd64 ~arm64 ~loong ~riscv" +IUSE="+extra" RESTRICT="test" RDEPEND=" - >=app-containers/aardvark-dns-2.0.0 - >=app-containers/crun-1.25.1 - >=app-containers/netavark-2.0.0 - net-firewall/nftables - >=net-misc/passt-2026.05.26 + app-containers/containers-shortnames >=sys-fs/fuse-overlayfs-1.16 + extra? ( + >=app-containers/crun-1.25.1 + >=app-containers/netavark-2.0.0 + >=net-misc/passt-2026.05.26 + ) !app-containers/containers-image !app-containers/containers-common - !app-containers/containers-shortnames !app-containers/containers-storage !<app-containers/buildah-1.44.0 !<app-containers/podman-6.0.0 !<app-containers/skopeo-1.23 " -BDEPEND=">=dev-go/go-md2man-2.0.2" +DEPEND="${RDEPEND}" +BDEPEND=">=dev-go/go-md2man-2.0.7" +PATCHES=( + "${FILESDIR}"/c-libs-0.70.0-remove-go-cc.patch +) DOC_CONTENTS=" For rootless operations, one needs to configure subuid(5) and subgid(5). @@ -41,29 +46,33 @@ usermod --add-subgids 1065536-1131071 <rootless user> " src_compile() { - emake -C common GO=true docs - emake -C image GO=true docs - emake -C storage GO=true docs + emake -C common PREFIX="${EPREFIX}/usr" docs + emake -C image docs + emake -C storage docs touch {images,layers}.lock || die } src_install() { - emake -C common GO=true DESTDIR="${ED}" PREFIX="/usr" install - emake -C image GO=true PREFIX="${ED}/usr" CONTAINERSCONFDIR="${ED}/etc/containers" install - emake -C storage GO=true DESTDIR="${ED}" install + emake -C common DESTDIR="${D}" PREFIX="${EPREFIX}/usr" install + emake -C image DESTDIR="${ED}" install + emake -C storage DESTDIR="${ED}" install readme.gentoo_create_doc insinto /usr/share/containers - doins common/pkg/{seccomp/seccomp.json,subscriptions/mounts.conf} storage/storage.conf + doins common/pkg/{seccomp/seccomp.json,subscriptions/mounts.conf} storage/storage.conf image/registries.conf - keepdir /etc/containers/{certs.d,oci/hooks.d,networks,systemd} /var/lib/containers/{sigstore,storage} + keepdir /etc/containers/{certs.d,oci/hooks.d,networks,systemd,registries.conf.d,registries.d} \ + /var/lib/containers/sigstore /usr/share/containers/systemd diropts -m0700 dodir /usr/lib/containers/storage/overlay-{images,layers} + local i for i in images layers; do insinto /usr/lib/containers/storage/overlay-"${i}" doins "${i}".lock done + insinto /usr/share/containers/registries.conf.d + newins "${FILESDIR}/c-libs-0.68.0-registries.conf" 00-gentoo.conf } pkg_postinst() { diff --git a/app-containers/container-libs/files/c-libs-0.70.0-remove-go-cc.patch b/app-containers/container-libs/files/c-libs-0.70.0-remove-go-cc.patch new file mode 100644 index 000000000000..3fd28099a212 --- /dev/null +++ b/app-containers/container-libs/files/c-libs-0.70.0-remove-go-cc.patch @@ -0,0 +1,39 @@ +these lines add dev-lang/go and cc as BDEPEND unnecessarily so remove them +diff --git a/image/Makefile b/image/Makefile +index 9205e83..8b22f3e 100644 +--- a/image/Makefile ++++ b/image/Makefile +@@ -1,11 +1,6 @@ + export GOPROXY=https://proxy.golang.org + + +-GOBIN := $(shell go env GOBIN) +-ifeq ($(GOBIN),) +-GOBIN := $(shell go env GOPATH)/bin +-endif +- + # when cross compiling _for_ a Darwin or windows host, then we must use openpgp + BUILD_TAGS_WINDOWS_CROSS = containers_image_openpgp + BUILD_TAGS_DARWIN_CROSS = containers_image_openpgp +@@ -18,7 +13,6 @@ BUILDFLAGS := -tags "$(BUILDTAGS)" -ldflags '-X go.podman.io/image/v5/signature/ + TESTFLAGS := + TEST_PACKAGES := ./... + +-PACKAGES := $(shell go list $(BUILDFLAGS) ./...) + SOURCE_DIRS = $(shell echo $(PACKAGES) | awk 'BEGIN{FS="/"; RS=" "}{print $$4}' | uniq) + + PREFIX ?= ${DESTDIR}/usr +diff --git a/storage/Makefile b/storage/Makefile +index 064ed6b..0b95b92 100644 +--- a/storage/Makefile ++++ b/storage/Makefile +@@ -21,9 +21,7 @@ + test-unit + + NATIVETAGS := +-AUTOTAGS := $(shell ./hack/btrfs_tag.sh) $(shell ./hack/libsubid_tag.sh) + BUILDFLAGS := -tags "$(AUTOTAGS) $(TAGS)" $(FLAGS) +-GO ?= go + TESTFLAGS ?= + + default all: local-binary docs local-cross ## build and cross-build\nbinaries and docs diff --git a/app-containers/podman/Manifest b/app-containers/podman/Manifest index 0d38ec222fb2..f413a87c0598 100644 --- a/app-containers/podman/Manifest +++ b/app-containers/podman/Manifest @@ -1,10 +1,3 @@ -DIST podman-5.7.1.tar.gz 21266433 BLAKE2B 59adb23b18fe453d45d92b0dac22a2fc8db1b1d02074f5eabc65d95bf7764f43a1d835f7316fbd61bbb7d98f3aeb6a3306d53963c5f83b8f55d879c182518534 SHA512 81fd4c27ff1d16dcb85229d4e4fd2cb06943ddfe966b5324fa8a8a957b2d2ec2aed7c5da05d6c009148f53b76545b27b0cba506622c8861f70bf7cad6c214a08 DIST podman-5.8.2.tar.gz 21302563 BLAKE2B daa2eefbd5014926825225f60ac46f42857d630e3b215631bc14305779b45efa28bd876c92c8c3fbd030b78f862d2738ae71d1a4a57a38a6e285ad518250a8f9 SHA512 4fb26b278aaaa64799c7cc27e168c6fd836a1ed95653c757db5de9873e774a4650353ad4aadad6a7968dd82db8e2833f871ba41304512825b706d4919ee333a7 -DIST podman-5.8.5.tar.gz 20610224 BLAKE2B 72010127d3d8137194e2b394a2a5dd941a5f3c434ed6263dd05d43ef56af23dca9016600d8b3102a7430115c7cedb547a6863f8fd7c38fb0bda764b7adbe0ee0 SHA512 76b590691b0df0a2c635f367113f0be46ea78f9c034b511dd4e8cb842635171f63972c9b2f856f0312c7f754b10c5de169045db73a61030384aaa5f1c0e04e3d -DIST podman-5.8.6.tar.gz 20611851 BLAKE2B 976b498076ac9c759cf1cf6898e13545a35b8725af94647a1218fd72902f78696dee186ee816e7f05fc864523978845850f3dc9ecd4aa097205d96b1fc23e5e7 SHA512 8c209b2f881eb3d3bb6ef30401a620d5efa8cdc45fb4d08b6666b4039e17aa858888ccac8d5f3797c6d0948693f9094badcf0c724c3c4ba6362fdaa029178bc7 -DIST podman-6.0.0.tar.gz 20510790 BLAKE2B fd522279dedb6fefbb9da9bd8b8000e696ff97f66b8b0816b5ecdb7b7b312b9466068d23fc396e9d7539e7700f0ececc43a55a5bd28e8a6dd1624aa7f2cfc214 SHA512 fc79bd666b2a500b89846ab8f20c9cdb3d25359ea96de5f1416821aa16fa54e60348a2250b42f775bee3ea0b26ec36c3cc72c9ebd1f4e88beeeea8bdb9b7791e -DIST podman-6.0.1.tar.gz 20513090 BLAKE2B 7ad62803a09bfd6839c78cf958bda81f027f17d520c1469c58e01b31ff05656c1d29282c98e085b27ed8abdce295993794c75497a5b184f4994896af738752ff SHA512 d8aee45d0e49442512041e3d9b3c2dc153f3bda19a8388da9edcf81731b38978a534a90176dc4e9d2533bb051473a6cc226aec041cd7846603ca13283ea01692 -DIST podman-6.0.2.tar.gz 20516396 BLAKE2B cc0a0032a7f8f490a874bdc3d05b618f52e8d7c7597c70852feacff2cd145bb0ad127e686221237c31f4cccabeaa89465cd2f3234d8ab92eaecb71074796ece8 SHA512 1227f486d1c84c0d877fadf2627c2f491bd1634c2de036631b589a727a1dc3212297cf2d2d313c75b3556de1b0204205e664e70ccf8ebc0b0064d1136db9351e -DIST podman-6.1.0.tar.gz 20956524 BLAKE2B 01ed3f93ce6ddf52911b16061fa339069061faf30c24bc40eacd8ba5a6daeb9e59b08bcc849806a933bdcac58c6f79001742df9a86dbb689e6b042a47ad23edf SHA512 3822eb564111f6cdcdce0f34d06e962cb40b1b34be158c2162334fe966bc314a66e8d164326ceed7ab1f1f87607bd13be1258ca9cfb274ea2a69f7409be40149 -DIST podman-6.1.2.tar.gz 20980766 BLAKE2B 0314732573df6491b8e364d937eda14143a021b959ec43fe88839e0cd414446d30c95b661972069b5da77a374948dc8e8cf6c5c166bf932f9777d4a2939c1a40 SHA512 c17d05d4b5683a266290f91afa78945666f39f2dea3de8074bd3457083120f787532b6c88f4c00996ad309ce05b2c3cd50d91742aa315a10ace868d49adafb48 +DIST podman-5.8.9.tar.gz 20645330 BLAKE2B e859304330a4c9bac9ae3885977921cfb3ddfb44359deacfebe4a05748148bb0392921cbab516bf889f81fadb9a3bc6fac8752ab57279a20235ab8a849fc3c41 SHA512 d3876d57d705bcaa727fc73156b6cf87c960c3c50f6da63d5fea95ddd8ad5254486283fff8687f90947b8de6d8c44e792838a0e17f602c56e9d73c8b26993c43 DIST podman-6.1.4.tar.gz 21017012 BLAKE2B c3cfeeb199c0a1fc7d4a21349816decf7ab57dd9542f60e286b9b752d617ff1405beb831f1b644868092414fee4566694b0dfb1b4bed7b176820661dabff033c SHA512 46bd3a4ef7341a30a43582224a3c0a4c89cf0ea8fceac3c1ee03c0613981a6160afec1fede7a15f558f4174c9b0bf231f7447e461e9c2c87bbfaed8978ce41fc diff --git a/app-containers/podman/podman-5.7.1.ebuild b/app-containers/podman/podman-5.7.1.ebuild deleted file mode 100644 index d7dffb31b260..000000000000 --- a/app-containers/podman/podman-5.7.1.ebuild +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit go-module python-any-r1 tmpfiles toolchain-funcs linux-info - -DESCRIPTION="A tool for managing OCI containers and pods with Docker-compatible CLI" -HOMEPAGE="https://github.com/podman-container-tools/podman/ https://podman.io/" - -if [[ ${PV} == 9999* ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/podman-container-tools/podman.git" -else - SRC_URI="https://github.com/podman-container-tools/podman/archive/v${PV/_rc/-rc}.tar.gz -> ${P}.tar.gz" - S="${WORKDIR}/${P/_rc/-rc}" - [[ ${PV} != *rc* ]] && \ - KEYWORDS="amd64 arm64 ~loong ~riscv" -fi - -# main pkg -LICENSE="Apache-2.0" -# deps -LICENSE+=" BSD BSD-2 CC-BY-SA-4.0 ISC MIT MPL-2.0" -SLOT="0" -IUSE="apparmor btrfs +seccomp selinux systemd wrapper" -RESTRICT="test" - -RDEPEND=" - app-containers/catatonit - >=app-containers/conmon-2.1.10 - >=app-containers/containers-common-0.58.0-r1 - app-crypt/gpgme:= - dev-db/sqlite:3 - dev-libs/libassuan:= - dev-libs/libgpg-error:= - sys-apps/shadow:= - - apparmor? ( sys-libs/libapparmor ) - btrfs? ( sys-fs/btrfs-progs ) - wrapper? ( !app-containers/docker-cli ) - seccomp? ( sys-libs/libseccomp:= ) - selinux? ( sec-policy/selinux-podman sys-libs/libselinux:= ) - systemd? ( sys-apps/systemd:= ) -" -DEPEND="${RDEPEND}" -BDEPEND=" - ${PYTHON_DEPS} - dev-go/go-md2man -" - -PATCHES=( - "${FILESDIR}"/${PN}-5.5.2-togglable-seccomp.patch -) - -CONFIG_CHECK=" - ~USER_NS -" - -pkg_setup() { - use btrfs && CONFIG_CHECK+=" ~BTRFS_FS" - linux-info_pkg_setup - python-any-r1_pkg_setup -} - -src_prepare() { - default - - # assure necessary files are present - local file - for file in apparmor_tag btrfs_installed_tag systemd_tag; do - [[ -f hack/"${file}".sh ]] || die - done - - local feature - for feature in apparmor systemd; do - cat <<-EOF > hack/"${feature}"_tag.sh || die - #!/usr/bin/env bash - $(usex ${feature} "echo ${feature}" echo) - EOF - done - - cat <<-EOF > hack/btrfs_installed_tag.sh || die - #!/usr/bin/env bash - $(usex btrfs echo 'echo exclude_graphdriver_btrfs') - EOF -} - -src_compile() { - export ETCDIR="${EPREFIX}/etc" PREFIX="${EPREFIX}/usr" BUILD_ORIGIN="Gentoo Portage" - - # For non-live versions, prevent git operations which causes sandbox violations - # https://github.com/gentoo/gentoo/pull/33531#issuecomment-1786107493 - [[ ${PV} != 9999* ]] && export COMMIT_NO="" GIT_COMMIT="" EPOCH_TEST_COMMIT="" - - # Use proper pkg-config to get gpgme cflags and ldflags when - # cross-compiling, bug 930982. - if tc-is-cross-compiler; then - tc-export PKG_CONFIG - fi - - emake BUILDFLAGS="-v -work -x" GOMD2MAN="go-md2man" EXTRA_BUILDTAGS="$(usev seccomp)" SELINUXOPT= \ - all $(usev wrapper docker-docs) -} - -src_install() { - emake DESTDIR="${D}" SELINUXOPT= install install.completions $(usev wrapper install.docker-full) - - if use !systemd; then - newconfd "${FILESDIR}"/podman-5.0.0_rc4.confd podman - newinitd "${FILESDIR}"/podman-5.0.0_rc4.initd podman - - newinitd "${FILESDIR}"/podman-restart-5.0.0_rc4.initd podman-restart - newconfd "${FILESDIR}"/podman-restart-5.0.0_rc4.confd podman-restart - - newinitd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.initd podman-clean-transient - newconfd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.confd podman-clean-transient - - exeinto /etc/cron.daily - newexe "${FILESDIR}"/podman-auto-update-5.0.0.cron podman-auto-update - - insinto /etc/logrotate.d - newins "${FILESDIR}/podman.logrotated" podman - - exeinto /etc/user/init.d - newexe "${FILESDIR}/podman-5.0.0_rc4.user.initd" podman - - insinto /etc/user/conf.d - newins "${FILESDIR}/podman-5.0.0_rc4.user.confd" podman - fi - - keepdir /var/lib/containers -} - -pkg_postinst() { - tmpfiles_process podman.conf $(usev wrapper podman-docker.conf) -} diff --git a/app-containers/podman/podman-5.8.6.ebuild b/app-containers/podman/podman-5.8.6.ebuild deleted file mode 100644 index 0f8543a25874..000000000000 --- a/app-containers/podman/podman-5.8.6.ebuild +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit go-module python-any-r1 tmpfiles toolchain-funcs linux-info - -DESCRIPTION="A tool for managing OCI containers and pods with Docker-compatible CLI" -HOMEPAGE="https://github.com/podman-container-tools/podman/ https://podman.io/" - -if [[ ${PV} == 9999* ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/podman-container-tools/podman.git" -else - SRC_URI="https://github.com/podman-container-tools/podman/archive/v${PV/_rc/-rc}.tar.gz -> ${P}.tar.gz" - S="${WORKDIR}/${P/_rc/-rc}" - [[ ${PV} != *rc* ]] && \ - KEYWORDS="~amd64 ~arm64 ~loong ~riscv" -fi - -# main pkg -LICENSE="Apache-2.0" -# deps -LICENSE+=" BSD BSD-2 CC-BY-SA-4.0 ISC MIT MPL-2.0" -SLOT="0" -IUSE="apparmor btrfs cron +seccomp selinux systemd wrapper" -RESTRICT="test" - -RDEPEND=" - app-containers/catatonit - >=app-containers/conmon-2.1.10 - >=app-containers/containers-common-0.58.0-r1 - app-crypt/gpgme:= - dev-db/sqlite:3 - dev-libs/libassuan:= - dev-libs/libgpg-error:= - sys-apps/shadow:= - - apparmor? ( sys-libs/libapparmor ) - btrfs? ( sys-fs/btrfs-progs ) - wrapper? ( !app-containers/docker-cli ) - seccomp? ( sys-libs/libseccomp:= ) - selinux? ( sec-policy/selinux-podman sys-libs/libselinux:= ) - systemd? ( sys-apps/systemd:= ) -" -DEPEND="${RDEPEND}" -BDEPEND=" - ${PYTHON_DEPS} - >=dev-lang/go-1.25.0 - dev-go/go-md2man -" - -PATCHES=( - "${FILESDIR}"/${PN}-5.5.2-togglable-seccomp.patch -) - -CONFIG_CHECK=" - ~USER_NS -" - -pkg_setup() { - use btrfs && CONFIG_CHECK+=" ~BTRFS_FS" - linux-info_pkg_setup - python-any-r1_pkg_setup -} - -src_prepare() { - default - - # assure necessary files are present - local file - for file in apparmor_tag btrfs_installed_tag systemd_tag; do - [[ -f hack/"${file}".sh ]] || die - done - - local feature - for feature in apparmor systemd; do - cat <<-EOF > hack/"${feature}"_tag.sh || die - #!/usr/bin/env bash - $(usex ${feature} "echo ${feature}" echo) - EOF - done - - cat <<-EOF > hack/btrfs_installed_tag.sh || die - #!/usr/bin/env bash - $(usex btrfs echo 'echo exclude_graphdriver_btrfs') - EOF - - # hardcode using system sqlite instead of bundled one - [[ -f hack/sqlite_tag.sh ]] && echo -e '#!/usr/bin/env bash\necho libsqlite3' > hack/sqlite_tag.sh || die -} - -src_compile() { - export ETCDIR="${EPREFIX}/etc" PREFIX="${EPREFIX}/usr" BUILD_ORIGIN="Gentoo Portage" - - # For non-live versions, prevent git operations which causes sandbox violations - # https://github.com/gentoo/gentoo/pull/33531#issuecomment-1786107493 - [[ ${PV} != 9999* ]] && export COMMIT_NO="" GIT_COMMIT="" EPOCH_TEST_COMMIT="" - - # Use proper pkg-config to get gpgme cflags and ldflags when - # cross-compiling, bug 930982. - if tc-is-cross-compiler; then - tc-export PKG_CONFIG - fi - - emake BUILDFLAGS="-v -work -x" GOMD2MAN="go-md2man" EXTRA_BUILDTAGS="$(usev seccomp)" SELINUXOPT= \ - all $(usev wrapper docker-docs) -} - -src_install() { - emake DESTDIR="${D}" SELINUXOPT= install install.completions $(usev wrapper install.docker-full) - - newconfd "${FILESDIR}"/podman-5.0.0_rc4.confd podman - newinitd "${FILESDIR}"/podman-5.0.0_rc4.initd podman - - newinitd "${FILESDIR}"/podman-restart-5.0.0_rc4.initd podman-restart - newconfd "${FILESDIR}"/podman-restart-5.0.0_rc4.confd podman-restart - - newinitd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.initd podman-clean-transient - newconfd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.confd podman-clean-transient - - if use cron; then - exeinto /etc/cron.daily - newexe "${FILESDIR}"/podman-auto-update-5.0.0.cron podman-auto-update - fi - insinto /etc/logrotate.d - newins "${FILESDIR}/podman.logrotated" podman - - exeinto /etc/user/init.d - newexe "${FILESDIR}/podman-5.0.0_rc4.user.initd" podman - - insinto /etc/user/conf.d - newins "${FILESDIR}/podman-5.0.0_rc4.user.confd" podman -} - -pkg_postinst() { - tmpfiles_process podman.conf $(usev wrapper podman-docker.conf) -} diff --git a/app-containers/podman/podman-5.8.5.ebuild b/app-containers/podman/podman-5.8.9.ebuild index 0f8543a25874..0f8543a25874 100644 --- a/app-containers/podman/podman-5.8.5.ebuild +++ b/app-containers/podman/podman-5.8.9.ebuild diff --git a/app-containers/podman/podman-6.0.0.ebuild b/app-containers/podman/podman-6.0.0.ebuild deleted file mode 100644 index ca8843baac02..000000000000 --- a/app-containers/podman/podman-6.0.0.ebuild +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit go-module python-any-r1 tmpfiles toolchain-funcs linux-info - -DESCRIPTION="A tool for managing OCI containers and pods with Docker-compatible CLI" -HOMEPAGE="https://github.com/podman-container-tools/podman/ https://podman.io/" - -if [[ ${PV} == 9999* ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/podman-container-tools/podman.git" -else - SRC_URI="https://github.com/podman-container-tools/podman/archive/v${PV/_rc/-rc}.tar.gz -> ${P}.tar.gz" - S="${WORKDIR}/${P/_rc/-rc}" - [[ ${PV} != *rc* ]] && \ - KEYWORDS="~amd64 ~arm64 ~loong ~riscv" -fi - -# main pkg -LICENSE="Apache-2.0" -# deps -LICENSE+=" BSD BSD-2 CC-BY-SA-4.0 ISC MIT MPL-2.0" -SLOT="0" -IUSE="apparmor btrfs cron +seccomp selinux systemd wrapper" -RESTRICT="test" - -RDEPEND=" - app-containers/catatonit - >=app-containers/conmon-2.1.10 - >=app-containers/container-libs-0.68.0[extra(-)] - app-crypt/gpgme:= - dev-db/sqlite:3 - dev-libs/libassuan:= - dev-libs/libgpg-error:= - sys-apps/shadow:= - - apparmor? ( sys-libs/libapparmor ) - btrfs? ( sys-fs/btrfs-progs ) - wrapper? ( !app-containers/docker-cli ) - seccomp? ( sys-libs/libseccomp:= ) - selinux? ( sec-policy/selinux-podman sys-libs/libselinux:= ) - systemd? ( sys-apps/systemd:= ) -" -DEPEND="${RDEPEND}" -BDEPEND=" - ${PYTHON_DEPS} - dev-go/go-md2man - >=dev-lang/go-1.25.6 -" - -PATCHES=( - "${FILESDIR}"/${PN}-5.5.2-togglable-seccomp.patch -) - -CONFIG_CHECK=" - ~USER_NS -" - -pkg_setup() { - use btrfs && CONFIG_CHECK+=" ~BTRFS_FS" - linux-info_pkg_setup - python-any-r1_pkg_setup -} - -src_prepare() { - default - - # assure necessary files are present - local file - for file in apparmor_tag btrfs_installed_tag systemd_tag; do - [[ -f hack/"${file}".sh ]] || die - done - - local feature - for feature in apparmor systemd; do - cat <<-EOF > hack/"${feature}"_tag.sh || die - #!/usr/bin/env bash - $(usex ${feature} "echo ${feature}" echo) - EOF - done - - cat <<-EOF > hack/btrfs_installed_tag.sh || die - #!/usr/bin/env bash - $(usex btrfs echo 'echo exclude_graphdriver_btrfs') - EOF - - # hardcode using system sqlite instead of bundled one - [[ -f hack/sqlite_tag.sh ]] && echo -e '#!/usr/bin/env bash\necho libsqlite3' > hack/sqlite_tag.sh || die -} - -src_compile() { - export ETCDIR="${EPREFIX}/etc" PREFIX="${EPREFIX}/usr" BUILD_ORIGIN="Gentoo Portage" - - # For non-live versions, prevent git operations which causes sandbox violations - # https://github.com/gentoo/gentoo/pull/33531#issuecomment-1786107493 - [[ ${PV} != 9999* ]] && export COMMIT_NO="" GIT_COMMIT="" EPOCH_TEST_COMMIT="" - - # Use proper pkg-config to get gpgme cflags and ldflags when - # cross-compiling, bug 930982. - if tc-is-cross-compiler; then - tc-export PKG_CONFIG - fi - - emake BUILDFLAGS="-v -work -x" GOMD2MAN="go-md2man" EXTRA_BUILDTAGS="$(usev seccomp)" SELINUXOPT= \ - all $(usev wrapper docker-docs) -} - -src_install() { - emake DESTDIR="${D}" SELINUXOPT= install install.completions $(usev wrapper install.docker-full) - - newconfd "${FILESDIR}"/podman-5.0.0_rc4.confd podman - newinitd "${FILESDIR}"/podman-5.0.0_rc4.initd podman - - newinitd "${FILESDIR}"/podman-restart-5.0.0_rc4.initd podman-restart - newconfd "${FILESDIR}"/podman-restart-5.0.0_rc4.confd podman-restart - - newinitd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.initd podman-clean-transient - newconfd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.confd podman-clean-transient - - if use cron; then - exeinto /etc/cron.daily - newexe "${FILESDIR}"/podman-auto-update-5.0.0.cron podman-auto-update - fi - - insinto /etc/logrotate.d - newins "${FILESDIR}/podman.logrotated" podman - - exeinto /etc/user/init.d - newexe "${FILESDIR}/podman-5.0.0_rc4.user.initd" podman - - insinto /etc/user/conf.d - newins "${FILESDIR}/podman-5.0.0_rc4.user.confd" podman -} - -pkg_postinst() { - tmpfiles_process podman.conf $(usev wrapper podman-docker.conf) -} diff --git a/app-containers/podman/podman-6.0.1.ebuild b/app-containers/podman/podman-6.0.1.ebuild deleted file mode 100644 index ca8843baac02..000000000000 --- a/app-containers/podman/podman-6.0.1.ebuild +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit go-module python-any-r1 tmpfiles toolchain-funcs linux-info - -DESCRIPTION="A tool for managing OCI containers and pods with Docker-compatible CLI" -HOMEPAGE="https://github.com/podman-container-tools/podman/ https://podman.io/" - -if [[ ${PV} == 9999* ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/podman-container-tools/podman.git" -else - SRC_URI="https://github.com/podman-container-tools/podman/archive/v${PV/_rc/-rc}.tar.gz -> ${P}.tar.gz" - S="${WORKDIR}/${P/_rc/-rc}" - [[ ${PV} != *rc* ]] && \ - KEYWORDS="~amd64 ~arm64 ~loong ~riscv" -fi - -# main pkg -LICENSE="Apache-2.0" -# deps -LICENSE+=" BSD BSD-2 CC-BY-SA-4.0 ISC MIT MPL-2.0" -SLOT="0" -IUSE="apparmor btrfs cron +seccomp selinux systemd wrapper" -RESTRICT="test" - -RDEPEND=" - app-containers/catatonit - >=app-containers/conmon-2.1.10 - >=app-containers/container-libs-0.68.0[extra(-)] - app-crypt/gpgme:= - dev-db/sqlite:3 - dev-libs/libassuan:= - dev-libs/libgpg-error:= - sys-apps/shadow:= - - apparmor? ( sys-libs/libapparmor ) - btrfs? ( sys-fs/btrfs-progs ) - wrapper? ( !app-containers/docker-cli ) - seccomp? ( sys-libs/libseccomp:= ) - selinux? ( sec-policy/selinux-podman sys-libs/libselinux:= ) - systemd? ( sys-apps/systemd:= ) -" -DEPEND="${RDEPEND}" -BDEPEND=" - ${PYTHON_DEPS} - dev-go/go-md2man - >=dev-lang/go-1.25.6 -" - -PATCHES=( - "${FILESDIR}"/${PN}-5.5.2-togglable-seccomp.patch -) - -CONFIG_CHECK=" - ~USER_NS -" - -pkg_setup() { - use btrfs && CONFIG_CHECK+=" ~BTRFS_FS" - linux-info_pkg_setup - python-any-r1_pkg_setup -} - -src_prepare() { - default - - # assure necessary files are present - local file - for file in apparmor_tag btrfs_installed_tag systemd_tag; do - [[ -f hack/"${file}".sh ]] || die - done - - local feature - for feature in apparmor systemd; do - cat <<-EOF > hack/"${feature}"_tag.sh || die - #!/usr/bin/env bash - $(usex ${feature} "echo ${feature}" echo) - EOF - done - - cat <<-EOF > hack/btrfs_installed_tag.sh || die - #!/usr/bin/env bash - $(usex btrfs echo 'echo exclude_graphdriver_btrfs') - EOF - - # hardcode using system sqlite instead of bundled one - [[ -f hack/sqlite_tag.sh ]] && echo -e '#!/usr/bin/env bash\necho libsqlite3' > hack/sqlite_tag.sh || die -} - -src_compile() { - export ETCDIR="${EPREFIX}/etc" PREFIX="${EPREFIX}/usr" BUILD_ORIGIN="Gentoo Portage" - - # For non-live versions, prevent git operations which causes sandbox violations - # https://github.com/gentoo/gentoo/pull/33531#issuecomment-1786107493 - [[ ${PV} != 9999* ]] && export COMMIT_NO="" GIT_COMMIT="" EPOCH_TEST_COMMIT="" - - # Use proper pkg-config to get gpgme cflags and ldflags when - # cross-compiling, bug 930982. - if tc-is-cross-compiler; then - tc-export PKG_CONFIG - fi - - emake BUILDFLAGS="-v -work -x" GOMD2MAN="go-md2man" EXTRA_BUILDTAGS="$(usev seccomp)" SELINUXOPT= \ - all $(usev wrapper docker-docs) -} - -src_install() { - emake DESTDIR="${D}" SELINUXOPT= install install.completions $(usev wrapper install.docker-full) - - newconfd "${FILESDIR}"/podman-5.0.0_rc4.confd podman - newinitd "${FILESDIR}"/podman-5.0.0_rc4.initd podman - - newinitd "${FILESDIR}"/podman-restart-5.0.0_rc4.initd podman-restart - newconfd "${FILESDIR}"/podman-restart-5.0.0_rc4.confd podman-restart - - newinitd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.initd podman-clean-transient - newconfd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.confd podman-clean-transient - - if use cron; then - exeinto /etc/cron.daily - newexe "${FILESDIR}"/podman-auto-update-5.0.0.cron podman-auto-update - fi - - insinto /etc/logrotate.d - newins "${FILESDIR}/podman.logrotated" podman - - exeinto /etc/user/init.d - newexe "${FILESDIR}/podman-5.0.0_rc4.user.initd" podman - - insinto /etc/user/conf.d - newins "${FILESDIR}/podman-5.0.0_rc4.user.confd" podman -} - -pkg_postinst() { - tmpfiles_process podman.conf $(usev wrapper podman-docker.conf) -} diff --git a/app-containers/podman/podman-6.0.2.ebuild b/app-containers/podman/podman-6.0.2.ebuild deleted file mode 100644 index 15f2461c2efc..000000000000 --- a/app-containers/podman/podman-6.0.2.ebuild +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit go-module python-any-r1 tmpfiles toolchain-funcs linux-info - -DESCRIPTION="A tool for managing OCI containers and pods with Docker-compatible CLI" -HOMEPAGE="https://github.com/podman-container-tools/podman/ https://podman.io/" - -if [[ ${PV} == 9999* ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/podman-container-tools/podman.git" -else - SRC_URI="https://github.com/podman-container-tools/podman/archive/v${PV/_rc/-rc}.tar.gz -> ${P}.tar.gz" - S="${WORKDIR}/${P/_rc/-rc}" - [[ ${PV} != *rc* ]] && \ - KEYWORDS="~amd64 ~arm64 ~loong ~riscv" -fi - -# main pkg -LICENSE="Apache-2.0" -# deps -LICENSE+=" BSD BSD-2 CC-BY-SA-4.0 ISC MIT MPL-2.0" -SLOT="0" -IUSE="apparmor btrfs cron +seccomp selinux systemd wrapper" -RESTRICT="test" - -RDEPEND=" - app-containers/catatonit - >=app-containers/conmon-2.1.10 - >=app-containers/container-libs-0.68.0[extra(-)] - app-crypt/gpgme:= - dev-db/sqlite:3 - dev-libs/libassuan:= - dev-libs/libgpg-error:= - sys-apps/shadow:= - - apparmor? ( sys-libs/libapparmor ) - btrfs? ( sys-fs/btrfs-progs ) - wrapper? ( !app-containers/docker-cli ) - seccomp? ( sys-libs/libseccomp:= ) - selinux? ( sec-policy/selinux-podman ) - systemd? ( sys-apps/systemd:= ) -" -DEPEND="${RDEPEND}" -BDEPEND=" - ${PYTHON_DEPS} - dev-go/go-md2man - >=dev-lang/go-1.25.7 -" - -PATCHES=( - "${FILESDIR}"/${PN}-5.5.2-togglable-seccomp.patch -) - -CONFIG_CHECK=" - ~USER_NS -" - -pkg_setup() { - use btrfs && CONFIG_CHECK+=" ~BTRFS_FS" - linux-info_pkg_setup - python-any-r1_pkg_setup -} - -src_prepare() { - default - - # assure necessary files are present - local file - for file in apparmor_tag btrfs_installed_tag systemd_tag; do - [[ -f hack/"${file}".sh ]] || die - done - - local feature - for feature in apparmor systemd; do - cat <<-EOF > hack/"${feature}"_tag.sh || die - #!/usr/bin/env bash - $(usex ${feature} "echo ${feature}" echo) - EOF - done - - cat <<-EOF > hack/btrfs_installed_tag.sh || die - #!/usr/bin/env bash - $(usex btrfs echo 'echo exclude_graphdriver_btrfs') - EOF - - # hardcode using system sqlite instead of bundled one - [[ -f hack/sqlite_tag.sh ]] && echo -e '#!/usr/bin/env bash\necho libsqlite3' > hack/sqlite_tag.sh || die -} - -src_compile() { - export ETCDIR="${EPREFIX}/etc" PREFIX="${EPREFIX}/usr" BUILD_ORIGIN="Gentoo Portage" - - # For non-live versions, prevent git operations which causes sandbox violations - # https://github.com/gentoo/gentoo/pull/33531#issuecomment-1786107493 - [[ ${PV} != 9999* ]] && export COMMIT_NO="" GIT_COMMIT="" EPOCH_TEST_COMMIT="" - - # Use proper pkg-config to get gpgme cflags and ldflags when - # cross-compiling, bug 930982. - if tc-is-cross-compiler; then - tc-export PKG_CONFIG - fi - - emake BUILDFLAGS="-v -work -x" GOMD2MAN="go-md2man" EXTRA_BUILDTAGS="$(usev seccomp)" SELINUXOPT= \ - all $(usev wrapper docker-docs) -} - -src_install() { - emake DESTDIR="${D}" SELINUXOPT= install install.completions $(usev wrapper install.docker-full) - - newconfd "${FILESDIR}"/podman-5.0.0_rc4.confd podman - newinitd "${FILESDIR}"/podman-5.0.0_rc4.initd podman - - newinitd "${FILESDIR}"/podman-restart-5.0.0_rc4.initd podman-restart - newconfd "${FILESDIR}"/podman-restart-5.0.0_rc4.confd podman-restart - - newinitd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.initd podman-clean-transient - newconfd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.confd podman-clean-transient - - if use cron; then - exeinto /etc/cron.daily - newexe "${FILESDIR}"/podman-auto-update-5.0.0.cron podman-auto-update - fi - - insinto /etc/logrotate.d - newins "${FILESDIR}/podman.logrotated" podman - - exeinto /etc/user/init.d - newexe "${FILESDIR}/podman-5.0.0_rc4.user.initd" podman - - insinto /etc/user/conf.d - newins "${FILESDIR}/podman-5.0.0_rc4.user.confd" podman -} - -pkg_postinst() { - tmpfiles_process podman.conf $(usev wrapper podman-docker.conf) -} diff --git a/app-containers/podman/podman-6.1.0.ebuild b/app-containers/podman/podman-6.1.0.ebuild deleted file mode 100644 index 2f00f0db9bdd..000000000000 --- a/app-containers/podman/podman-6.1.0.ebuild +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit go-module python-any-r1 tmpfiles toolchain-funcs linux-info - -DESCRIPTION="A tool for managing OCI containers and pods with Docker-compatible CLI" -HOMEPAGE="https://github.com/podman-container-tools/podman/ https://podman.io/" - -if [[ ${PV} == 9999* ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/podman-container-tools/podman.git" -else - SRC_URI="https://github.com/podman-container-tools/podman/archive/v${PV/_rc/-rc}.tar.gz -> ${P}.tar.gz" - S="${WORKDIR}/${P/_rc/-rc}" - [[ ${PV} != *rc* ]] && \ - KEYWORDS="~amd64 ~arm64 ~loong ~riscv" -fi - -# main pkg -LICENSE="Apache-2.0" -# deps -LICENSE+=" BSD BSD-2 CC-BY-SA-4.0 ISC MIT MPL-2.0" -SLOT="0" -IUSE="apparmor btrfs cron +seccomp selinux systemd wrapper" -RESTRICT="test" - -RDEPEND=" - app-containers/catatonit - >=app-containers/conmon-2.1.10 - >=app-containers/container-libs-0.68.0[extra(-)] - app-crypt/gpgme:= - dev-db/sqlite:3 - dev-libs/libassuan:= - dev-libs/libgpg-error:= - sys-apps/shadow:= - - apparmor? ( sys-libs/libapparmor ) - btrfs? ( sys-fs/btrfs-progs ) - wrapper? ( !app-containers/docker-cli ) - seccomp? ( sys-libs/libseccomp:= ) - selinux? ( sec-policy/selinux-podman ) - systemd? ( sys-apps/systemd:= ) -" -DEPEND="${RDEPEND}" -BDEPEND=" - ${PYTHON_DEPS} - dev-go/go-md2man - >=dev-lang/go-1.25.9 -" - -PATCHES=( - "${FILESDIR}"/${PN}-5.5.2-togglable-seccomp.patch -) - -CONFIG_CHECK=" - ~USER_NS -" - -pkg_setup() { - use btrfs && CONFIG_CHECK+=" ~BTRFS_FS" - linux-info_pkg_setup - python-any-r1_pkg_setup -} - -src_prepare() { - default - - # assure necessary files are present - local file - for file in apparmor_tag btrfs_installed_tag systemd_tag; do - [[ -f hack/"${file}".sh ]] || die - done - - local feature - for feature in apparmor systemd; do - cat <<-EOF > hack/"${feature}"_tag.sh || die - #!/usr/bin/env bash - $(usex ${feature} "echo ${feature}" echo) - EOF - done - - cat <<-EOF > hack/btrfs_installed_tag.sh || die - #!/usr/bin/env bash - $(usex btrfs echo 'echo exclude_graphdriver_btrfs') - EOF - - # hardcode using system sqlite instead of bundled one - [[ -f hack/sqlite_tag.sh ]] && echo -e '#!/usr/bin/env bash\necho libsqlite3' > hack/sqlite_tag.sh || die -} - -src_compile() { - export ETCDIR="${EPREFIX}/etc" PREFIX="${EPREFIX}/usr" BUILD_ORIGIN="Gentoo Portage" - - # For non-live versions, prevent git operations which causes sandbox violations - # https://github.com/gentoo/gentoo/pull/33531#issuecomment-1786107493 - [[ ${PV} != 9999* ]] && export COMMIT_NO="" GIT_COMMIT="" EPOCH_TEST_COMMIT="" - - # Use proper pkg-config to get gpgme cflags and ldflags when - # cross-compiling, bug 930982. - if tc-is-cross-compiler; then - tc-export PKG_CONFIG - fi - - emake BUILDFLAGS="-v -work -x" GOMD2MAN="go-md2man" EXTRA_BUILDTAGS="$(usev seccomp)" SELINUXOPT= \ - all $(usev wrapper docker-docs) -} - -src_install() { - emake DESTDIR="${D}" SELINUXOPT= install install.completions $(usev wrapper install.docker-full) - - newconfd "${FILESDIR}"/podman-5.0.0_rc4.confd podman - newinitd "${FILESDIR}"/podman-5.0.0_rc4.initd podman - - newinitd "${FILESDIR}"/podman-restart-5.0.0_rc4.initd podman-restart - newconfd "${FILESDIR}"/podman-restart-5.0.0_rc4.confd podman-restart - - newinitd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.initd podman-clean-transient - newconfd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.confd podman-clean-transient - - if use cron; then - exeinto /etc/cron.daily - newexe "${FILESDIR}"/podman-auto-update-5.0.0.cron podman-auto-update - fi - - insinto /etc/logrotate.d - newins "${FILESDIR}/podman.logrotated" podman - - exeinto /etc/user/init.d - newexe "${FILESDIR}/podman-5.0.0_rc4.user.initd" podman - - insinto /etc/user/conf.d - newins "${FILESDIR}/podman-5.0.0_rc4.user.confd" podman -} - -pkg_postinst() { - tmpfiles_process podman.conf $(usev wrapper podman-docker.conf) -} diff --git a/app-containers/podman/podman-6.1.2.ebuild b/app-containers/podman/podman-6.1.2.ebuild deleted file mode 100644 index 9d9f1b34f9ed..000000000000 --- a/app-containers/podman/podman-6.1.2.ebuild +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit go-module python-any-r1 tmpfiles toolchain-funcs linux-info - -DESCRIPTION="A tool for managing OCI containers and pods with Docker-compatible CLI" -HOMEPAGE="https://github.com/podman-container-tools/podman/ https://podman.io/" - -if [[ ${PV} == 9999* ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/podman-container-tools/podman.git" -else - SRC_URI="https://github.com/podman-container-tools/podman/archive/v${PV/_rc/-rc}.tar.gz -> ${P}.tar.gz" - S="${WORKDIR}/${P/_rc/-rc}" - [[ ${PV} != *rc* ]] && \ - KEYWORDS="~amd64 ~arm64 ~loong ~riscv" -fi - -# main pkg -LICENSE="Apache-2.0" -# deps -LICENSE+=" BSD BSD-2 CC-BY-SA-4.0 ISC MIT MPL-2.0" -SLOT="0" -IUSE="apparmor btrfs cron +seccomp selinux systemd wrapper" -RESTRICT="test" - -RDEPEND=" - app-containers/catatonit - >=app-containers/conmon-2.1.10 - >=app-containers/container-libs-0.68.0[extra(-)] - app-crypt/gpgme:= - dev-db/sqlite:3 - dev-libs/libassuan:= - dev-libs/libgpg-error:= - sys-apps/shadow:= - - apparmor? ( sys-libs/libapparmor ) - btrfs? ( sys-fs/btrfs-progs ) - wrapper? ( !app-containers/docker-cli ) - seccomp? ( sys-libs/libseccomp:= ) - selinux? ( sec-policy/selinux-podman ) - systemd? ( sys-apps/systemd:= ) -" -DEPEND="${RDEPEND}" -BDEPEND=" - ${PYTHON_DEPS} - dev-go/go-md2man - >=dev-lang/go-1.26.0 -" - -PATCHES=( - "${FILESDIR}"/${PN}-5.5.2-togglable-seccomp.patch -) - -CONFIG_CHECK=" - ~USER_NS -" - -pkg_setup() { - use btrfs && CONFIG_CHECK+=" ~BTRFS_FS" - linux-info_pkg_setup - python-any-r1_pkg_setup -} - -src_prepare() { - default - - # assure necessary files are present - local file - for file in apparmor_tag btrfs_installed_tag systemd_tag; do - [[ -f hack/"${file}".sh ]] || die - done - - local feature - for feature in apparmor systemd; do - cat <<-EOF > hack/"${feature}"_tag.sh || die - #!/usr/bin/env bash - $(usex ${feature} "echo ${feature}" echo) - EOF - done - - cat <<-EOF > hack/btrfs_installed_tag.sh || die - #!/usr/bin/env bash - $(usex btrfs echo 'echo exclude_graphdriver_btrfs') - EOF - - # hardcode using system sqlite instead of bundled one - [[ -f hack/sqlite_tag.sh ]] && echo -e '#!/usr/bin/env bash\necho libsqlite3' > hack/sqlite_tag.sh || die -} - -src_compile() { - export ETCDIR="${EPREFIX}/etc" PREFIX="${EPREFIX}/usr" BUILD_ORIGIN="Gentoo Portage" - - # For non-live versions, prevent git operations which causes sandbox violations - # https://github.com/gentoo/gentoo/pull/33531#issuecomment-1786107493 - [[ ${PV} != 9999* ]] && export COMMIT_NO="" GIT_COMMIT="" EPOCH_TEST_COMMIT="" - - # Use proper pkg-config to get gpgme cflags and ldflags when - # cross-compiling, bug 930982. - if tc-is-cross-compiler; then - tc-export PKG_CONFIG - fi - - emake BUILDFLAGS="-v -work -x" GOMD2MAN="go-md2man" EXTRA_BUILDTAGS="$(usev seccomp)" SELINUXOPT= \ - all $(usev wrapper docker-docs) -} - -src_install() { - emake DESTDIR="${D}" SELINUXOPT= install install.completions $(usev wrapper install.docker-full) - - newconfd "${FILESDIR}"/podman-5.0.0_rc4.confd podman - newinitd "${FILESDIR}"/podman-5.0.0_rc4.initd podman - - newinitd "${FILESDIR}"/podman-restart-5.0.0_rc4.initd podman-restart - newconfd "${FILESDIR}"/podman-restart-5.0.0_rc4.confd podman-restart - - newinitd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.initd podman-clean-transient - newconfd "${FILESDIR}"/podman-clean-transient-5.0.0_rc6.confd podman-clean-transient - - if use cron; then - exeinto /etc/cron.daily - newexe "${FILESDIR}"/podman-auto-update-5.0.0.cron podman-auto-update - fi - - insinto /etc/logrotate.d - newins "${FILESDIR}/podman.logrotated" podman - - exeinto /etc/user/init.d - newexe "${FILESDIR}/podman-5.0.0_rc4.user.initd" podman - - insinto /etc/user/conf.d - newins "${FILESDIR}/podman-5.0.0_rc4.user.confd" podman -} - -pkg_postinst() { - tmpfiles_process podman.conf $(usev wrapper podman-docker.conf) -} diff --git a/app-containers/podman/podman-9999.ebuild b/app-containers/podman/podman-9999.ebuild index 2f00f0db9bdd..45a24982ccfa 100644 --- a/app-containers/podman/podman-9999.ebuild +++ b/app-containers/podman/podman-9999.ebuild @@ -49,7 +49,7 @@ DEPEND="${RDEPEND}" BDEPEND=" ${PYTHON_DEPS} dev-go/go-md2man - >=dev-lang/go-1.25.9 + >=dev-lang/go-1.26.8 " PATCHES=( diff --git a/app-crypt/hashcat/hashcat-7.1.2-r1.ebuild b/app-crypt/hashcat/hashcat-7.1.2-r1.ebuild index c2a80f156b80..b3a7aafa575a 100644 --- a/app-crypt/hashcat/hashcat-7.1.2-r1.ebuild +++ b/app-crypt/hashcat/hashcat-7.1.2-r1.ebuild @@ -70,7 +70,7 @@ SLOT="0" IUSE="brain video_cards_nvidia" RESTRICT=test -DEPEND="app-arch/lzma +RDEPEND="app-arch/lzma app-arch/unrar virtual/minizip:= brain? ( dev-libs/xxhash ) @@ -82,7 +82,9 @@ DEPEND="app-arch/lzma ) ) !video_cards_nvidia? ( virtual/opencl )" -RDEPEND="${DEPEND}" +# src_prepare removes the bundled OpenCL headers +DEPEND="${RDEPEND} + dev-util/opencl-headers" src_unpack() { if [[ ${PV} == "9999" ]]; then diff --git a/app-crypt/hashcat/hashcat-9999.ebuild b/app-crypt/hashcat/hashcat-9999.ebuild index e58c825b4b11..809c01992ae6 100644 --- a/app-crypt/hashcat/hashcat-9999.ebuild +++ b/app-crypt/hashcat/hashcat-9999.ebuild @@ -135,7 +135,7 @@ SLOT="0" IUSE="brain video_cards_nvidia" RESTRICT=test -DEPEND="app-arch/lzma +RDEPEND="app-arch/lzma app-arch/unrar virtual/minizip:= brain? ( dev-libs/xxhash ) @@ -147,7 +147,9 @@ DEPEND="app-arch/lzma ) ) !video_cards_nvidia? ( virtual/opencl )" -RDEPEND="${DEPEND}" +# src_prepare removes the bundled OpenCL headers +DEPEND="${RDEPEND} + dev-util/opencl-headers" src_unpack() { if [[ ${PV} == "9999" ]]; then diff --git a/app-doc/gnucash-docs/Manifest b/app-doc/gnucash-docs/Manifest index 9b527812e795..f25c28aa9117 100644 --- a/app-doc/gnucash-docs/Manifest +++ b/app-doc/gnucash-docs/Manifest @@ -1 +1,2 @@ DIST gnucash-docs-5.14.tar.gz 133720288 BLAKE2B e256eee425470164de374385ff06b87000fdb12ceadfa5e7fb3c5cd114d44cc13072320b8b7598436ca806cb6b444359bb8f56393360be51d1961fc032ff211d SHA512 7e496f95e1642478db38bbd166374a1e7e79c9e28154b75c9ad73c310ad28d8883cba62fb3ede6675225c56447eaaad3b10a5b1efd6a00788c20ecf7159c942c +DIST gnucash-docs-5.17.tar.gz 133726594 BLAKE2B 7c80426c6be201bf4942277f42d82d1be6145d3506b5b3db10c35391e0b09c7edc3eac55748aa9ed3a2fefdeaaa9ec5e68af91216e259ff269a4aa3eb4bf43d6 SHA512 8b9fa415a9a9233a719e5b3f371371e407ad556947c313c2b3b20a87b0f80b60265b2c045a897f4a2bb6573c53fdac96ebe00ff554a7f99dad716d57443ffd3e diff --git a/app-doc/gnucash-docs/gnucash-docs-5.17.ebuild b/app-doc/gnucash-docs/gnucash-docs-5.17.ebuild new file mode 100644 index 000000000000..d2868166cddc --- /dev/null +++ b/app-doc/gnucash-docs/gnucash-docs-5.17.ebuild @@ -0,0 +1,63 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +# Please bump with app-office/gnucash + +CMAKE_MAKEFILE_GENERATOR=emake +inherit cmake optfeature + +DESCRIPTION="Documentation package for GnuCash" +HOMEPAGE="https://www.gnucash.org/" +SRC_URI="https://github.com/Gnucash/${PN}/archive/${PV}.tar.gz -> ${P}.tar.gz" + +LICENSE="GPL-2 FDL-1.1" +SLOT="0" +KEYWORDS="~amd64 ~arm64 ~ppc ~ppc64 ~riscv ~x86" +LOCALES=( de it ja pt ) +IUSE="${LOCALES[*]/#/l10n_}" + +BDEPEND=" + app-text/docbook-xml-dtd:4.5 + app-text/docbook-xsl-stylesheets + dev-libs/libxml2 + dev-libs/libxslt +" + +src_install() { + local doc_type my_lang + + for doc_type in manual guide; do + for my_lang in C ${L10N}; do + [[ -z ${my_lang} ]] && continue + + case "${my_lang}" in + # Both help and guides translated + C|de|it|pt) ;; + ja|ru) # Only guides translated + if [[ "${doc_type}" == "manual" ]] ; then + elog "Help documentation hasn't been translated for ${my_lang}" + elog "Will do English instead." + continue + fi + ;; + *) + die "Invalid locale: ${my_lang}" + ;; + esac + + emake \ + -C "${BUILD_DIR}/${my_lang}/${doc_type}" \ + DESTDIR="${D}" \ + install + done + done + + einstalldocs +} + +pkg_postinst() { + optfeature "generating PDF files" dev-java/fop + optfeature "viewing the docs" gnome-extra/yelp +} diff --git a/app-doc/gnucash-docs/metadata.xml b/app-doc/gnucash-docs/metadata.xml index fc5d8b3d84f8..8065fa50fea7 100644 --- a/app-doc/gnucash-docs/metadata.xml +++ b/app-doc/gnucash-docs/metadata.xml @@ -1,5 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE pkgmetadata SYSTEM "https://srcux.com/dtd/metadata.dtd"> <pkgmetadata> + <maintainer type="person" proxied="yes"> + <email>zhixu.liu@gmail.com</email> + <name>Z. Liu</name> + </maintainer> + <maintainer type="project" proxied="proxy"> + <email>proxy-maint@gentoo.org</email> + <name>Proxy Maintainers</name> + </maintainer> <origin>srcux</origin> </pkgmetadata> diff --git a/app-editors/neovim/Manifest b/app-editors/neovim/Manifest index fc0a70d12f29..110c342f16a6 100644 --- a/app-editors/neovim/Manifest +++ b/app-editors/neovim/Manifest @@ -1,4 +1,4 @@ DIST neovim-0.11.7.tar.gz 13018335 BLAKE2B c1951bee1d6284317555e79cb92b855a25ecb4b97619ecf0e4244b43bc6673b40be6ab3cebe1451642a13b293c8474cffb96ced3ee4a6943a153d4b9043e2ef5 SHA512 23e13f8c2964bff4d69c69cce137aaaaae9d0219750793da1834d099478f7a9d7170ae3d66cf096824ff42bbd0c9196e4a31858b84032533678e359fe75c6f79 -DIST neovim-0.12.0.tar.gz 13648075 BLAKE2B caa5899489b0d0fba4f1214a342d0c2bb4f6e37c7c263bbde8fe01b103e0f0230d94518a69121e228cec1d70fdcbea15683e9a02d055dcbfb6afabbc28fba19a SHA512 1897ddcf730a6593e561b7bb555405a5fd5aa495bd73a6434ec0358b525b53925e0b45346697fb846bd36c724e1fa3b7a310908152bafb1c3724a08e86a97c5b DIST neovim-0.12.4.tar.gz 13711232 BLAKE2B b3ed3a197b225c79359d89dff615215f14180e81622b73a9ed3425b413b6b7d8a06fc2a6ebb4f0a556e7413287e96350136803dc2824b912917019437c83fe71 SHA512 24656352a4d4785068640a3c53246bbfaf14c2d2e59f9ec8348399db17bc4104032ae62e56797038a65fc086d893dac1ce692a5eb4d186226b65f4cf694c835e DIST neovim-0.12.5.tar.gz 13720423 BLAKE2B 7fc7098d67e21981999ed01f5beac3ea6d1c0f80fe3f67a19436bd2ce73f17934720d8c307dcec81697ace9644f7f5b4fa08e95e7e6052ffdd89b04f6bc5af0d SHA512 94b681395a60ce06ec327c578f875b828256891d660c9e34f1f9c0b48577d2f85cbe0c2a8f377fba3b068782cc54dcaeda046045a2bdb7166340e12cf3d70354 +DIST neovim-0.12.6.tar.gz 13722771 BLAKE2B 23809190a67920ca54050325e8b1881f78fcbda97282b95b475109c3ded8247a1ed202f34357db7dd58ece888e76fc54874ca1d9d073f6b4ca0146065750f538 SHA512 7baf1a6c5e46845b415f522ad17129a3af33c698356f1f7f276481a7f55e3fc994ba1399aac85e1cd1b8436cc59f733ff1fddc12cdb027c295be1abd2dd20448 diff --git a/app-editors/neovim/neovim-0.12.0-r1.ebuild b/app-editors/neovim/neovim-0.12.6.ebuild index 6bb4d8add213..bf60a46f7a07 100644 --- a/app-editors/neovim/neovim-0.12.0-r1.ebuild +++ b/app-editors/neovim/neovim-0.12.6.ebuild @@ -16,7 +16,7 @@ if [[ ${PV} == 9999 ]]; then EGIT_REPO_URI="https://github.com/neovim/neovim.git" else SRC_URI="https://github.com/neovim/neovim/archive/v${PV}.tar.gz -> ${P}.tar.gz" - KEYWORDS="~amd64 ~arm ~arm64 ~ppc ~ppc64 ~riscv ~x86 ~x64-macos" + KEYWORDS="~amd64 ~arm ~arm64 ~ppc ~ppc64 ~x86" fi LICENSE="Apache-2.0 vim" @@ -108,8 +108,8 @@ src_install() { # symlink tree-sitter parsers dodir /usr/share/nvim/runtime - for parser in c lua markdown query vim vimdoc; do - dosym ../../../../$(get_libdir)/libtree-sitter-${parser}.so /usr/share/nvim/runtime/parser/${parser}.so + for parser in c lua markdown markdown_inline query vim vimdoc; do + dosym ../../../../$(get_libdir)/libtree-sitter-${parser/_/-}.so /usr/share/nvim/runtime/parser/${parser}.so done # conditionally install a symlink for nvimpager diff --git a/app-emacs/acp/Manifest b/app-emacs/acp/Manifest index 35b764271060..60d42472bebd 100644 --- a/app-emacs/acp/Manifest +++ b/app-emacs/acp/Manifest @@ -1,4 +1,4 @@ -DIST acp-0.14.1.gh.tar.gz 5356762 BLAKE2B 8a5d073dfcde43d13b0d851720fdfe55a000fe95e385479f78739349f6f579eff9c9b45248670a83d91d07e42b72eaa33f2efea4df9814363fe81bbdb378e9d5 SHA512 64569277c9a2abb805815e0edfefa70ee39bfecec64d81fdfaf820a1fec9a73a974d6086ede40a20b882b3730cd17de02e1b20628eaac4718fb13792197bbb85 DIST acp-0.15.1.gh.tar.gz 5358224 BLAKE2B b263c7fe384f76560e2c292e700f28d0600249af70b0762eaede6e49049704dade8563e7c5d3884575c6c97901246a86bccb14f951d2efc6c9e336273dc90aff SHA512 d9deffa1d61b8240bcb5982cf269627ecf1bec7cc615f94e42d26e0ab1e314b664deec1f0b06d502ea7c8db63938463883065a44bc8c2f07cb96f2ce92d66110 DIST acp-0.16.1.gh.tar.gz 5359543 BLAKE2B a9ad44074700c8189e79d280655589a484f62a49619cfdc7ed17405c652cd1e9a5a7ff10eea056fe854247c7bf7c01acf1a13892a939f7f9e27e42daa9eb2caa SHA512 11339a4b4eecec55646d7baa794567db78c4745429b0aa6e87398761078275bacff2d2114f97c2d0cd8e3f7596989a29dd7fbb6c1a6d1bc065e9bf1510dc1692 DIST acp-0.17.1.gh.tar.gz 5360501 BLAKE2B 81e9cf974635da428e7728e9a0a3078f232d59eb2a8540d22a0109e3edaa346eae4794e13ce467ff89203b55470b8f3d79eff4be3b5bb91be46f506fe8636f58 SHA512 c431925826ddb748746358115f8a4fab1e1a29bf03eb5a1d6a446d05bdb6acf6d806ba389d7285d8b06d37d03308346eeae1ba7fe6496ed826e0f74323655dd6 +DIST acp-0.18.1.gh.tar.gz 5360617 BLAKE2B 8dd01520b78ccfc7dd131af6e445fb444a779eca60564b11edc7adea18009ede80180a9ede5216b3beae45b37f975f86f8ead236416460efce0155242ece9bac SHA512 8532378bc49a08ffb452e87da2b246ab3e68d32065148670e09a0885c50c9d15df857bc0f89d5ceb2d98be4a847c3974f564959a8d48c8d88b894f208e9a36a8 diff --git a/app-emacs/acp/acp-0.14.1.ebuild b/app-emacs/acp/acp-0.18.1.ebuild index c6e32391c228..7d1c20bf35fe 100644 --- a/app-emacs/acp/acp-0.14.1.ebuild +++ b/app-emacs/acp/acp-0.18.1.ebuild @@ -17,7 +17,7 @@ else SRC_URI="https://github.com/xenodium/${PN}.el/archive/refs/tags/v${PV}.tar.gz -> ${P}.gh.tar.gz" S="${WORKDIR}/${PN}.el-${PV}" - KEYWORDS="amd64 ~x86" + KEYWORDS="~amd64 ~x86" fi LICENSE="GPL-3+" diff --git a/app-emacs/agent-shell/Manifest b/app-emacs/agent-shell/Manifest index 92f4ca140afc..5be348a9c272 100644 --- a/app-emacs/agent-shell/Manifest +++ b/app-emacs/agent-shell/Manifest @@ -1,6 +1,4 @@ DIST agent-shell-0.57.3.gh.tar.gz 1647268 BLAKE2B d3984b13f6d68edaa4a926aa8d6f1ddc46c14a75e360c50c8ac0dff53f6102a034661987754aa6332f12466b9a916a6d07fc4d78c06a4718a53133f364e3cf27 SHA512 95883aa4dcbff908b8d57c940f9983fa83904de84c16d90518a0f76b8e54524e0c5e997e6c4e345895401259ff7f999aab7d46279eabb390201d8e40c39b0df1 -DIST agent-shell-0.73.1.gh.tar.gz 1795378 BLAKE2B 775314b309f05eb34fb766037b4cfbec9f49f30f37be804057ab2731d0ed2ca6fb6a32dfb38d939938bd6c943ff91ed9ab53d7e7624daf0718f71bbfb3c0343b SHA512 c3c8a6b5e653ae3fcfd8a0f1181bc6187d199a4a8f358e86d03d47e3bf4557e05f7ee96858216a8a4d5dd240e3bc9256f51d30ccef1fbd8cd5a33295c53ca1e0 DIST agent-shell-0.74.3.gh.tar.gz 1815020 BLAKE2B c6bcb2a25eec224c60721c8ebdc09c14461e70c75f0cf56797fab33c4d92dccd4a1516d1fa62da7ca32e1bcfbcd46dd8cab4657b775b1aa8d9d726b663b774d9 SHA512 9fa3641b8096270d25bfb1bb26fb6e7b8a21723c49a08f80d504ae3cd45d6aace928ba75fdc7a04941bee196fd1e976ffa4f95360e5ab7a8fbd58fc5edaa4797 -DIST agent-shell-0.83.2.gh.tar.gz 2041839 BLAKE2B 310254438c2c4ff5e4cd855d967af58fb57655325d8a35c1363eb82d7dfe17e182cc1f323e824d9f1855357e55619c0bd40c2199b6622d9294e8715e18ed1b28 SHA512 71c009c6fa6acf4b1e2396a9fb9db8d615f509c6e208acedf5e606788f57ad639bc5b4cb6b126d4da9a4d840e408ca06fcc69d586dc0bcfe2a144e10fe3a7909 DIST agent-shell-0.87.10.gh.tar.gz 2103111 BLAKE2B 9748bfbe6426736cdca4da2ea8b040e1b1fbc43bb2f8c4bcd9fa6c0c9b6b365cf1c91c47289ce8ef1a8f1ae5f3d3aefcbdc6f72b7617275d756ab6ff175c46a0 SHA512 4d5beaad8b3b173d4b79fb5133fa154dc311b7e9d3cbb323799972191ee90d8c964a0e08f0fa41e6714b84ef15809092e6a174e624321c652cc19d443bb3292b -DIST agent-shell-0.87.2.gh.tar.gz 2094101 BLAKE2B 6eb460d60b89909408e4626eed5351e6234658a87c2cc323f612c2d53daa6e7c056653035695f43e2775a646afb9b506177c2d9cecae35b65c4bc1516d351b7c SHA512 f9f0b6f35cff49fdb034148e5aa759f6fcdd1b1c3dae683eb6f8b360ef51c5a6a93879b3af753311f1378052db23f1999625ae68ae50a6c750d1c0f89b3df360 +DIST agent-shell-0.88.3.gh.tar.gz 2118312 BLAKE2B 004463b97b9e3406dd1e2a4d31d41e184778afdb8556f89648ad7686ce475e50b6b023c7c071367d97f30621ad18026f1b5536a33adc20dd67aa1be1dd7bb937 SHA512 125f40a95ad4bf86176d231de84c2a4264ed29e4a2599f838082bee964c67bdd9a25972569d1ae25c24481306ced5cf2bff12ba65ba85e14ad552d85222156a6 diff --git a/app-emacs/agent-shell/agent-shell-0.83.2.ebuild b/app-emacs/agent-shell/agent-shell-0.83.2.ebuild deleted file mode 100644 index 139fce24eab6..000000000000 --- a/app-emacs/agent-shell/agent-shell-0.83.2.ebuild +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=9 - -NEED_EMACS="29.1" - -inherit elisp - -DESCRIPTION="Many native agentic integrations inside GNU Emacs" -HOMEPAGE="https://github.com/xenodium/agent-shell/" - -if [[ "${PV}" == *9999* ]] ; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/xenodium/${PN}" -else - SRC_URI="https://github.com/xenodium/${PN}/archive/refs/tags/v${PV}.tar.gz - -> ${P}.gh.tar.gz" - KEYWORDS="~amd64 ~x86" -fi - -LICENSE="GPL-3+" -SLOT="0" - -RDEPEND=" - >=app-emacs/acp-0.13.1 - >=app-emacs/shell-maker-0.97.2 -" -BDEPEND=" - ${RDEPEND} -" - -DOCS=( CONTRIBUTING.org README.org blog-post.org ) -SITEFILE="50${PN}-gentoo.el" - -src_compile() { - elisp_src_compile - elisp-make-autoload-file -} - -src_test() { - local test_file="" - for test_file in ./tests/*-tests.el ; do - elisp-test-ert ./tests/ -l "${test_file}" - done -} diff --git a/app-emacs/agent-shell/agent-shell-0.87.2.ebuild b/app-emacs/agent-shell/agent-shell-0.87.2.ebuild deleted file mode 100644 index 608839870422..000000000000 --- a/app-emacs/agent-shell/agent-shell-0.87.2.ebuild +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=9 - -NEED_EMACS="29.1" - -inherit elisp - -DESCRIPTION="Many native agentic integrations inside GNU Emacs" -HOMEPAGE="https://github.com/xenodium/agent-shell/" - -if [[ "${PV}" == *9999* ]] ; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/xenodium/${PN}" -else - SRC_URI="https://github.com/xenodium/${PN}/archive/refs/tags/v${PV}.tar.gz - -> ${P}.gh.tar.gz" - KEYWORDS="~amd64 ~x86" -fi - -LICENSE="GPL-3+" -SLOT="0" - -RDEPEND=" - >=app-emacs/acp-0.16.1 - >=app-emacs/shell-maker-0.97.5 -" -BDEPEND=" - ${RDEPEND} -" - -DOCS=( CONTRIBUTING.org README.org blog-post.org ) -SITEFILE="50${PN}-gentoo.el" - -src_compile() { - elisp_src_compile - elisp-make-autoload-file -} - -src_test() { - local test_file="" - for test_file in ./tests/*-tests.el ; do - elisp-test-ert ./tests/ -l "${test_file}" - done -} diff --git a/app-emacs/agent-shell/agent-shell-0.73.1.ebuild b/app-emacs/agent-shell/agent-shell-0.88.3.ebuild index 0420d6d6d3ba..e5dd959baf32 100644 --- a/app-emacs/agent-shell/agent-shell-0.73.1.ebuild +++ b/app-emacs/agent-shell/agent-shell-0.88.3.ebuild @@ -23,8 +23,8 @@ LICENSE="GPL-3+" SLOT="0" RDEPEND=" - >=app-emacs/acp-0.13.1 - >=app-emacs/shell-maker-0.97.1 + >=app-emacs/acp-0.18.1 + >=app-emacs/shell-maker-0.98.2 " BDEPEND=" ${RDEPEND} diff --git a/app-emacs/agent-shell/agent-shell-9999.ebuild b/app-emacs/agent-shell/agent-shell-9999.ebuild index 46e5efc672d3..e5dd959baf32 100644 --- a/app-emacs/agent-shell/agent-shell-9999.ebuild +++ b/app-emacs/agent-shell/agent-shell-9999.ebuild @@ -23,8 +23,8 @@ LICENSE="GPL-3+" SLOT="0" RDEPEND=" - >=app-emacs/acp-0.12.2 - >=app-emacs/shell-maker-0.93.3 + >=app-emacs/acp-0.18.1 + >=app-emacs/shell-maker-0.98.2 " BDEPEND=" ${RDEPEND} diff --git a/app-emacs/org-mode/Manifest b/app-emacs/org-mode/Manifest index 0228b4cb1c61..d9b083c9d4b9 100644 --- a/app-emacs/org-mode/Manifest +++ b/app-emacs/org-mode/Manifest @@ -1,2 +1 @@ DIST org-mode-9.8.10.tar.xz 1780592 BLAKE2B b240afac51188da5be519f37bad3a00dc472c37c15becc122c8e4e0af5a820ecfd9a3fd1d87c4887b984e929dd303926765e5a3ea99eed520f342ae7098c4d8d SHA512 3d156c88394bb5a804ecc36b06e921b6443b6f736d0f1ab31ebd78ace4fdd17270f318644bd61c1c711741f6bada56655e013a5a0e240e5e5f238760e9eac747 -DIST org-mode-9.8.9.tar.xz 1780452 BLAKE2B 06bdcb03d8b04541c63ef1b495d6274ba6ced428145ebf162b559e79691cd9b0501a2294ef3278053f51759445701758b5ea6d3a0574437219a1296fa57f02d9 SHA512 e6716d03be6deba4cc5c9247bd35c066641d2bf6724d5e1735b88cf4aad96ff138cb2e1378ad7310a46630dcb053c1316e8dfe259406a861348f7c2170728ba4 diff --git a/app-emacs/org-mode/org-mode-9.8.9.ebuild b/app-emacs/org-mode/org-mode-9.8.9.ebuild deleted file mode 100644 index 218f0a02fd49..000000000000 --- a/app-emacs/org-mode/org-mode-9.8.9.ebuild +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=9 - -NEED_EMACS="28.2" - -inherit elisp readme.gentoo-r1 - -DESCRIPTION="An Emacs mode for notes and project planning" -HOMEPAGE="https://orgmode.org/" - -if [[ "${PV}" == 9999 ]] ; then - inherit git-r3 - EGIT_REPO_URI="https://git.savannah.gnu.org/git/emacs/${PN}" - EGIT_CHECKOUT_DIR="${WORKDIR}/org" - S="${WORKDIR}/org" -else - # git archive --prefix=${P}/ release_${PV} | xz > ${P}.tar.xz - SRC_URI="https://dev.gentoo.org/~xgqt/distfiles/repackaged/${P}.tar.xz" - KEYWORDS="amd64 ~arm ~arm64 ppc ~riscv x86" -fi - -LICENSE="GPL-3+ FDL-1.3+ CC-BY-SA-3.0 odt-schema? ( OASIS-Open )" -SLOT="0" -IUSE="doc odt-schema" - -BDEPEND=" - doc? ( - virtual/texi2dvi - ) -" - -DOCS=( README.org CONTRIBUTE.org etc/ORG-NEWS ) -SITEFILE="50${PN}-gentoo.el" - -src_prepare() { - elisp_src_prepare - - # Remove failing tests. - rm testing/lisp/test-{ob{,-exp,-tangle,-shell,-sqlite},org-clock,ox-icalendar}.el \ - || die "failed to remove some test files" -} - -src_configure() { - elisp_src_configure - - if use doc ; then - DOCS+=( doc/org.pdf doc/orgcard.pdf doc/orgguide.pdf ) - fi - - EMAKEARGS=( - ORGVERSION="${PV}" - ETCDIRS="styles csl $(usev odt-schema schema)" - lispdir="${EPREFIX}${SITELISP}/${PN}" - datadir="${EPREFIX}${SITEETC}/${PN}" - infodir="${EPREFIX}/usr/share/info" - ) -} - -src_compile() { - emake -j1 "${EMAKEARGS[@]}" - - if use doc ; then - emake -j1 pdf - emake -j1 card - fi -} - -src_test() { - local -x LANG="C" - emake -j1 "${EMAKEARGS[@]}" TEST_NO_AUTOCLEAN="TRUE" test-dirty -} - -src_install() { - emake -j1 "${EMAKEARGS[@]}" DESTDIR="${D}" install - elisp-site-file-install "${FILESDIR}/${SITEFILE}" - einstalldocs - - local DOC_CONTENTS="Org mode has a large variety of run-time dependencies, - so you may have to install one or more additional packages. - A non-exhaustive list of these dependencies may be found at - <http://orgmode.org/worg/org-dependencies.html>." - readme.gentoo_create_doc -} diff --git a/app-emacs/posframe/Manifest b/app-emacs/posframe/Manifest index 3fb787f29d16..0dd2ab6d0031 100644 --- a/app-emacs/posframe/Manifest +++ b/app-emacs/posframe/Manifest @@ -1,2 +1,3 @@ DIST posframe-1.5.2.gh.tar.gz 80315 BLAKE2B 714093a40232363abb834c2323f13cb8f0bca1f29203b5309ef905f84dc9507849096da1b54adf51c2c1755bba23c334435e1f14f3b174ec42cde7eda4f0784b SHA512 aced29025ff61df942f977d80a1c2478676fabf3e37b94eb36e3a18bdc12cbc9efba39327fc841dfe3f47ef4b763487e2f943f969fa409b4e0c6eab82ae7e297 DIST posframe-1.5.3.gh.tar.gz 80423 BLAKE2B c7318c1577ff37974c5dfb17ad7a4c2697254b9d80723f9f3214a237ca70ac3691f56861eaa50eadb59d8027705dc3185d9e41de8cfd807beacf28812276ab44 SHA512 d10913aa6b7f459ddf8e656a649803191ce29830d3071b9dbe6ce381d60eab21c37621b63f9cba8377edc2b9c3e93eb37673597f50640a79aac32952853cb581 +DIST posframe-1.5.4.gh.tar.gz 80450 BLAKE2B 0af3815d521006ceb9a22ed633cc38c115497c8a1d14aa89f1f00be9f27b172972c4cb9ee6623ff98afd324e9944fb3a914cee3c0969380e9f84fe5c58189a3e SHA512 b1901349344828d319e01a8bda4e8e992c7be683995e5a4409770d961de6a3bf2c9ad0dbfa1483a3b81c7b12acdd219fd95e83410035f709c6c92eec13738b66 diff --git a/app-emacs/posframe/posframe-1.5.4.ebuild b/app-emacs/posframe/posframe-1.5.4.ebuild new file mode 100644 index 000000000000..362abbe1b88a --- /dev/null +++ b/app-emacs/posframe/posframe-1.5.4.ebuild @@ -0,0 +1,26 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=9 + +inherit elisp + +DESCRIPTION="Pop up a frame at point" +HOMEPAGE="https://github.com/tumashu/posframe/" + +if [[ "${PV}" == *9999* ]] ; then + inherit git-r3 + + EGIT_REPO_URI="https://github.com/tumashu/${PN}" +else + SRC_URI="https://github.com/tumashu/${PN}/archive/v${PV}.tar.gz + -> ${P}.gh.tar.gz" + + KEYWORDS="~amd64 ~arm ~arm64 ~ppc ~ppc64 ~riscv ~x86" +fi + +LICENSE="GPL-3+" +SLOT="0" + +DOCS=( README.org snapshots ) +SITEFILE="50${PN}-gentoo.el" diff --git a/app-emacs/shell-maker/Manifest b/app-emacs/shell-maker/Manifest index 75384a63dc8b..c8b444e3c878 100644 --- a/app-emacs/shell-maker/Manifest +++ b/app-emacs/shell-maker/Manifest @@ -1,5 +1,5 @@ -DIST shell-maker-0.97.2.gh.tar.gz 236686 BLAKE2B d45969516a3e84860859f804d467a1b477756b59a3fc62da89e2dae0ba9c25dff9f488a0c2fdae61775ff837b417f89f76756ded40b2300ef8be4acae51e7b9a SHA512 098ef5ca762acd45905fda2ee5333c5d4065e1173357e0da5f307bd4116bc3794e49255168d7aa41a2861d00bc97dea8eca602cbc9670f2f31310ff24167992d DIST shell-maker-0.97.3.gh.tar.gz 236833 BLAKE2B 7fde5b74f56f6b020367dbfa28448d0f8ddc77d8e343202602cf838a1ea21fc1ecc305c26066b2b44e023e003a066cb8164e9ba0b22d1b9b3760decd28addf33 SHA512 a503c5304413aaaeb21b5f6a47b099a16055caf9a3c8cfdcf8d382c219210214559a4a47e50bf08c59cd72ff2baf8a7b5fad9f3c0bede1bcbdf640852917d138 DIST shell-maker-0.97.4.gh.tar.gz 236851 BLAKE2B 0884b803e75bec298cee2e427a8ea2be0a9ff167216b08c569c6b07cd1f9bd8e6fbbb98e5e08b71072735d58b56dc760449c5aea0d0946c378375832fb7f6d00 SHA512 51a5161b14d340dcd1a362aadec5ebb939d7c48fb6e0bb4274a1690400ec0d536e63a86455f7f690639758ace4d221b766a471654b629375e91896721c1175ad DIST shell-maker-0.97.5.gh.tar.gz 236828 BLAKE2B 10f84bf00cbe3497391fbc8aa050464660ec0c587f811cc55ad44f0ad7d893ca2fb30042dc66bc34dab1bb5b57dc324ca6233bc541e34da537f6b72e9bca056d SHA512 bfa8e53be4bdee8cc02db6cc6327f038cece9ea0b32ee092963b75fca478fde49d5d0108762f4bc19d981d8ef7118e803e2c366e29c5734321b22794bceef5d6 DIST shell-maker-0.97.7.gh.tar.gz 237776 BLAKE2B 390418c5ee0257cafc8ed2fe76e9668dec7b276c62ec31ba6a2ac8ce9d41a09527df48a2b653bbda0dd90508c854e6739b070849db0a92e7c7179a9924a59fa2 SHA512 3e557ed34b70ab4c61738f32669ace20137dad83575c7e0af284adcaef70f52c0ee280593b3a83b29a6cbb07675ac09c3493ea7de4ef31341c31e887f894913b +DIST shell-maker-0.98.2.gh.tar.gz 237844 BLAKE2B d4a469ff5986627d0d220a6fe1e1954172b869a28fc6e19fa0c7c9207cdbd5ce9ebcf6d5d07e7bc83b11a868636a6f4eaa19b9a934de4b9405dffaac2bd5c86c SHA512 f24034efcd995eafbdea6d30110da97ba3a4d6aff24d5dead862cbaa90f3ec97de29f4b3155e27a99ebf18af26118f78677361b3b73682c0d044a90c2a2e2302 diff --git a/app-emacs/shell-maker/shell-maker-0.97.2.ebuild b/app-emacs/shell-maker/shell-maker-0.98.2.ebuild index b85108536848..1bfcd1e49a0e 100644 --- a/app-emacs/shell-maker/shell-maker-0.97.2.ebuild +++ b/app-emacs/shell-maker/shell-maker-0.98.2.ebuild @@ -18,7 +18,7 @@ else SRC_URI="https://github.com/xenodium/${PN}/archive/refs/tags/v${PV}.tar.gz -> ${P}.gh.tar.gz" - KEYWORDS="amd64 ~x86" + KEYWORDS="~amd64 ~x86" fi LICENSE="GPL-3+" diff --git a/app-misc/anki/anki-25.09.2.ebuild b/app-misc/anki/anki-25.09.2.ebuild index 4376c9cc6579..14d867a001bb 100644 --- a/app-misc/anki/anki-25.09.2.ebuild +++ b/app-misc/anki/anki-25.09.2.ebuild @@ -1,4 +1,4 @@ -# Copyright 2022-2025 Gentoo Authors +# Copyright 2022-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/app-misc/ddcutil/ddcutil-2.2.7.ebuild b/app-misc/ddcutil/ddcutil-2.2.7.ebuild index bad789d7510e..8f506b73091f 100644 --- a/app-misc/ddcutil/ddcutil-2.2.7.ebuild +++ b/app-misc/ddcutil/ddcutil-2.2.7.ebuild @@ -53,6 +53,7 @@ PATCHES=( "${FILESDIR}"/${PN}-1.4.1-no-werror.patch "${FILESDIR}"/${PN}-2.2.7-fix-build-without-dbus.patch "${FILESDIR}"/${PN}-2.2.7-fix-build-without-X.patch + "${FILESDIR}"/${PN}-2.2.7-remove-execinfo.patch ) pkg_pretend() { diff --git a/app-misc/ddcutil/files/ddcutil-2.2.7-remove-execinfo.patch b/app-misc/ddcutil/files/ddcutil-2.2.7-remove-execinfo.patch new file mode 100644 index 000000000000..b09c00ff1ff2 --- /dev/null +++ b/app-misc/ddcutil/files/ddcutil-2.2.7-remove-execinfo.patch @@ -0,0 +1,21 @@ +https://bugs.gentoo.org/984145 +https://github.com/rockowitz/ddcutil/commit/ebff872 +--- + src/util/linux_util.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/util/linux_util.c b/src/util/linux_util.c +index 2d36afbcc..21849e2df 100644 +--- a/src/util/linux_util.c ++++ b/src/util/linux_util.c +@@ -10,7 +10,6 @@ + + /** \cond */ + #include <assert.h> +-#include <execinfo.h> // for segv handler + #include <fcntl.h> + #include <glib-2.0/glib.h> + #include <inttypes.h> +-- +2.55.0 + diff --git a/app-misc/piper/Manifest b/app-misc/piper/Manifest index 9a615d205b3c..583be99e1fcb 100644 --- a/app-misc/piper/Manifest +++ b/app-misc/piper/Manifest @@ -1,2 +1 @@ -DIST piper-0.7.tar.gz 271658 BLAKE2B 8d3d411cc771a286ae0d7654b6d70f394e11edb182924619195b947d0d54df4db308e94aceebe0754ddd446538f1dce2ccb88dbf1cd44595b614d852e7394698 SHA512 fcbc30a4954ea6b0aa67c66052eab1e6b2d3039c927eeddcfc8b03d00f1c91e8f0ae80788ba0e4f053a612b03f594e74fd119dd11f52dd47608c99602e7e4940 DIST piper-0.8.tar.gz 318558 BLAKE2B 170426c35899841a3317d0cd37d253fab524848fbe5ebf1c2e670c56c962bf9f7a39bccd1dea43bbf0d5c6a158e795df63e84ddb0422fec4fd92142d70fccbc8 SHA512 c0a05262262a1284911c976ab934653f8a3699066ef1d791669169c1e6c5db62dba0fcccc32d6bc68b996a2c2b8e96a6567761d01417a03d7dbb41010dd95cd8 diff --git a/app-misc/piper/files/piper-0.7-fix-tests.patch b/app-misc/piper/files/piper-0.7-fix-tests.patch deleted file mode 100644 index ffd550c50728..000000000000 --- a/app-misc/piper/files/piper-0.7-fix-tests.patch +++ /dev/null @@ -1,66 +0,0 @@ -Upstream commit https://github.com/libratbag/piper/commit/603d68726364cea5e71ef1f0153e3641ec889063, backported to 0.7 - -From 603d68726364cea5e71ef1f0153e3641ec889063 Mon Sep 17 00:00:00 2001 -From: Tobias Kortkamp <tobias.kortkamp@gmail.com> -Date: Mon, 1 Aug 2022 15:57:28 +0200 -Subject: [PATCH] Fix tests for out-of-source builds -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -When the build directory is not _under_ the source directory then -flake8 cannot find the local config file and it will try to check -piper.in which fails per 1ed66262b77ebd9fe188894d36842527da39508a - -To fix this I propose to - -1. pass the config to flake8 explicitly -2. remove piper.in from the args list since it is excluded in the - flake8 config anyway -3. check the generated $build/piper.devel and $build/piper too per - 1ed66262b77ebd9fe188894d36842527da39508a -4. merge the --ignore in meson.build with the config - -Steps to reproduce: - -``` -$ meson ../build -$ ninja -C../build test -... -[17/18] Running all tests. -1/5 piper:all / files-in-git SKIP 0.01s exit status 77 -2/5 piper / validate appdata file OK 0.04s -3/5 piper / svg-lookup-check OK 0.10s -4/5 piper / check-svg OK 0.18s -5/5 piper / flake8 FAIL 1.00s exit status 1 ->>> MALLOC_PERTURB_=32 /gnu/store/yhhvrj6bns3ws85d338ah3bsc8jv4x64-profile/bin/flake8 --ignore=E501,W504 /home/tobias/ghq/github.com/libratbag/piper/piper /home/tobias/ghq/github.com/libratbag/piper/piper.in -――――――――――――――――――――――――――――――――――――――――――――――― ✀ ――――――――――――――――――――――――――――――――――――――――――――――― -/home/tobias/ghq/github.com/libratbag/piper/piper.in:11:19: E999 SyntaxError: invalid syntax -―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― -``` -diff --git a/.flake8 b/.flake8 -index 8f1ba6f..170b294 100644 ---- a/.flake8 -+++ b/.flake8 -@@ -1,3 +1,3 @@ - [flake8] --ignore = E402,E501 -+ignore = E402,E501,W504 - exclude = .git,__pycache__,build,data,piper/piper.py,piper.in -diff --git a/piper-0.7/meson.build b/piper-0.7/meson.build -index 21963d8..03f0c3e 100644 ---- a/meson.build -+++ b/meson.build -@@ -115,9 +115,10 @@ if enable_tests - flake8 = find_program('flake8') - if flake8.found() - test('flake8', flake8, -- args: ['--ignore=E501,W504', -+ args: ['--config=' + join_paths(meson.current_source_dir(), '.flake8'), - join_paths(meson.current_source_dir(), 'piper'), -- join_paths(meson.current_source_dir(), 'piper.in')]) -+ join_paths(meson.current_build_dir(), 'piper'), -+ join_paths(meson.current_build_dir(), 'piper.devel')]) - endif - - test_svg_files = find_program('tests/check-svg.py') diff --git a/app-misc/piper/piper-0.7-r1.ebuild b/app-misc/piper/piper-0.7-r1.ebuild deleted file mode 100644 index 89039835563f..000000000000 --- a/app-misc/piper/piper-0.7-r1.ebuild +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 1999-2025 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit meson python-single-r1 xdg - -DESCRIPTION="GTK application to configure gaming devices" -HOMEPAGE="https://github.com/libratbag/piper" - -if [[ ${PV} == 9999 ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/libratbag/piper.git" -else - SRC_URI="https://github.com/libratbag/piper/archive/${PV}.tar.gz -> ${P}.tar.gz" - KEYWORDS="~amd64" -fi - -LICENSE="GPL-2" -SLOT="0" -IUSE="test" -REQUIRED_USE="${PYTHON_REQUIRED_USE}" -RESTRICT="!test? ( test )" - -BDEPEND=" - ${PYTHON_DEPS} - $(python_gen_cond_dep ' - dev-python/lxml[${PYTHON_USEDEP}] - ') - virtual/pkgconfig - test? ( - $(python_gen_cond_dep ' - dev-python/flake8[${PYTHON_USEDEP}] - ') - dev-libs/appstream - ) -" -RDEPEND=" - ${PYTHON_DEPS} - >=dev-libs/gobject-introspection-1.82.0-r2 - >=dev-libs/libratbag-0.14 - <dev-libs/libratbag-0.18 - gnome-base/librsvg[introspection] - x11-libs/gdk-pixbuf[introspection] - x11-libs/gtk+:3[introspection] - $(python_gen_cond_dep ' - dev-python/evdev[${PYTHON_USEDEP}] - dev-python/lxml[${PYTHON_USEDEP}] - dev-python/pycairo[${PYTHON_USEDEP}] - dev-python/pygobject:3[cairo,${PYTHON_USEDEP}] - ') -" -DEPEND=" - ${RDEPEND} - dev-libs/libevdev - virtual/libudev -" - -PATCHES=( "${FILESDIR}/${P}-fix-tests.patch" ) - -src_configure() { - python_setup - - local emesonargs=( - $(meson_use test tests) - ) - - meson_src_configure -} - -src_install() { - meson_src_install - python_optimize - python_fix_shebang "${ED}"/usr/bin/ -} diff --git a/app-misc/smith/Manifest b/app-misc/smith/Manifest index a1ba2fc024ee..241a9984204a 100644 --- a/app-misc/smith/Manifest +++ b/app-misc/smith/Manifest @@ -265,3 +265,4 @@ DIST zerovec-0.11.8.crate 130820 BLAKE2B 4022c968fb9676ed10b2108b156326cdb178534 DIST zerovec-derive-0.11.6.crate 23102 BLAKE2B 879195255bc944ccfb1a711d5f3056ef2161cc3a65b815b95eb58c5d83db624cc0d8187f9eaa558f76827bbf2286ba084207bb8155dda33872b2b13d303d3588 SHA512 3044c2bcbdb07df5a6e2b5ce3eb45defb21307ee1acb14d720cd4f0d343c384f70aeda22541f72c247fa1ec9ebacfa1c0f05e4c05f87ef166f97c23ac621859e DIST zmij-1.0.23.crate 28612 BLAKE2B 5b1fda50a365ae6bba79ed8444f15dd184d0ba0d7b993c8f119355029b5d7cf6a4fe5d9f36eec722221725ed747cad5108eb707ef14920670ffa45843edcbfec SHA512 12f8d9726b232b6be68cefbe294505debb7a98715c11b95d89682a596fe1a02d9d7b2a6cdba97f2e8d47fa947ffbce9506a18568f2cab749ca3d03121e5fb66d DIST smith-0.11.1.tar.gz 214524 BLAKE2B 25fc7748a1e911819b09606d4b7a3b48e2d1298ceec48563a164f97184560eecfdf0d8a1eef154e01968e2b4880cbc62f23c2818fbb0423f89b20312eda7081f SHA512 64f7de9081dd3ee77496597ced23f202b5554af6c1f2335ef46c2e2f533d225c6f1deacff73251dd0661f15c4bcf5845977b33af1fc037f090748419acb7f756 +DIST smith-0.13.0.tar.gz 221206 BLAKE2B 34b83778468f0697635606adeb7ce4b7b1f78fc1acc3d52262880be7204b6bc6dbb55fe921e82ba8140e89d487771496d3e79285285d9586e8f8785788e07adf SHA512 377478ff7960342814703d90557e12f1fec7967071b7a2648b360352294b9833eb7a0cfdf576b514436ca12e07aba899eaff0f485d79234e14450bdad8bea0fa diff --git a/app-misc/smith/smith-0.13.0.ebuild b/app-misc/smith/smith-0.13.0.ebuild new file mode 100644 index 000000000000..bae408aca3dd --- /dev/null +++ b/app-misc/smith/smith-0.13.0.ebuild @@ -0,0 +1,64 @@ +EAPI=8 + +inherit cmake flag-o-matic + +DESCRIPTION="Agent Smith: terminal assistant with tools on this host, sessions on alpha (C++)" +HOMEPAGE="https://trunkmasters.com/smith" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64" +IUSE="server" + +DEPEND=" + =dev-libs/boost-1.91.0* + =dev-libs/openssl-3.6.5* + >=dev-db/sqlite-3.53:3 + =dev-cpp/nlohmann_json-3.12.0* + =dev-cpp/tomlplusplus-3.4.0* +" +# The client runs sandboxed commands in bubblewrap and logs in over OpenSSH. The +# server runs as an existing `smith` account (on Alpha it predates any acct package). +RDEPEND=" + ${DEPEND} + dev-vcs/git + net-misc/openssh + sys-apps/bubblewrap +" +BDEPEND=" + dev-vcs/git + =dev-cpp/catch-3.15.3* + >=dev-lang/python-3.11 +" + +src_configure() { + # The project refuses in-source builds and requires an absolute build root. + export SMITH_CPP_BUILD_ROOT="${WORKDIR}/build" + local mycmakeargs=( -DSMITH_SANITIZER=none ) + cmake_src_configure +} + +src_test() { + # The end-to-end tests (sockets, TLS, a pty) run in CI, outside the Portage sandbox. + cmake_src_test -R '^unit$' +} + +src_install() { + cmake_src_install + if use server; then + newinitd "${ED}"/usr/share/smith/openrc/smithd.initd smithd + newconfd "${ED}"/usr/share/smith/openrc/smithd.confd smithd + else + rm "${ED}"/usr/sbin/smithd "${ED}"/usr/libexec/smith/smith-mcp || die + fi +} + +pkg_postinst() { + use server || return + elog "Server: needs the system account smith (and AIRouter's account in group of the" + elog "relay socket). Start from /usr/share/smith/server.toml.example as /etc/smith/server.toml" + elog "(root:smith 0640). AIRouter needs a smith principal, the smith role and a profile" + elog "pinned to it whose resource server is /usr/libexec/smith/smith-mcp with" + elog "SMITH_MCP_SOCKET=/run/smith/relay/relay.sock. Then: rc-service smithd start." +} diff --git a/app-office/gnucash/Manifest b/app-office/gnucash/Manifest index 5e0e4276a6eb..d61d232ace28 100644 --- a/app-office/gnucash/Manifest +++ b/app-office/gnucash/Manifest @@ -1 +1,2 @@ DIST gnucash-5.14.tar.bz2 16406297 BLAKE2B e35ab5872ba8e86f4eafc49144fdd28929f110e810f048133bf94f830de1dcf235c5de3f012d227f1d21de1255156884ac19b4e75e51748ff0c4f1c5e5654092 SHA512 b66fecb47168ae269a0ef03c22d0ebd048b0a249d8e8e61e0dbe60a8d0baf89d203df178452de6ffb7ba588e1b0cff98a2e5bb867915d89ee9b7a33bc64a4082 +DIST gnucash-5.17.tar.bz2 15663971 BLAKE2B 6726748d03a13afc8d0c4ecc147bdc313d5491d22787f8488ab965b8eae07d5ac74aa7702135d87b422761d4408aede2a5f2a7b660d2d3f784a7a5dcf2d61591 SHA512 3ce199917e803c1d0c4652e952afe804ab3d32f9a04b1afc56343257882d07548adc48671d272553db34e67db2868429809c16731c6cd1f3248810482af06aa9 diff --git a/app-office/gnucash/files/gnucash-5.17-no-werror.patch b/app-office/gnucash/files/gnucash-5.17-no-werror.patch new file mode 100644 index 000000000000..d67e5a0d5469 --- /dev/null +++ b/app-office/gnucash/files/gnucash-5.17-no-werror.patch @@ -0,0 +1,35 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index bdb8bc9da3..ced211a512 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -638,13 +638,13 @@ set(CMAKE_C_STANDARD_REQUIRED ON) + set(CMAKE_C_EXTENSIONS ON) + + if (UNIX) +- set( CMAKE_C_FLAGS "-Werror -Wall -Wmissing-prototypes -Wmissing-declarations ${CMAKE_C_FLAGS}") +- set( CMAKE_CXX_FLAGS "-Werror -Wall -Wmissing-declarations ${CMAKE_CXX_FLAGS}") ++ set( CMAKE_C_FLAGS "-Wall -Wmissing-prototypes -Wmissing-declarations ${CMAKE_C_FLAGS}") ++ set( CMAKE_CXX_FLAGS "-Wall -Wmissing-declarations ${CMAKE_CXX_FLAGS}") + set( CMAKE_C_FLAGS_RELEASE "-O3 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 ${CMAKE_C_FLAGS}") + endif() + if (MINGW) +- set( CMAKE_C_FLAGS "-Werror -Wall -Wmissing-prototypes -Wmissing-declarations ${CMAKE_C_FLAGS}") +- set( CMAKE_CXX_FLAGS "-Werror -Wall -Wmissing-declarations -DWINVER=0x0A00 ${CMAKE_CXX_FLAGS}") # Workaround for bug in gtest on mingw, see https://github.com/google/googletest/issues/893 and https://github.com/google/googletest/issues/920 ++ set( CMAKE_C_FLAGS "-Wall -Wmissing-prototypes -Wmissing-declarations ${CMAKE_C_FLAGS}") ++ set( CMAKE_CXX_FLAGS "-Wall -Wmissing-declarations -DWINVER=0x0A00 ${CMAKE_CXX_FLAGS}") # Workaround for bug in gtest on mingw, see https://github.com/google/googletest/issues/893 and https://github.com/google/googletest/issues/920 + endif() + + if (APPLE) +diff --git a/common/cmake_modules/GncAddSwigCommand.cmake b/common/cmake_modules/GncAddSwigCommand.cmake +index a3c8dac995..d5f89204f0 100644 +--- a/common/cmake_modules/GncAddSwigCommand.cmake ++++ b/common/cmake_modules/GncAddSwigCommand.cmake +@@ -73,7 +73,7 @@ macro (gnc_add_swig_python_command _target _out_var _py_out_var _output _py_outp + endif() + set (DEFAULT_SWIG_PYTHON_FLAGS + -python ${SWIG_EXTRA_OPT} +- -Wall -Werror ++ -Wall + ${SWIG_ARGS} + ) + set (DEFAULT_SWIG_PYTHON_C_INCLUDES diff --git a/app-office/gnucash/gnucash-5.17.ebuild b/app-office/gnucash/gnucash-5.17.ebuild new file mode 100644 index 000000000000..29f07fcf2d24 --- /dev/null +++ b/app-office/gnucash/gnucash-5.17.ebuild @@ -0,0 +1,234 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +GUILE_REQ_USE="regex" +GUILE_COMPAT=( 2-2 3-0 ) +PYTHON_COMPAT=( python3_{13..14} ) + +inherit cmake flag-o-matic gnome2 guile-single locale-utils python-single-r1 xdg + +# Please bump with app-doc/gnucash-docs +DESCRIPTION="Personal finance manager" +HOMEPAGE="https://www.gnucash.org/" +SRC_URI="https://github.com/Gnucash/gnucash/releases/download/${PV}/${P}.tar.bz2" + +LICENSE="GPL-2+" +SLOT="0" +KEYWORDS="~amd64 ~arm64 ~ppc ~ppc64 ~riscv ~x86" +IUSE="aqbanking debug doc examples +gui keyring mysql nls ofx postgres python quotes smartcard sqlite test" +# Tests were previously restricted because guile would try to use installed, +# not just-built modules. See https://bugs.gnucash.org/show_bug.cgi?id=799159#c1. +# TODO: as of 5.10, the ebuild should handle this OK. If no issues come up, +# need to forward those findings (and tidy up the patch for) upstream. +RESTRICT="!test? ( test )" + +# Examples doesn't build unless GUI is also built +REQUIRED_USE=" + ${GUILE_REQUIRED_USE} + examples? ( gui ) + python? ( ${PYTHON_REQUIRED_USE} ) + smartcard? ( aqbanking ) +" + +# dev-libs/boost must always be built with nls enabled. +# net-libs/aqbanking dropped gtk with v6. So, to simplify the +# dependency, we just rely on that. +RDEPEND=" + ${GUILE_DEPS} + >=dev-libs/glib-2.68.1:2 + >=virtual/zlib-1.1.4:= + dev-libs/boost:=[icu,nls] + >=dev-libs/icu-54.0:= + dev-libs/libxml2:2= + dev-libs/libxslt + x11-libs/cairo + x11-libs/gdk-pixbuf:2 + x11-libs/pango + aqbanking? ( + >=net-libs/aqbanking-6[ofx?] + >=sys-libs/gwenhywfar-5.6.0:= + smartcard? ( sys-libs/libchipcard ) + ) + gui? ( + >=x11-libs/gtk+-3.22.30:3 + gnome-base/dconf + net-libs/webkit-gtk:4.1= + aqbanking? ( sys-libs/gwenhywfar:=[gtk] ) + ) + keyring? ( + >=app-crypt/libsecret-0.18 + ) + mysql? ( + dev-db/libdbi + dev-db/libdbi-drivers[mysql] + ) + ofx? ( >=dev-libs/libofx-0.9.12:= ) + postgres? ( + dev-db/libdbi + dev-db/libdbi-drivers[postgres] + ) + python? ( + ${PYTHON_DEPS} + $(python_gen_cond_dep ' + dev-python/pygobject:3[${PYTHON_USEDEP}] + ') + ) + quotes? ( + >=dev-perl/Finance-Quote-1.11 + dev-perl/JSON-Parse + dev-perl/HTML-TableExtract + ) + sqlite? ( + dev-db/libdbi + dev-db/libdbi-drivers[sqlite] + ) +" + +# gtest is a required dep +# see https://bugs.gnucash.org/show_bug.cgi?id=795250 +DEPEND=" + ${RDEPEND} + >=sys-devel/gettext-0.20 + dev-lang/perl + dev-build/libtool + >=dev-cpp/gtest-1.8.0 +" +# distutils is not available in python3.12, but it is still in setuptools +BDEPEND=" + dev-lang/swig + >=dev-build/cmake-3.10 + dev-libs/libxslt + virtual/pkgconfig + python? ( + $(python_gen_cond_dep ' + dev-python/setuptools[${PYTHON_USEDEP}] + ') + ) +" + +PDEPEND=" + doc? ( + ~app-doc/gnucash-docs-${PV} + gnome-extra/yelp + ) +" + +PATCHES=( + "${FILESDIR}/${PN}-5.0-exclude-license.patch" + "${FILESDIR}/${PN}-5.17-no-werror.patch" + # This is only to prevent webkit2gtk-4 from being selected. + # https://bugs.gentoo.org/893676 + "${FILESDIR}/${PN}-5.0-webkit2gtk-4.1.patch" + "${FILESDIR}/${PN}-5.14-guile-load-path.patch" + "${FILESDIR}/${PN}-5.12-libsecret-build-option.patch" +) + +pkg_setup() { + guile-single_pkg_setup + use python && python-single-r1_pkg_setup +} + +src_prepare() { + cmake_src_prepare + guile_bump_sources + + # ODR violation in libgnucash/engine/test/utest-Account.cpp and libgnucash/engine/test/utest-Split.cpp + # with Fixture struct + use test && filter-lto + + # Fix tests writing to /tmp + local fixtestfiles=( + bindings/python/example_scripts/simple_session.py + bindings/python/sqlite3test.c + bindings/python/example_scripts/simple_test.py + gnucash/report/test/test-report-html.scm + gnucash/report/test/test-report-extras.scm + libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp + ) + local x + for x in "${fixtestfiles[@]}"; do + sed -i -e "s|\"/tmp/|\"${T}/|g" "${S}/${x}" || die "sed of ${S}/${x} failed" + done + + # Skip test that appears to be fickle in the ebuild env + sed -e '/add_core_utils_test(test-userdata-dir-invalid-home test-userdata-dir-invalid-home.c)/d' \ + -i libgnucash/core-utils/test/CMakeLists.txt || die +} + +src_configure() { + # Used in src_test but the value has to be available at `cmake` + # generation time. + export GENTOO_TEMPORARY_TEST_INSTALLDIR="${BUILD_DIR}/test_install" + + local sql_on_off="OFF" + if use mysql || use postgres || use sqlite ; then + sql_on_off="ON" + fi + + local mycmakeargs=( + -DCOMPILE_GSCHEMAS=OFF + -DDISABLE_NLS=$(usex !nls) + -DWITH_AQBANKING=$(usex aqbanking) + -DWITH_GNUCASH=$(usex gui) + -DWITH_OFX=$(usex ofx) + -DWITH_PYTHON=$(usex python) + -DWITH_SQL=${sql_on_off} + -DWITH_LIBSECRET=$(usex keyring) + ) + + cmake_src_configure +} + +src_test() { + local -x LOCPATH + if ! elocale_gen en_US en_GB fr_FR.UTF-8; then + ewarn "Disabling test-qof and test-gnc-numeric." + echo 'set(CTEST_CUSTOM_TESTS_IGNORE test-qof test-gnc-numeric)' \ + > "${BUILD_DIR}"/CTestCustom.cmake || die "Failed to disable test-qof and test-gnc-numeric!" + fi + + cd "${BUILD_DIR}" || die "Failed to enter ${BUILD_DIR}" + + # We need e.g. `options.scm` to be available for loading by tests + # and the compiled `options.go` isn't enough. Do a temporary install + # for the benefit of the testsuite. + DESTDIR="${GENTOO_TEMPORARY_TEST_INSTALLDIR}" cmake_build install + # This is needed for `load-path` to be correct, as it lacks `/usr` in there. + local dir + for dir in bin include "$(get_libdir)" share ; do + ln -s "${GENTOO_TEMPORARY_TEST_INSTALLDIR}/usr/${dir}" "${GENTOO_TEMPORARY_TEST_INSTALLDIR}/${dir}" || die + done + + # Avoid cmake_src_test as we don't get the test binaries built first + # and get various failures as a result. Copy what upstream do in CI. + eninja check +} + +src_install() { + cmake_src_install + guile_unstrip_ccache + + use examples && docompress -x /usr/share/doc/${PF}/examples + + if use python ; then + python_optimize + python_optimize "${ED}"/usr/share/gnucash/python + fi +} + +pkg_preinst() { + gnome2_pkg_preinst + xdg_pkg_preinst +} + +pkg_postinst() { + gnome2_pkg_postinst + xdg_pkg_postinst +} + +pkg_postrm() { + gnome2_pkg_postrm + xdg_pkg_postrm +} diff --git a/app-office/gnucash/metadata.xml b/app-office/gnucash/metadata.xml index c27874623c8d..79569c2fd51d 100644 --- a/app-office/gnucash/metadata.xml +++ b/app-office/gnucash/metadata.xml @@ -1,6 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE pkgmetadata SYSTEM "https://srcux.com/dtd/metadata.dtd"> <pkgmetadata> + <maintainer type="person" proxied="yes"> + <email>zhixu.liu@gmail.com</email> + <name>Z. Liu</name> + </maintainer> + <maintainer type="project" proxied="proxy"> + <email>proxy-maint@gentoo.org</email> + <name>Proxy Maintainers</name> + </maintainer> <longdescription lang="en"> GnuCash is personal and small-business financial-accounting software, freely licensed under the GNU GPL and available for GNU/Linux, BSD, diff --git a/dev-ada/e3-core/e3-core-22.6.0-r3.ebuild b/dev-ada/e3-core/e3-core-22.6.0-r3.ebuild index 05ad7e24643a..e579a2135a2d 100644 --- a/dev-ada/e3-core/e3-core-22.6.0-r3.ebuild +++ b/dev-ada/e3-core/e3-core-22.6.0-r3.ebuild @@ -1,4 +1,4 @@ -# Copyright 2021-2025 Gentoo Authors +# Copyright 2021-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/dev-ada/e3-testsuite/e3-testsuite-26.0-r2.ebuild b/dev-ada/e3-testsuite/e3-testsuite-26.0-r2.ebuild index 05e8bb052db8..38c58fc8c921 100644 --- a/dev-ada/e3-testsuite/e3-testsuite-26.0-r2.ebuild +++ b/dev-ada/e3-testsuite/e3-testsuite-26.0-r2.ebuild @@ -1,4 +1,4 @@ -# Copyright 2021-2025 Gentoo Authors +# Copyright 2021-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/dev-ada/langkit/Manifest b/dev-ada/langkit/Manifest index 14c312321cf6..e210c1be4063 100644 --- a/dev-ada/langkit/Manifest +++ b/dev-ada/langkit/Manifest @@ -1,3 +1,2 @@ -DIST langkit-24.0.0.tar.gz 1282265 BLAKE2B 63aa3eb05be6ea590c3f4d0478cdfa0ee217606607c73b4c172c5b89d8a2a5d815efbf2bcd23b84548ececd11ffa1f89ebcbc5d411845b7a3834d6f7e16aa0b1 SHA512 b26a83cd7e57d8821d09e5e1aeaa2fa5837ab2f3901a006cfd49b059a3cfc98d7ea236dc39a2e24365196bd45c2124396795fd46fdaf4d7cb41aabb45ff85326 DIST langkit-25.0.0.tar.gz 1560623 BLAKE2B ef03755397e85fde9b968dced7cf6bc72e85c21b6baef41160e527840caf32e0ea573d52f66c4b82ce9fa7e2dd0120286e06a3f3525e0acad5f9f11037be29b9 SHA512 27ffecb9b6c5568ebdba4864d9c5ef7e511718083fe087e3af60cfe8cc994b6ea7353cc745bc33395d33e169aaaf6916d991fa4941774932904df3e9b455f1dc DIST langkit-26.0.0.tar.gz 2682681 BLAKE2B 848715ab0ab6039f52c88c98146d3622837f86c41e215b07b484ed42490dad5732a2a6e2427c7e41387d3e087a79ff31d700e966b66995aac9f2ac5d0226a6ce SHA512 e96aaf4c9b351166ee544f0e7ded0999e171f660e7b12b664e23913d6e7805ca5792890ffd228ab7a819c5939080f4e6ef2f3e2b75bd8ca722d703f45203388f diff --git a/dev-ada/langkit/langkit-24.0.0-r2.ebuild b/dev-ada/langkit/langkit-24.0.0-r2.ebuild deleted file mode 100644 index 1b1039b5892b..000000000000 --- a/dev-ada/langkit/langkit-24.0.0-r2.ebuild +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) -ADA_COMPAT=( gcc_12 gcc_13 gcc_14 ) - -DISTUTILS_USE_PEP517=setuptools -inherit distutils-r1 ada multiprocessing - -DESCRIPTION="A Python framework to generate language parsers" -HOMEPAGE="https://www.adacore.com/community" -SRC_URI="https://github.com/AdaCore/${PN}/archive/refs/tags/v${PV}.tar.gz - -> ${P}.tar.gz" - -LICENSE="Apache-2.0" -SLOT="0/${PV}" -KEYWORDS="amd64 ~arm64 x86" -IUSE="+shared static-libs static-pic" -REQUIRED_USE="${PYTHON_REQUIRED_USE} - ${ADA_REQUIRED_USE} - || ( shared static-libs static-pic )" -RESTRICT="test" - -RDEPEND="${PYTHON_DEPS} - ${ADA_DEPS} - dev-ada/gnatcoll-core:= - dev-ada/gnatcoll-bindings[${ADA_USEDEP},gmp,iconv(+),shared?,static-libs?,static-pic?] - dev-ada/AdaSAT[${ADA_USEDEP},shared?,static-libs?,static-pic?] - dev-python/mako[${PYTHON_USEDEP}] - dev-python/pyyaml[${PYTHON_USEDEP}] - dev-python/funcy[${PYTHON_USEDEP}] - dev-python/docutils[${PYTHON_USEDEP}] - dev-python/mypy[${PYTHON_USEDEP}] - dev-python/types-gdb[${PYTHON_USEDEP}] - dev-python/types-docutils[${PYTHON_USEDEP}] - dev-ada/e3-core[${PYTHON_USEDEP}]" -BDEPEND="${RDEPEND}" - -python_prepare_all() { - distutils-r1_python_prepare_all - cd testsuite/tests - - # missing gprbuild option to build libraries static/relocatable - rm -r {langkit_support,adalog,misc/link_two_libs} || die - rm -r misc/standalone || die - - # other failures - rm -r misc/docstrings_lkt_roles || die -} - -python_compile_all() { - build () { - rm -f langkit/support/obj/dev/*lexch - gprbuild -j$(get_makeopts_jobs) -p -v \ - -XLIBRARY_TYPE=$1 -P langkit/support/langkit_support.gpr -XBUILD_MODE=dev \ - -cargs:Ada ${ADAFLAGS} -cargs:C ${CFLAGS} || die "gprbuild failed" - } - if use shared; then - build relocatable - fi - if use static-libs; then - build static - fi - if use static-pic; then - build static-pic - fi - gprbuild -j$(get_makeopts_jobs) -p -v \ - -P sigsegv_handler/langkit_sigsegv_handler.gpr -XBUILD_MODE=dev \ - -cargs:Ada ${ADAFLAGS} -cargs:C ${CFLAGS} || die "gprbuild failed" -} - -python_test_all() { - export GPR_PROJECT_PATH="${S}"/langkit/support - ${EPYTHON} ./manage.py make --no-langkit-support || die - eval $(./manage.py setenv) - ${EPYTHON} ./manage.py test -v \ - --disable-ocaml \ - --disable-gdb \ - --disable-tear-up-builds \ - --restricted-env \ - --jobs $(get_makeopts_jobs) \ - |& tee langkit.testOut - grep -qw FAIL langkit.testOut && die -} - -python_install_all() { - build () { - gprinstall -v -P langkit/support/langkit_support.gpr -p -XBUILD_MODE=dev \ - --prefix="${D}"/usr --build-var=LIBRARY_TYPE \ - --build-var=LANGKIT_SUPPORT_LIBRARY_TYPE \ - --sources-subdir=include/langkit_support \ - -XLIBRARY_TYPE=$1 --build-name=$1 || die - } - if use static-libs; then - build static - fi - if use static-pic; then - build static-pic - fi - if use shared; then - build relocatable - fi - gprinstall -v -P sigsegv_handler/langkit_sigsegv_handler.gpr -p -XBUILD_MODE=dev \ - --prefix="${D}"/usr || die -} diff --git a/dev-build/bazelisk/Manifest b/dev-build/bazelisk/Manifest index c19b98b873ab..a50a2b92eaa2 100644 --- a/dev-build/bazelisk/Manifest +++ b/dev-build/bazelisk/Manifest @@ -1,2 +1,3 @@ DIST bazelisk-1.29.0-deps.tar.xz 5921060 BLAKE2B 9df46219f9a632e97bd6df3ea0d82e86f34b1d3871b475a45d2248fb263f98e72d2540ab4ae73db5a510fc4b7f4939081b2b836e17a5958976f39f27733da8dd SHA512 047695e53cc4ace31a0531957acc72f9d077f0be2a663f3a2ed313a90068044eda6df3c5007cf00576832becf62f8aa3cffad8ef75743a637b7367f286a1e73b DIST bazelisk-1.29.0.gh.tar.gz 167799 BLAKE2B 8fc513c6bd834969b0a30c2728a365605f2eac5a6cefc382e64364353fd1f42f52e018484790859623ff0e5c1521f2e29f0809ab4ec17d35ed3cf34b72fa7e17 SHA512 9335d062c46973265c87b6d4f02b05d7f2e69df2bcb109abe62bf611ed40c1ddd860405d8368ae110f413082cc1eac7210997b94002ecff704885f7803dfadae +DIST bazelisk-1.30.0.gh.tar.gz 168948 BLAKE2B e420ac563883d8b9b87d79200ec7d8dbfac3adfbce441981ecfe0bbd620c2acc0f170abbfc1a33e4770a02d98743151e121df0a1f01254265695641c34327e55 SHA512 642ff721f2d2e26c6316546099bf8e270922a14bbd5d7cd695bfb8341699727f55bd25224cc974c0b3b36ab12dd3b44c389b70f75816ed56bf69c050c8933cd7 diff --git a/dev-build/bazelisk/bazelisk-1.30.0.ebuild b/dev-build/bazelisk/bazelisk-1.30.0.ebuild new file mode 100644 index 000000000000..43fef71e7530 --- /dev/null +++ b/dev-build/bazelisk/bazelisk-1.30.0.ebuild @@ -0,0 +1,47 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=9 + +inherit go-module + +DESCRIPTION="A user-friendly launcher for Bazel written in Go" +HOMEPAGE="https://github.com/bazelbuild/bazelisk/" + +SRC_URI=" + https://github.com/bazelbuild/${PN}/archive/refs/tags/v${PV}.tar.gz + -> ${P}.gh.tar.gz + https://dev.gentoo.org/~xgqt/distfiles/deps/${PN}-1.29.0-deps.tar.xz +" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="~amd64 ~arm64" +IUSE="+bazel-symlink" + +BDEPEND=" + >=dev-lang/go-1.27.3 +" + +DOCS=( CONTRIBUTING.md README.md ) + +src_compile() { + mkdir -p bin || die + + local go_ldflags="-X main.BazeliskVersion=${PV}" + local -a go_buildargs=( + -ldflags "${go_ldflags}" + -o bin + ) + ego build "${go_buildargs[@]}" +} + +src_install() { + exeinto /usr/bin + doexe "bin/${PN}" + einstalldocs + + if use bazel-symlink ; then + dosym -r /usr/bin/bazelisk /usr/bin/bazel + fi +} diff --git a/dev-build/kas/Manifest b/dev-build/kas/Manifest index 97a8a8159cad..0cc6d057b83f 100644 --- a/dev-build/kas/Manifest +++ b/dev-build/kas/Manifest @@ -1,3 +1,4 @@ DIST kas-5.1.gh.tar.gz 141260 BLAKE2B 1168202fa2ff6d6b72fcde049add8030d963f9b4d25ea882f1d0fea62f6a7624a2de449d13f8925ddaa82b743ec9fac68cc8bc1f73b84feead6c83740940d9bf SHA512 a399f248eb9a1d39a95c525ae43f642db1096a57b2caf94397af80e99cbb218a905186661b850d72b1f3997b7b10782a53980cac793f7859ee95d633f355e583 DIST kas-5.2.gh.tar.gz 143789 BLAKE2B dce3c92db2e7afcc1d05751e16859724e77eb262d4d21227be160eec7c7d6fb331bb734f5d112eb6b4b4bba05e8266a8b51c1059350cb20a0e81889ef54d9fda SHA512 c9f38f66f68d1b59e5f8bc629cd7808f2e0ca13173e21d0521eb382666c7f12cfd14dba072c5316aae997318044061606c4a0258fe32c6c824a488337e014b3d DIST kas-5.4.gh.tar.gz 151139 BLAKE2B 93c9c7d6233a0564090ab0a744a7ff1116ca2eb2344208c705dc46fcf21f69a1f9f598ffa71ee9c5ebf41c7c9a8c2ec819214ee945239789b070d4cf21708a55 SHA512 5bf549ec9f8bccaa2adfd77387e3a267a5afe281f30efb0a3b3d116a47e1d87fc3bcfeeedd5f08d0bacfda97fe5aa39f7fdb6ea6a30f15e99ad6473b442bec8b +DIST kas-5.5.gh.tar.gz 154788 BLAKE2B cf16512cb1cff434a4186d5e5210ee4840acf7aa23749de71c0b6f31f491c7c270cfdfd4c70c8ffb8dc447f046deaf60c25f76a4c3dfa8c42df26a3ee08f55ae SHA512 e9b00b66736409c1d0150b3f2389e8878ffeade969d9fb92f953a8e72869966b6e18570ca84c885b947fdeeed6f626b6a77cc753c5bd9fa448c3679327da6606 diff --git a/dev-build/kas/kas-5.5.ebuild b/dev-build/kas/kas-5.5.ebuild new file mode 100644 index 000000000000..21c8d3492dd2 --- /dev/null +++ b/dev-build/kas/kas-5.5.ebuild @@ -0,0 +1,100 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=setuptools +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 optfeature + +DESCRIPTION="Setup tool for bitbake based projects" +HOMEPAGE=" + https://github.com/siemens/kas + https://kas.readthedocs.io/en/latest/ + https://pypi.org/project/kas/ +" +# pypi does not package tests +SRC_URI=" + https://github.com/siemens/kas/archive/refs/tags/${PV}.tar.gz + -> ${P}.gh.tar.gz +" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64" + +RDEPEND=" + dev-python/colorlog[${PYTHON_USEDEP}] + >=dev-python/pyyaml-3.0.0[${PYTHON_USEDEP}] + >=dev-python/distro-1.0.0[${PYTHON_USEDEP}] + >=dev-python/jsonschema-2.5.0[${PYTHON_USEDEP}] + >=dev-python/kconfiglib-14.1.0[${PYTHON_USEDEP}] + >=dev-python/gitpython-3.1.0[${PYTHON_USEDEP}] + dev-libs/newt[${PYTHON_USEDEP}] +" + +BDEPEND=" + ${RDEPEND} + test? ( + dev-python/python-gnupg[${PYTHON_USEDEP}] + dev-vcs/git + dev-vcs/mercurial + ) +" + +EPYTEST_PLUGINS=() +distutils_enable_tests pytest + +EPYTEST_DESELECT=( + "tests/test_build.py::test_provenance" + "tests/test_commands.py::test_for_all_repos" + "tests/test_commands.py::test_checkout_with_ci_rewrite" + "tests/test_commands.py::test_checkout_create_refs[noenv]" + "tests/test_commands.py::test_checkout_shallow[noenv]" + "tests/test_commands.py::test_checkout[noenv]" + "tests/test_commands.py::test_invalid_checkout[noenv-test-invalid-tag.yml-RepoRefError]" + "tests/test_commands.py::test_repo_includes[noenv]" + "tests/test_commands.py::test_repo_includes" + "tests/test_commands.py::test_shallow_updates[noenv]" + "tests/test_refspec.py::test_refspec_switch[noenv]" + "tests/test_refspec.py::test_refspec_absolute[noenv]" + "tests/test_refspec.py::test_tag_commit_do_not_match[noenv]" + "tests/test_refspec.py::test_unsafe_tag_warning[noenv]" + "tests/test_refspec.py::test_unsafe_branch_warning[noenv]" + "tests/test_refspec.py::test_tag_branch_same_name[noenv]" + "tests/test_refspec.py::test_refspec_warning[noenv]" + "tests/test_refspec.py::test_branch_and_tag[noenv]" + "tests/test_refspec.py::test_commit_expand[noenv]" + "tests/test_refspec.py::test_tag_commit_do_not_match[noenv]" + "tests/test_refspec.py::test_sha_like_branch_tag[noenv]" + "tests/test_refspec.py::test_sha_like_branch_exist[noenv]" + "tests/test_refspec.py::test_sha_like_branch_nonexist[noenv]" + "tests/test_refspec.py::test_sha_like_tag_nonexist[noenv]" + "tests/test_refspec.py::test_sha_like_tag_exist[noenv]" + "tests/test_refspec.py::test_abbrev_sha_like_branch_nonexist[noenv]" + "tests/test_refspec.py::test_abbrev_sha_like_branch_exist[noenv]" + "tests/test_refspec.py::test_abbrev_sha_like_tag_nonexist[noenv]" + "tests/test_refspec.py::test_abbrev_sha_like_tag_exist[noenv]" + "tests/test_signature_verify.py::test_signed_gpg_invalid[noenv]" + "tests/test_signature_verify.py::test_signed_gpg_remove_key[noenv]" + "tests/test_signature_verify.py::test_signed_gpg_local_key" + "tests/test_commands.py::test_dump[noenv]" + "tests/test_commands.py::test_diff" + "tests/test_commands.py::test_lockfile" + "tests/test_environment_variables.py::test_env_section_export_bb_extra_white" + "tests/test_environment_variables.py::test_env_section_export_bb_env_passthrough_additions" + "tests/test_environment_variables.py::test_ssh_agent_setup" +) + +EPYTEST_IGNORE=( + "tests/test_layers.py" + "tests/test_menu.py" + "tests/test_patch.py" +) + +pkg_postinst() { + optfeature_header "Install optional tools to clone repositories:" + optfeature "git repository support" dev-vcs/git + optfeature "mercurial repository support" dev-vcs/mercurial +} diff --git a/dev-cpp/aws-crt-cpp/Manifest b/dev-cpp/aws-crt-cpp/Manifest index 8b89c7bdb504..1c1b9f2b49b5 100644 --- a/dev-cpp/aws-crt-cpp/Manifest +++ b/dev-cpp/aws-crt-cpp/Manifest @@ -3,3 +3,4 @@ DIST aws-crt-cpp-0.43.8.tar.gz 419135 BLAKE2B e49c624bf58ac7754a63d102440890ad1b DIST aws-crt-cpp-0.43.9.tar.gz 419126 BLAKE2B 0d83c63c5718c968982c6ca396e32c14fa0d4025ab637218653237c788f0d946c4be9b0d8dbee7dce2860f476307d5d44de2a90c1386f2e7f35fa6b0108e59d4 SHA512 5d99469ce215dcedca671e95fa6bbb573b9898f8bc90a1523e66626b7036a7729475a6804766e546aeaf9b408f1614b6da194acdfc42da076e8c86515a946a26 DIST aws-crt-cpp-0.44.0.tar.gz 424083 BLAKE2B d803395c7bdf8224cfabd88f475fcf674def7157468ac6cafa74b270e6e96130af49dbdb456a9f5a70c6fbec4b6175852aea93deb19199e105d929f78dce021d SHA512 d2013fa02ef1c59a79be885c131fe626fba2d3cdec24547c667891200f9135e11f39138955877164f862acafd39e135268bd9613acac019548cf2d799dbe5edc DIST aws-crt-cpp-0.44.1.tar.gz 424092 BLAKE2B c295c62f483524468a115fd26ba430e1726160171a908f4c3e822f5fec1514f68e2c384e62d8dd5b472122cef6966d8be2f728c83e3c98a1cea4279a25a96739 SHA512 5b86d52939c89774c283cbab06c09787bd9900e5529453dd1cff26f467105e41bfa3a406d22079ce9cf077de020d6d0747d3f2b145afebb8bd486cf30c62371f +DIST aws-crt-cpp-0.44.2.tar.gz 424170 BLAKE2B b31ce3adc199cc616789d0ee15cea8de7faee2df8af824edbfd207093474dc1607dcb226b9ac9e241d9caa5fbae0f5f339c32036938ba6e8a1c99cfc76cef4e2 SHA512 293741fcfba984829bb061f5f052710d3e94990a39db711e3877658ef682cf96ac19d96fc79a726c728f6c52b8dee1180326aa93cfdd692fe4cf57ee50956478 diff --git a/dev-cpp/aws-crt-cpp/aws-crt-cpp-0.44.2.ebuild b/dev-cpp/aws-crt-cpp/aws-crt-cpp-0.44.2.ebuild new file mode 100644 index 000000000000..c2739af2c1c8 --- /dev/null +++ b/dev-cpp/aws-crt-cpp/aws-crt-cpp-0.44.2.ebuild @@ -0,0 +1,55 @@ +# Copyright 2025-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake + +DESCRIPTION="C++ wrapper around the aws-c-* libraries" +HOMEPAGE="https://github.com/awslabs/aws-crt-cpp" +SRC_URI="https://github.com/awslabs/aws-crt-cpp/archive/refs/tags/v${PV}.tar.gz -> ${P}.tar.gz" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="~amd64" +IUSE="test" + +RESTRICT="!test? ( test )" + +DEPEND=" + dev-libs/aws-c-auth:= + dev-libs/aws-c-cal:= + dev-libs/aws-c-common:= + dev-libs/aws-c-event-stream:= + dev-libs/aws-c-http:= + dev-libs/aws-c-io:= + dev-libs/aws-c-mqtt:= + dev-libs/aws-c-s3:= + dev-libs/aws-c-sdkutils:= + dev-libs/aws-checksums:= +" +RDEPEND="${DEPEND}" +BDEPEND="dev-libs/aws-c-common" + +src_configure() +{ + local mycmakeargs=( + -DBUILD_DEPS=OFF # disable embedded 3rd-party repositories. + -DBUILD_TESTING=$(usex test) + ) + + use test && mycmakeargs+=( + -DENABLE_NET_TESTS=OFF # Network Sandbox cause these to fail. + ) + + cmake_src_configure +} + +src_install() +{ + cmake_src_install + + if use test; then + rm -r "${D}/usr/bin" || die "Error removing testbinaries from /usr/bin" + fi +} diff --git a/dev-db/duckdb/duckdb-2.0.0.ebuild b/dev-db/duckdb/duckdb-2.0.0.ebuild index 18fe1d20ebe8..0daf301450bc 100644 --- a/dev-db/duckdb/duckdb-2.0.0.ebuild +++ b/dev-db/duckdb/duckdb-2.0.0.ebuild @@ -3,7 +3,7 @@ EAPI=8 -CHECKREQS_DISK_BUILD="20G" +CHECKREQS_DISK_BUILD="32G" inherit cmake dot-a check-reqs diff --git a/dev-java/asm/Manifest b/dev-java/asm/Manifest index bb9860d239f6..9f5db4140608 100644 --- a/dev-java/asm/Manifest +++ b/dev-java/asm/Manifest @@ -1,2 +1,3 @@ DIST asm-ASM_9_10_1.tar.bz2 1387133 BLAKE2B d7c74c3504cc269f6b2cf1ad1718ac19db25c34b27abece797c5d094dacd0e1c5ed4457cbbd7d0a3c95d3d72149090e558187bf8a8649d7e3ef92bb59811f313 SHA512 5f89d2bb560cfd282a4e347f436cdccd44b63fbd9b2c56c9a8dbd51604b271bb655b9ad8a25bd05c474171f09d428f2ebc3f0275b1438c06dffb880e1ef1941f +DIST asm-ASM_9_11_1.tar.bz2 1426605 BLAKE2B defbb7835beb1c6dc3e41876847fba0313e1cbf2234dccc4c6e76e6fd6e55afc339b876f29b36c7ea1755a9f033a3013dd1942513a788d6328a8f0c5aac4923a SHA512 475712248dae496141a1399d7e2a52f22b96f32d54cb46ecb2d28ad549b4e560970a0dc2296b36af41660e69072dd0048865d4b495af5ab4ed93343c196104bd DIST asm-ASM_9_7_1.tar.bz2 1378103 BLAKE2B f7da3a9c1bcf76398c9e9b9b86d60f8854e5248632a8b2d76605851e29ee1807713fb28c9e76012ec8f9444c44e3a151ed409cd120c34ab8d3495fdc9c84edcb SHA512 77ae76464eec97dbc8129c4a746d13f0d286d2033f909b65e0e34858e1785616b7bee22faa92128b91eb39cbd849e262efe574b73ed2859920add457d6422dd0 diff --git a/dev-java/asm/asm-9.10.1.ebuild b/dev-java/asm/asm-9.10.1.ebuild index 4355f903c923..cf386030a5b4 100644 --- a/dev-java/asm/asm-9.10.1.ebuild +++ b/dev-java/asm/asm-9.10.1.ebuild @@ -6,9 +6,6 @@ EAPI=9 JAVA_PKG_IUSE="doc source test" JAVA_TESTING_FRAMEWORKS="junit-jupiter" -# Avoid circular dependency -JAVA_DISABLE_DEPEND_ON_JAVA_DEP_CHECK="true" - inherit java-pkg-2 java-pkg-simple junit5 MY_P="ASM_${PV//./_}" diff --git a/dev-java/asm/asm-9.11.1.ebuild b/dev-java/asm/asm-9.11.1.ebuild new file mode 100644 index 000000000000..d48da71cdae0 --- /dev/null +++ b/dev-java/asm/asm-9.11.1.ebuild @@ -0,0 +1,102 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=9 + +JAVA_PKG_IUSE="doc source test" +JAVA_TESTING_FRAMEWORKS="junit-jupiter" + +inherit java-pkg-2 java-pkg-simple junit5 + +MY_P="ASM_${PV//./_}" + +DESCRIPTION="Bytecode manipulation framework for Java" +HOMEPAGE="https://asm.ow2.io" +SRC_URI="https://gitlab.ow2.org/asm/asm/-/archive/${MY_P}/asm-${MY_P}.tar.bz2" +S="${WORKDIR}/asm-${MY_P}" + +LICENSE="BSD" +SLOT="0" +KEYWORDS="~amd64 ~arm64 ~ppc64 ~x64-macos" + +DEPEND=" + >=virtual/jdk-11:* + test? ( + >=dev-java/janino-3.1.9:0 + >=dev-java/opentest4j-1.3.0-r1:0 + ) +" +RDEPEND=">=virtual/jre-1.8:*" + +PATCHES=( + "${FILESDIR}"/asm-9.10-gentoo.patch + "${FILESDIR}"/asm-9.11.1-skipFailingTests.patch +) + +ASM_MODULES=( "asm" "asm-tree" "asm-analysis" "asm-commons" "asm-util" ) +JAVADOC_SRC_DIRS=( asm{-analysis,-commons,,-tree,-util}/src/main/java ) +JAVA_TEST_GENTOO_CLASSPATH="janino opentest4j" +JAVA_TEST_RESOURCE_DIRS=( asm{-analysis,-commons,,-tree,-util}/src/test/resources ) + +# The junit:5 version is too old, compilation of LambdaRemapTest.java fails with: +# asm-commons/src/test/java/org/objectweb/asm/commons/LambdaRemapTest.java:9: error: cannot find symbol +# import org.junit.jupiter.params.Parameter; +# ^ +# symbol: class Parameter + +JAVA_TEST_SRC_DIR=( asm{-analysis,-commons,,-test,-tree,-util}/src/test/java \ + ! -name LambdaRemapTest.java ) + +src_compile() { + local module + for module in "${ASM_MODULES[@]}"; do + einfo "Compiling ${module}" + JAVA_GENTOO_CLASSPATH_EXTRA+=":${module}.jar" + JAVA_INTERMEDIATE_JAR_NAME="org.objectweb.${module/-/.}" + JAVA_JAR_FILENAME="${module}.jar" + JAVA_MODULE_INFO_OUT="${module}/src/main" + JAVA_MODULE_INFO_RELEASE=9 + JAVA_SRC_DIR="${module}/src/main/java" + java-pkg-simple_src_compile + rm -r target || die + done + + # upstream generates module-info into root of the jars + for module in "${ASM_MODULES[@]}"; do + einfo "Adjust mudule-info in ${module}" + mkdir target || die + pushd target >/dev/null || die + jar xf ../${module}.jar || die + rm -v ../${module}.jar || die + mv {META-INF/versions/9/,}module-info.class || die + rm -rv META-INF || die + popd >/dev/null || die + jar cf ${module}.jar -C target . || die + rm -r target || die + done + + use doc && ejavadoc +} + +src_test() { + JAVAC_ARGS="-g" + JAVA_CLASSPATH_EXTRA="junit-5" + JAVA_GENTOO_CLASSPATH_EXTRA+=":asm-test.jar" + JAVA_JAR_FILENAME="asm-test.jar" + JAVA_RESOURCE_DIRS=( asm-test/src/main/resources ) + JAVA_SRC_DIR=( asm-test/src/main/java ) + java-pkg-simple_src_compile + junit5_src_test +} + +src_install() { + JAVA_JAR_FILENAME="asm.jar" + java-pkg-simple_src_install + local module + for module in asm-{analysis,commons,tree,util}; do + java-pkg_dojar ${module}.jar + if use source; then + java-pkg_dosrc "${module}/src/main/java/*" + fi + done +} diff --git a/dev-java/asm/asm-9.7.1-r1.ebuild b/dev-java/asm/asm-9.7.1-r1.ebuild index 67a1e694913e..9d17e57bcd8a 100644 --- a/dev-java/asm/asm-9.7.1-r1.ebuild +++ b/dev-java/asm/asm-9.7.1-r1.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -14,9 +14,6 @@ MAVEN_PROVIDES=" org.ow2.asm:asm-util:${PV} " -# Avoid circular dependency -JAVA_DISABLE_DEPEND_ON_JAVA_DEP_CHECK="true" - inherit java-pkg-2 java-pkg-simple DESCRIPTION="Bytecode manipulation framework for Java" diff --git a/dev-java/asm/files/asm-9.11.1-skipFailingTests.patch b/dev-java/asm/files/asm-9.11.1-skipFailingTests.patch new file mode 100644 index 000000000000..e488a19dd309 --- /dev/null +++ b/dev-java/asm/files/asm-9.11.1-skipFailingTests.patch @@ -0,0 +1,68 @@ + +Failures (2): + JUnit Jupiter:AnalyzerTest:testAnalyze_simpleNestedFinally() + MethodSource [className = 'org.objectweb.asm.tree.analysis.AnalyzerTest', methodName = 'testAnalyze_simpleNestedFinally', methodParameterTypes = ''] + => org.opentest4j.AssertionFailedError: Unexpected exception thrown: java.lang.LinkageError: Illegal class file encountered. Try running with -Xverify:all in method m + org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:152) + org.junit.jupiter.api.AssertDoesNotThrow.createAssertionFailedError(AssertDoesNotThrow.java:84) + org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:75) + org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:58) + org.junit.jupiter.api.Assertions.assertDoesNotThrow(Assertions.java:3228) + [...] + Caused by: java.lang.LinkageError: Illegal class file encountered. Try running with -Xverify:all in method m + java.base/java.lang.Class.getDeclaredConstructors0(Native Method) + java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:2988) + java.base/java.lang.Class.getDeclaredConstructors(Class.java:2357) + org.objectweb.asm.test.ClassFile.newInstance(ClassFile.java:200) + org.objectweb.asm.test.ClassFile.newInstance(ClassFile.java:170) + [...] + JUnit Jupiter:ClassWriterComputeMaxsTest:testVisitMaxs_simpleNestedFinally() + MethodSource [className = 'org.objectweb.asm.ClassWriterComputeMaxsTest', methodName = 'testVisitMaxs_simpleNestedFinally', methodParameterTypes = ''] + => org.opentest4j.AssertionFailedError: Unexpected exception thrown: java.lang.LinkageError: Illegal class file encountered. Try running with -Xverify:all in method m + org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:152) + org.junit.jupiter.api.AssertDoesNotThrow.createAssertionFailedError(AssertDoesNotThrow.java:84) + org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:75) + org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:58) + org.junit.jupiter.api.Assertions.assertDoesNotThrow(Assertions.java:3228) + [...] + Caused by: java.lang.LinkageError: Illegal class file encountered. Try running with -Xverify:all in method m + java.base/java.lang.Class.getDeclaredConstructors0(Native Method) + java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:2988) + java.base/java.lang.Class.getDeclaredConstructors(Class.java:2357) + org.objectweb.asm.test.ClassFile.newInstance(ClassFile.java:200) + org.objectweb.asm.test.ClassFile.newInstance(ClassFile.java:170) + [...] + +Failures happen with openjdk-27_p35 and openjdk-28_alpha18. +With openjdk:25 all tests pass. + +--- a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerTest.java ++++ b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerTest.java +@@ -513,7 +514,7 @@ class AnalyzerTest extends AsmTest { + * + * @throws AnalyzerException if the test fails + */ +- @Test ++ @Test @Disabled + void testAnalyze_simpleNestedFinally() throws AnalyzerException { + MethodNode methodNode = + new MethodNodeBuilder(5, 6) +--- a/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java ++++ b/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java +@@ -40,6 +40,7 @@ import java.util.Set; + import java.util.StringTokenizer; + import java.util.concurrent.atomic.AtomicReference; + import org.junit.jupiter.api.Test; ++import org.junit.jupiter.api.Disabled; + import org.junit.jupiter.params.ParameterizedTest; + import org.junit.jupiter.params.provider.ValueSource; + import org.objectweb.asm.test.ClassFile; +@@ -241,7 +242,7 @@ class ClassWriterComputeMaxsTest { + * } + * </pre> + */ +- @Test ++ @Test @Disabled + void testVisitMaxs_simpleNestedFinally() { + TestCaseBuilder testCase = + new TestCaseBuilder() diff --git a/dev-java/asm/metadata.xml b/dev-java/asm/metadata.xml index b489fb846277..ce9c2de80c67 100644 --- a/dev-java/asm/metadata.xml +++ b/dev-java/asm/metadata.xml @@ -25,7 +25,7 @@ </longdescription> <upstream> <doc>https://asm.ow2.io/documentation.html</doc> - <bugs-to>https://gitlab.ow2.org/asm/asm/-/issues</bugs-to> + <bugs-to>https://gitlab.ow2.org/asm/asm/-/work_items</bugs-to> </upstream> <origin>srcux</origin> </pkgmetadata> diff --git a/dev-java/batik/Manifest b/dev-java/batik/Manifest index 654726319316..a84a96a8c143 100644 --- a/dev-java/batik/Manifest +++ b/dev-java/batik/Manifest @@ -1,2 +1,4 @@ DIST batik-src-1.18.tar.gz 13699469 BLAKE2B 003a467b6948b902f56f004bf5c7d41f369bf49520d55fd70af3d3b8edef02d04557fdad5e4da70395a30c755f777791c89714edaa094eaa2964972eb81bdbf1 SHA512 209c6f4c76769cf1db616c5fe43c71d23c92afeb62e11bc7605baaa18bf9177efbb7a91a98bf03c3b4cd917bd7b7bd83e87083d16b11b3e57d3f31beeb8e1d7b DIST batik-src-1.18.tar.gz.asc 833 BLAKE2B a779b94a666edcab5b4c69866b4e7388ce6b8d9869d59773b084d870152cffd8c8cfd74df0f2a4ef06e5d598acb3b7bc00ec841871411b379f514b3fe2019765 SHA512 2fe6de9394f89c90e5ad2ede241c57bfbe5da4627b3730d445b2daa06b1ae71945bd65cda0d7a6bd46db536f318f755778a5b5a5b52080872a3143acd0d4677d +DIST batik-src-1.20.tar.gz 14157209 BLAKE2B 122e5deb6831472481f6d2681f7acf7167aece6d264f0fb76be0883a34f27e28cdc6764d29ff0ea0205eefb80201b115b8a69ec337d1da53b5a15632b38eef92 SHA512 b9387551551d2846a8b7039c80cacc4860487abd450218e300441a734bc836aab68abfb50b240f97d56d7317d1e98532e6bd60f6d95d77d790fb7e07eed01264 +DIST batik-src-1.20.tar.gz.asc 833 BLAKE2B 7e0ff9ed06838a37d553de90245e3f6df5b08955b098257f49815a8553bb4185f535127df4c8fc2b3f09dce4365b0ae8d4b73b633e6bfcd3294827711cb30f71 SHA512 824bddc0825cb9d2790c1f1ea43390e1d8d79410fe9ec4c1303a7fee114e5568ae1311ed428076c1b9aae9b9f0b857cfa40d9f4711a98e03b5026502c0d5d19d diff --git a/dev-java/batik/batik-1.18-r2.ebuild b/dev-java/batik/batik-1.18-r3.ebuild index f9ae5259b2f1..7441bef36394 100644 --- a/dev-java/batik/batik-1.18-r2.ebuild +++ b/dev-java/batik/batik-1.18-r3.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -29,7 +29,7 @@ CP_DEPEND=" >=dev-java/rhino-1.8.0:0 dev-java/xalan:0 dev-java/xml-commons-external:1.4 - dev-java/xmlgraphics-commons:0 + ~dev-java/xmlgraphics-commons-2.10:0 " DEPEND=" diff --git a/dev-java/batik/batik-1.20.ebuild b/dev-java/batik/batik-1.20.ebuild new file mode 100644 index 000000000000..19460d62ebc3 --- /dev/null +++ b/dev-java/batik/batik-1.20.ebuild @@ -0,0 +1,134 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +JAVA_PKG_IUSE="doc source test" +JAVA_TESTING_FRAMEWORKS="junit-4" + +inherit desktop java-pkg-2 java-pkg-simple verify-sig xdg + +DESCRIPTION="XML Graphics Batik" +HOMEPAGE="https://xmlgraphics.apache.org/batik/" +SRC_URI="mirror://apache/xmlgraphics/batik/source/batik-src-${PV}.tar.gz + verify-sig? ( mirror://apache/xmlgraphics/batik/source/batik-src-${PV}.tar.gz.asc )" +S="${WORKDIR}/${P}" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="~amd64 ~arm64 ~ppc64" + +BDEPEND="verify-sig? ( sec-keys/openpgp-keys-apache-xmlgraphics )" + +CP_DEPEND=" + >=dev-java/rhino-1.9.1:0 + dev-java/xml-commons-external:1.4 + ~dev-java/xmlgraphics-commons-2.12:0 +" + +DEPEND=" + ${CP_DEPEND} + >=virtual/jdk-11:* +" + +RDEPEND=" + ${CP_DEPEND} + >=virtual/jre-1.8:* +" + +DOCS=( CHANGES NOTICE README ) + +JAVA_RESOURCE_DIRS="resources" +JAVA_SRC_DIR=( ! -path '*test*/src/*' ! -path '*/jacl/*' -path '*/src/main/java/**.java' ) +JAVA_TEST_EXCLUDES=( + # JAXP00010003: The length of entity "%SVG.Presentation.attrib" is "15,002" + # that exceeds the "15,000" limit set by "jdk.xml.maxParameterEntitySizeLimit". + org.apache.batik.svg.ValidateDTDTestCase + # org.apache.batik.test.TestException: Cannot build Test instance of class : . Got exception of class : + # java.lang.IllegalArgumentException with messages test-references/samples/anne.png and stack trace : + # java.lang.IllegalArgumentException: test-references/samples/anne.png + org.apache.batik.test.xml.JUnitRunnerTestCase +) +JAVA_TEST_GENTOO_CLASSPATH="junit-4" +JAVA_TEST_RESOURCE_DIRS=( batik-util/src/test/resources test-resources ) +JAVA_TEST_SRC_DIR=( -path '*/src/test/java/**/*TestCase*.java' ) +VERIFY_SIG_OPENPGP_KEY_PATH="/usr/share/openpgp-keys/apache-xmlgraphics.asc" + +src_prepare() { + java-pkg_clean + java-pkg-2_src_prepare + # We drop support for jython due to bug #825486. + rm batik-script/src/main/java/org/apache/batik/script/jpython/JPythonInterpreter.java || die + rm batik-script/src/main/java/org/apache/batik/script/jpython/JPythonInterpreterFactory.java || die + + cat > "batik-squiggle.desktop" <<-EOF || die + [Desktop Entry] + Name=Squiggle + Comment=SVG browser + Exec=batik-squiggle + Icon=init + Terminal=false + Type=Application + Categories=Graphics;VectorGraphics; + MimeType=image/svg+xml + EOF + + mkdir resources || die "mkdir" + # collect resources from all those modules' directories + local module + for module in $(find ! -path '*test*/src/*' -path '*/src/main/resources'); do + pushd "${module}" >/dev/null || die + find -type f ! -name '*.java' ! -name 'LICENSE' -printf '%P\n' | + xargs cp --parent -t "${S}"/resources || die "copy resources" + popd >/dev/null || die + done + + mkdir -p src/test/resources || die "test-resources" + mv {batik-bridge/,}src/test/resources/circular.svg || die "circular.svg" +} + +src_test() { + einfo "building batik-test.jar" + JAVA_GENTOO_CLASSPATH_EXTRA="batik.jar:batik-test.jar" + JAVA_JAR_FILENAME="batik-test.jar" + JAVA_SRC_DIR=( -path '**/*test*/**/*.java' ) + java-pkg-simple_src_compile + + einfo "compiling and running tests" + java-pkg-simple_src_test + + # a mistake in java-pkg-simple.eclass suppresses this test-class + # but we can run it separately + JAVA_TEST_RUN_ONLY=( org.apache.batik.util.Base64TestCases ) + java-pkg-simple_src_test +} + +src_install() { + JAVA_JAR_FILENAME="batik.jar" # avoid installing 'batik-test.jar' + JAVA_SRC_DIR=( batik-* ) + java-pkg-simple_src_install + domenu "batik-squiggle.desktop" + newicon -s scalable \ + batik-svgbrowser/src/main/resources/org/apache/batik/apps/svgbrowser/resources/init.svg \ + squiggle.svg + + grep mainClass */pom.xml | + sed -n 's:.*<mainClass>\(.*\)</mainClass>:\1:p' | + sort | uniq | while read line + do + local launcher=$(echo "${line}" | cut -d. -f 5) || die + java-pkg_dolauncher "batik-${launcher}" --main "${line}" + done + + # java_policy_file is copied from previous version + local java_policy_file="${JAVA_PKG_SHAREPATH}/etc/${PN}.policy" + insinto "$(dirname "${java_policy_file}")" + newins - "$(basename "${java_policy_file}")" <<- _EOF_ + grant codeBase "file:${EPREFIX}${JAVA_PKG_JARDEST}/-" { + permission java.security.AllPermission; + }; + _EOF_ + java-pkg_register-environment-variable \ + gjl_java_args \ + "\$gjl_java_args -Djava.security.policy=file:${EPREFIX}${java_policy_file}" +} diff --git a/dev-java/byte-buddy/Manifest b/dev-java/byte-buddy/Manifest index 8f2b5f02b292..45bdfc0a3f99 100644 --- a/dev-java/byte-buddy/Manifest +++ b/dev-java/byte-buddy/Manifest @@ -1 +1,2 @@ DIST byte-buddy-1.18.10.tar.gz 2176379 BLAKE2B 7a406662e40d1081981cbc7ae9e461a5c67043c54442a6e9cf099c28bfc0817a44490f3d51f4412cb906633bacebfc7ae5acc3db56089979758808c80163bb4f SHA512 b1d17965a9a57eed847105d8cd350c5373a2eb2caf0ae0021cbaf556705c28741a7d796fcac6c561dc991bac248b2ff0408abeda0df68f0a9d72c222a517d336 +DIST byte-buddy-1.18.15.tar.gz 2215458 BLAKE2B 69caf08394f5d0b01dd20d50e2f9fb303d5f6b83005e6e20bc2c35879ccddc5c4af5b82f1bf1e781bcee0919a7ce23256a17943ca2193f8b507a86049fbc56b5 SHA512 951bb9fdb045549eb565bce24e586db8ad3a9ad82e5ca71dac2ad9ff9ecffffedd4cc4db87ac9167afa1fdcb906b308a1c1dbac3de1b73f6f94dad20a7a67e08 diff --git a/dev-java/byte-buddy/byte-buddy-1.18.15.ebuild b/dev-java/byte-buddy/byte-buddy-1.18.15.ebuild new file mode 100644 index 000000000000..18846c3abcff --- /dev/null +++ b/dev-java/byte-buddy/byte-buddy-1.18.15.ebuild @@ -0,0 +1,103 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=9 + +JAVA_PKG_IUSE="doc source test" +JAVA_TESTING_FRAMEWORKS="junit-4" + +inherit java-pkg-2 java-pkg-simple + +DESCRIPTION="Offers convenience for attaching an agent to the local or a remote VM" +HOMEPAGE="https://bytebuddy.net" +SRC_URI="https://github.com/raphw/byte-buddy/archive/${P}.tar.gz" +S="${WORKDIR}/byte-buddy-${P}" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="~amd64 ~arm64 ~ppc64" + +# Min java 11 because of module-info. +DEPEND=" + >=dev-java/asm-9.11:0 + >=dev-java/asm-jdk-bridge-0.0.13:0 + >=dev-java/findbugs-annotations-3.0.1:0 + >=dev-java/jna-5.19.1:0 + >=dev-java/jsr305-3.0.2-r1:0 + >=virtual/jdk-11:* + test? ( + >=dev-java/mockito-2.28.2-r1:2 + ) +" +RDEPEND=">=virtual/jre-1.8:*" + +JAVA_CLASSPATH_EXTRA="asm asm-jdk-bridge findbugs-annotations jna jsr305" +JAVADOC_CLASSPATH="${JAVA_CLASSPATH_EXTRA}" +JAVADOC_SRC_DIRS=( byte-buddy{,-agent}/src/main/java ) + +src_prepare() { + java-pkg_clean ! -path "./byte-buddy-dep/src/test/*" # Keep test-classes + java-pkg-2_src_prepare + + # instead of shading byte-buddy-dep we move it into byte-buddy. + mv byte-buddy{-dep,}/src/main/java || die "cannot move sources" +} + +src_compile() { + einfo "Compiling byte-buddy-agent.jar" + JAVA_INTERMEDIATE_JAR_NAME="net.bytebuddy.agent" + JAVA_JAR_FILENAME="byte-buddy-agent.jar" + JAVA_MODULE_INFO_OUT="byte-buddy-agent/src/main" + JAVA_RESOURCE_DIRS="byte-buddy-agent/src/main/resources" + JAVA_SRC_DIR="byte-buddy-agent/src/main/java" + java-pkg-simple_src_compile + JAVA_GENTOO_CLASSPATH_EXTRA+=":byte-buddy-agent.jar" + rm -r target || die + + einfo "Compiling byte-buddy.jar" + JAVA_INTERMEDIATE_JAR_NAME="net.bytebuddy" + JAVA_JAR_FILENAME="byte-buddy.jar" + JAVA_MODULE_INFO_OUT="byte-buddy/src/main" + JAVA_MAIN_CLASS="net.bytebuddy.build.Plugin\$Engine\$Default" + JAVA_RESOURCE_DIRS=() + JAVA_SRC_DIR="byte-buddy/src/main/java" + java-pkg-simple_src_compile + JAVA_GENTOO_CLASSPATH_EXTRA+=":byte-buddy.jar" + rm -r target || die + + use doc && ejavadoc +} + +src_test() { + # instead of shading byte-buddy-dep we move it into byte-buddy. + mv byte-buddy{-dep,}/src/test || die "cannot move tests" + + JAVAC_ARGS="-g" + JAVA_TEST_GENTOO_CLASSPATH="asm asm-jdk-bridge junit-4 mockito-2" + + einfo "Testing byte-buddy-agent" + # https://github.com/raphw/byte-buddy/issues/1321#issuecomment-1252776459 + JAVA_TEST_EXTRA_ARGS=( -Dnet.bytebuddy.test.jnapath="${EPREFIX}/usr/$(get_libdir)/jna/" ) + JAVA_TEST_SRC_DIR="byte-buddy-agent/src/test/java" + java-pkg-simple_src_test + + einfo "Testing byte-buddy" + JAVA_TEST_RESOURCE_DIRS=( byte-buddy/src/test/{resources,precompiled*} ) + JAVA_TEST_SRC_DIR="byte-buddy/src/test/java" + + JAVA_TEST_EXCLUDES=( + # all tests in this class fail, https://bugs.gentoo.org/863386 + net.bytebuddy.build.CachedReturnPluginTest + ) + java-pkg-simple_src_test +} + +src_install() { + java-pkg_dojar "byte-buddy-agent.jar" + java-pkg-simple_src_install + + if use source; then + java-pkg_dosrc "byte-buddy-agent/src/main/java/*" + java-pkg_dosrc "byte-buddy/src/main/java/*" + fi +} diff --git a/dev-java/fop/Manifest b/dev-java/fop/Manifest index 22c0a4d70524..befe2fd7820f 100644 --- a/dev-java/fop/Manifest +++ b/dev-java/fop/Manifest @@ -1,4 +1,6 @@ DIST fop-2.10-jars.tar.xz 7696 BLAKE2B aa5db0a4ee8b96e54e7beccccc21bd4559f2f228476ab65a12935fbbd69924e23296c1dfcf6e717b6031b9e21efd3aeed4df130ba5f8717ae47a1b0628013d59 SHA512 7c9d14f71e94e6a706c845d71a95adcbf0c77934618d9ce070dfd677adf8d2f171ab6e16954db684dc1781f91719e194382a629274435ecef3d3fbf0e5c7bae3 DIST fop-2.10-src.tar.gz 20903590 BLAKE2B 5217edbabc255b2e37c6198ea7cdfb1344db0726c7156f73bdc5ac452c4c233c5280d263e65656cde92ce6aa52c8ea94e346d9d14535ca802fc37fe1475ba255 SHA512 c5ae25be3e3a4da01195e108dec66743ab6017967220db1f4e4300f79594d8144de23876eeac07b2068db75a60e1b25d10ddad48533bd0cd6cb9e110b519fcf6 DIST fop-2.10-src.tar.gz.asc 833 BLAKE2B 70b200af6f3611b3dcd6da9d8728b12ee9a7bd4882919965c641e822cdd21510669cc98ace5d8caaa42e05347ae0d9b07384365377bbd4c5ca0b1edc07e2ff80 SHA512 ae9628cf9ca84d9c2ac1bfddcb4aec786985d3fdca16c2541e45690c81695ec5361dc1641a30572fc2d95defa69cab61a1992ab0d16843344411acb77835a73c +DIST fop-2.12-src.tar.gz 20980392 BLAKE2B 6d635fe7a17a2926ad2f099fcf3c81d13e4a4ec1f9709a70b6736889f7a8c798d8ab9696011f491a3ccee4b5474a89e1325a293df372a9619385051bed57c8e3 SHA512 d4c7b27d9c6f5856b7766126dca021e22c23b8d0ba2fea24261be5544df0736309f5ad6866b8e97f8941eb23327315deca7d46205757287efc2ed5e0ce8e9a1d +DIST fop-2.12-src.tar.gz.asc 833 BLAKE2B a64e4df0fa1281c9b1782afa9eb32d927cabb4d98e4ace424b3f416ae1df5baeee3115bd40e758b7a48b9a76541a40cf7324b0290311c73fa9133a5909e900ee SHA512 96aa127916930b79a2874b3da9e5b62ef197283a08f781a501e6c5ead97e45f20409c559f3f9452413ddb52f1886a32786bc57b46391af013105c88816b9b5dd DIST fop-hyph-2.0.jar 6655933 BLAKE2B bfbefcc2ebd9a9a467c23a70d7cef256f83cf2335f6e17db6bbefceaf8a3cee78de0302f269d096eb3238b3f4883429e09cc640bed11a42da4ef67ab9f0cb164 SHA512 3d5f27623008c7beba5332387bd42c501b7ae1dddfc98014b3fb35273394ca5f0a4ce69e7726090ec468c9b4f7983fb8b1a5759f49e81ea342c81c61311d87b3 diff --git a/dev-java/fop/files/fop-2.11-PDFDocumentInformationElementTestCase.patch b/dev-java/fop/files/fop-2.11-PDFDocumentInformationElementTestCase.patch new file mode 100644 index 000000000000..e6f24ec652c4 --- /dev/null +++ b/dev-java/fop/files/fop-2.11-PDFDocumentInformationElementTestCase.patch @@ -0,0 +1,30 @@ +3) standardKeyword(org.apache.fop.render.pdf.extensions.PDFDocumentInformationElementTestCase) +java.lang.AssertionError: Did not receive expected event: org.apache.fop.render.pdf.extensions.PDFExtensionEventProducer.reservedKeyword + at org.junit.Assert.fail(Assert.java:89) + at org.apache.fop.events.EventChecker.end(EventChecker.java:56) + at org.apache.fop.render.pdf.extensions.PDFDocumentInformationElementTestCase.runTest(PDFDocumentInformationElementTestCase.java:69) + at org.apache.fop.render.pdf.extensions.PDFDocumentInformationElementTestCase.standardKeyword(PDFDocumentInformationElementTestCase.java:59) + +Tests were run with +openjdk-26_beta34 +openjdk-25.0.2_p10 + +--- a/fop-core/src/test/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElementTestCase.java ++++ b/fop-core/src/test/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElementTestCase.java +@@ -24,6 +24,7 @@ import java.util.HashMap; + import java.util.Map; + + import org.junit.Test; ++import org.junit.Ignore; + + import org.apache.xmlgraphics.util.QName; + +@@ -52,7 +53,7 @@ public class PDFDocumentInformationElementTestCase { + runTest("invalid-child.fo", FOValidationEventProducer.class.getName() + ".invalidChild", expectedParams); + } + +- @Test ++ @Test @Ignore + public void standardKeyword() throws Exception { + Map<String, Object> expectedParams = new HashMap<String, Object>(); + expectedParams.put("keyword", "Creator"); diff --git a/dev-java/fop/files/fop-2.11-PDFLinearizationTestCase.patch b/dev-java/fop/files/fop-2.11-PDFLinearizationTestCase.patch new file mode 100644 index 000000000000..16dc0ddee615 --- /dev/null +++ b/dev-java/fop/files/fop-2.11-PDFLinearizationTestCase.patch @@ -0,0 +1,40 @@ +1) testAccessibility(org.apache.fop.pdf.PDFLinearizationTestCase) +javax.xml.transform.TransformerException: java.util.MissingResourceException: Error reading event-model.xml: org.xml.sax.SAXException: FWK005 parse may not be called while parsing. + at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:790) + at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:395) + at org.apache.fop.pdf.PDFLinearizationTestCase.testAccessibility(PDFLinearizationTestCase.java:106) + ... 31 trimmed +Caused by: java.util.MissingResourceException: Error reading event-model.xml: org.xml.sax.SAXException: FWK005 parse may not be called while parsing. + at org.apache.fop.events.DefaultEventBroadcaster.loadModel(DefaultEventBroadcaster.java:91) + at org.apache.fop.events.DefaultEventBroadcaster.getEventProducerModel(DefaultEventBroadcaster.java:113) + at org.apache.fop.events.DefaultEventBroadcaster.createProxyFor(DefaultEventBroadcaster.java:141) + at org.apache.fop.events.DefaultEventBroadcaster.getEventProducerFor(DefaultEventBroadcaster.java:128) + at org.apache.fop.render.pdf.PDFEventProducer$Provider.get(PDFEventProducer.java:45) + at org.apache.fop.render.pdf.PDFPainter$LanguageAvailabilityChecker.checkLanguageAvailability(PDFPainter.java:105) + at org.apache.fop.render.pdf.PDFPainter$LanguageAvailabilityChecker.access$000(PDFPainter.java:90) + at org.apache.fop.render.pdf.PDFPainter.drawText(PDFPainter.java:429) + at org.apache.fop.render.pdf.PDFPainter.drawText(PDFPainter.java:464) + +Tests were run with +openjdk-26_beta34 +openjdk-25.0.2_p10 + +--- a/fop-core/src/test/java/org/apache/fop/pdf/PDFLinearizationTestCase.java ++++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFLinearizationTestCase.java +@@ -41,6 +41,7 @@ import javax.xml.transform.stream.StreamSource; + + import org.junit.Assert; + import org.junit.Test; ++import org.junit.Ignore; + + import org.apache.fop.apps.FOUserAgent; + import org.apache.fop.apps.Fop; +@@ -77,7 +78,7 @@ public class PDFLinearizationTestCase { + checkPDF(data, 5, 6); + } + +- @Test ++ @Test @Ignore + public void testAccessibility() throws Exception { + String fopxconf = "<fop version=\"1.0\">" + + "<accessibility>true</accessibility>" diff --git a/dev-java/fop/files/fop-2.11-PDFStructureTreeTestCase.patch b/dev-java/fop/files/fop-2.11-PDFStructureTreeTestCase.patch new file mode 100644 index 000000000000..00fe37671351 --- /dev/null +++ b/dev-java/fop/files/fop-2.11-PDFStructureTreeTestCase.patch @@ -0,0 +1,31 @@ +2) testRemoveUnusedStructs(org.apache.fop.pdf.PDFStructureTreeTestCase) +java.lang.AssertionError: expected:<1> but was:<2> + at org.junit.Assert.fail(Assert.java:89) + at org.junit.Assert.failNotEquals(Assert.java:835) + at org.junit.Assert.assertEquals(Assert.java:647) + at org.junit.Assert.assertEquals(Assert.java:633) + at org.apache.fop.pdf.PDFStructureTreeTestCase.testRemoveUnusedStructs(PDFStructureTreeTestCase.java:77) + +Tests were run with +openjdk-26_beta34 +openjdk-25.0.2_p10 + +--- a/fop-core/src/test/java/org/apache/fop/pdf/PDFStructureTreeTestCase.java ++++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFStructureTreeTestCase.java +@@ -33,6 +33,7 @@ import javax.xml.transform.stream.StreamSource; + + import org.junit.Assert; + import org.junit.Test; ++import org.junit.Ignore; + import org.xml.sax.SAXException; + + import org.apache.fop.apps.FOUserAgent; +@@ -41,7 +42,7 @@ import org.apache.fop.apps.FopFactory; + import org.apache.fop.apps.MimeConstants; + + public class PDFStructureTreeTestCase { +- @Test ++ @Test @Ignore + public void testRemoveUnusedStructs() throws Exception { + String fo = "<fo:root xmlns:fo=\"http://www.w3.org/1999/XSL/Format\">\n" + + " <fo:layout-master-set>\n" diff --git a/dev-java/fop/files/fop-2.12-IFRendererTestCase.patch b/dev-java/fop/files/fop-2.12-IFRendererTestCase.patch new file mode 100644 index 000000000000..9ff589d1805c --- /dev/null +++ b/dev-java/fop/files/fop-2.12-IFRendererTestCase.patch @@ -0,0 +1,29 @@ +There was 1 failure: +1) testSoftHyphen(org.apache.fop.render.intermediate.IFRendererTestCase) +java.lang.AssertionError: PDF files are able to handle the soft hyphen, so must not replace with normal hyphen + at org.junit.Assert.fail(Assert.java:89) + at org.junit.Assert.assertTrue(Assert.java:42) + at org.apache.fop.render.intermediate.IFRendererTestCase.testSoftHyphen(IFRendererTestCase.java:93) + +FAILURES!!! +Tests run: 2, Failures: 1 + +--- a/fop-core/src/test/java/org/apache/fop/render/intermediate/IFRendererTestCase.java ++++ b/fop-core/src/test/java/org/apache/fop/render/intermediate/IFRendererTestCase.java +@@ -34,6 +34,7 @@ import javax.xml.transform.sax.SAXResult; + import javax.xml.transform.stream.StreamSource; + + import org.junit.Test; ++import org.junit.Ignore; + import static org.junit.Assert.assertFalse; + import static org.junit.Assert.assertTrue; + +@@ -86,7 +87,7 @@ public class IFRendererTestCase { + assertFalse(wordAreas.get(1).isNextIsSpace()); + } + +- @Test ++ @Test @Ignore + public void testSoftHyphen() throws FOPException, TransformerException { + String fo = getSoftHyphenTestFO("­"); + foToOutput(new ByteArrayInputStream(fo.getBytes())); diff --git a/dev-java/fop/files/fop-2.12-PDFPainterTestCase.patch b/dev-java/fop/files/fop-2.12-PDFPainterTestCase.patch new file mode 100644 index 000000000000..b4d407bead3b --- /dev/null +++ b/dev-java/fop/files/fop-2.12-PDFPainterTestCase.patch @@ -0,0 +1,28 @@ +There was 1 failure: +1) testSoftHyphenNotReplaced(org.apache.fop.render.pdf.PDFPainterTestCase) +org.junit.ComparisonFailure: Soft hyphen must not be replaced by a normal hyphen expected:<[]> but was:<[#]> + at org.junit.Assert.assertEquals(Assert.java:117) + at org.apache.fop.render.pdf.PDFPainterTestCase.testSoftHyphenNotReplaced(PDFPainterTestCase.java:386) + +FAILURES!!! +Tests run: 25, Failures: 1 + +--- a/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java ++++ b/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java +@@ -39,6 +39,7 @@ import javax.xml.transform.stream.StreamResult; + + import org.junit.Assert; + import org.junit.Test; ++import org.junit.Ignore; + import org.mockito.invocation.InvocationOnMock; + import org.mockito.stubbing.Answer; + import static org.junit.Assert.assertEquals; +@@ -352,7 +353,7 @@ public class PDFPainterTestCase { + verify(pdfContentGenerator).add("2 Tr 0.63086 w\n"); + } + +- @Test ++ @Test @Ignore + public void testSoftHyphenNotReplaced() throws IFException { + pdfDocumentHandler = makePDFDocumentHandler(new StringBuilder()); + diff --git a/dev-java/fop/fop-2.10-r2.ebuild b/dev-java/fop/fop-2.10-r3.ebuild index 6036e9128063..0235457eaf8e 100644 --- a/dev-java/fop/fop-2.10-r2.ebuild +++ b/dev-java/fop/fop-2.10-r3.ebuild @@ -32,7 +32,7 @@ BDEPEND=" CP_DEPEND=" >=dev-java/ant-1.10.14-r3:0 - dev-java/batik:0 + ~dev-java/batik-1.18:0 dev-java/bcprov:0 dev-java/bcpkix:0 dev-java/commons-io:1 @@ -41,7 +41,7 @@ CP_DEPEND=" dev-java/jakarta-servlet-api:6.1 dev-java/qdox:1.12 dev-java/sun-jai-bin:0 - dev-java/xmlgraphics-commons:0 + ~dev-java/xmlgraphics-commons-${PV}:0 " DEPEND=" diff --git a/dev-java/fop/fop-2.12.ebuild b/dev-java/fop/fop-2.12.ebuild new file mode 100644 index 000000000000..6cf56865d455 --- /dev/null +++ b/dev-java/fop/fop-2.12.ebuild @@ -0,0 +1,236 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=9 + +JAVA_PKG_IUSE="doc source test" +JAVA_TESTING_FRAMEWORKS="junit-4" + +inherit java-pkg-2 java-pkg-simple verify-sig + +DESCRIPTION="XML Graphics Format Object Processor All-In-One" +HOMEPAGE="https://xmlgraphics.apache.org/fop/" +SRC_URI=" + mirror://apache/xmlgraphics/fop/source/${P}-src.tar.gz + verify-sig? ( mirror://apache/xmlgraphics/fop/source/${P}-src.tar.gz.asc ) + test? ( https://repo1.maven.org/maven2/net/sf/offo/fop-hyph/2.0/fop-hyph-2.0.jar ) +" +S="${WORKDIR}/${P}" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="~amd64 ~arm64 ~ppc64" + +BDEPEND=" + >=dev-java/xalan-2.7.3-r3:0 + verify-sig? ( sec-keys/openpgp-keys-apache-xmlgraphics ) +" + +CP_DEPEND=" + >=dev-java/ant-1.10.15-r1:0 + ~dev-java/batik-1.20:0 + >=dev-java/bcpkix-1.84:0 + >=dev-java/bcprov-1.84:0 + >=dev-java/bcutil-1.84:0 + dev-java/commons-io:1 + >=dev-java/commons-logging-1.3.6:0 + >=dev-java/fontbox-3.0.6:0 + dev-java/jakarta-servlet-api:6.1 + dev-java/qdox:1.12 + dev-java/sun-jai-bin:0 + dev-java/xml-commons-external:1.4 + ~dev-java/xmlgraphics-commons-2.12:0 +" + +# Min jdk-11 in order to avoid test failures like: +# java.lang.NoSuchMethodError: java.nio.ByteBuffer.position(I)Ljava/nio/ByteBuffer; +DEPEND=" + ${CP_DEPEND} + >=virtual/jdk-11:* + test? ( + dev-java/mockito:2 + >=dev-java/pdfbox-3.0.6:0 + dev-java/xmlunit:1 + ) +" + +RDEPEND=" + ${CP_DEPEND} + >=virtual/jre-1.8:* +" + +DOCS=( NOTICE README ) +PATCHES=( + "${FILESDIR}/fop-2.9-PDFEncodingTestCase.patch" + "${FILESDIR}/fop-2.11-PDFDocumentInformationElementTestCase.patch" + "${FILESDIR}/fop-2.11-PDFLinearizationTestCase.patch" + "${FILESDIR}/fop-2.11-PDFStructureTreeTestCase.patch" + "${FILESDIR}/fop-2.12-IFRendererTestCase.patch" + "${FILESDIR}/fop-2.12-PDFPainterTestCase.patch" +) + +JAVADOC_SRC_DIRS=( fop-{util,events,core}/src/main/java ) +VERIFY_SIG_OPENPGP_KEY_PATH="/usr/share/openpgp-keys/apache-xmlgraphics.asc" + +src_unpack() { + if use verify-sig; then + verify-sig_verify_detached "${DISTDIR}"/${P}-src.tar.gz{,.asc} + fi + unpack ${P}-src.tar.gz +} + +src_prepare() { + java-pkg_clean + java-pkg-2_src_prepare + + einfo "prepare fop-events/build-gentoo.xml" + # ant-task to create "event-model.xml" file directly into fop-events/src/test/resources + local extract_events="$(sed -n '/taskdef/,/\/collectEvents/p' fop-events/pom.xml | + sed -e 's:${project.basedir}\/::' \ + -e 's:${project.build.testOutputDirectory}:src/test/resources:' \ + -e '/maven.test.classpath/d' | + cut -d' ' -f 15-25 ; pipestatus || die)" + # The first three lines are manually copied headers needed for a build.xml + # but are not given in the pom.xml. + # The 'path' element, also not from the pom.xml, creates the classpath + # needed to run this ant-task using eant(). + cat <<- FOP-EVENTS > fop-events/build-gentoo.xml || die "FOP-EVENTS" + <?xml version="1.0" encoding="UTF-8" ?> + <project default="main"> + <target name="main"> + <path id="maven.compile.classpath"> + <fileset dir="../"> + <include name="commons-io.jar"/> + <include name="fop-events.jar"/> + <include name="qdox.jar"/> + <include name="xmlgraphics-commons.jar"/> + </fileset> + </path> + ${extract_events} + </target> + </project> + FOP-EVENTS + + einfo "prepare fop-core/build-gentoo.xml" + # ant-task to create "event-model.xml" files directly into target/classes + local extract_core="$(sed -n '/taskdef/,/\/collectEvents/p' fop-core/pom.xml | + sed -e 's:${project.basedir}\/::' \ + -e 's:${project.build.outputDirectory}:../target/classes:' \ + -e '/maven.test.classpath/d' | + cut -d' ' -f 15-25 ; pipestatus || die)" + # The first three lines are manually copied headers needed for a build.xml + # but are not given in the pom.xml. + # The 'path' element, also not from the pom.xml, creates the classpath + # needed to run this ant-task using eant(). + cat <<- FOP-CORE > fop-core/build-gentoo.xml || die "FOP-CORE" + <?xml version="1.0" encoding="UTF-8" ?> + <project default="main"> + <target name="main"> + <path id="maven.compile.classpath"> + <fileset dir="../"> + <include name="commons-io.jar"/> + <include name="commons-logging.jar"/> + <include name="fop-core.jar"/> + <include name="fop-events.jar"/> + <include name="qdox.jar"/> + <include name="xmlgraphics-commons.jar"/> + </fileset> + </path> + ${extract_core} + </target> + </project> + FOP-CORE + + einfo "generate Java code according to fop-core/pom.xml lines 172-243" + local fonts=$(find fop-core/src/main/codegen/fonts \( -name "Courier*.xml" \ + -o -name "Helvetica*.xml" \ + -o -name "Times*.xml" \ + -o -name "Symbol.xml" \ + -o -name "ZapfDingbats.xml" \) -printf '%P\n' + ) + cd fop-core/src/main/codegen/fonts || die "change dir" + for font in ${fonts}; do + xalan -XSLTC \ + -IN $font \ + -XSL font-file.xsl \ + -OUT ../../java/org/apache/fop/fonts/base14/${font//.xml}.java || die + done + + xalan -XSLTC \ + -IN encodings.xml \ + -XSL code-point-mapping.xsl \ + -OUT ../../java/org/apache/fop/fonts/base14/CodePointMapping.java || die +} + +src_compile() { + # while ant could install multiple jar files we only need ant.jar + JAVA_GENTOO_CLASSPATH_EXTRA=":$(java-pkg_getjar --build-only ant ant.jar)" + + JAVA_AUTOMATIC_MODULE_NAME="org.apache.xmlgraphics.fop.util" + JAVA_JAR_FILENAME="fop-util.jar" + JAVA_SRC_DIR="fop-util/src/main/java" + java-pkg-simple_src_compile + JAVA_GENTOO_CLASSPATH_EXTRA+=":fop-util.jar" + rm -r target || die + + JAVA_AUTOMATIC_MODULE_NAME="org.apache.xmlgraphics.fop.events" + JAVA_JAR_FILENAME="fop-events.jar" + JAVA_RESOURCE_DIRS="fop-events/src/main/resources" + JAVA_SRC_DIR="fop-events/src/main/java" + java-pkg-simple_src_compile + JAVA_GENTOO_CLASSPATH_EXTRA+=":fop-events.jar" + rm -r target || die + + JAVA_AUTOMATIC_MODULE_NAME="org.apache.xmlgraphics.fop.core" + JAVA_JAR_FILENAME="fop-core.jar" + JAVA_MAIN_CLASS=$( sed -n 's:.*<mainClass>\(.*\)</mainClass>:\1:p' fop-core/pom.xml ) + JAVA_RESOURCE_DIRS="fop-core/src/main/resources" + JAVA_SRC_DIR="fop-core/src/main/java" + + java-pkg-simple_src_compile + + java-pkg_jar-from commons-io-1,commons-logging,qdox-1.12,xmlgraphics-commons + eant -f fop-core/build-gentoo.xml # this creates the *event-model.xml files + jar cf fop-core.jar -C target/classes . || die "add event-model.xml files" + + JAVADOC_CLASSPATH="${JAVA_GENTOO_CLASSPATH}" + use doc && ejavadoc +} + +src_test() { + JAVA_TEST_GENTOO_CLASSPATH="junit-4,mockito-2,pdfbox,xmlunit-1" + + einfo "Testing fop-events" + JAVA_TEST_EXCLUDES="org.apache.fop.events.TestEventProducer" + JAVA_TEST_RESOURCE_DIRS=( fop-events/src/test/resources ) + JAVA_TEST_SRC_DIR=( fop-events/src/test/java ) + mkdir fop-events/src/test/resources || die "mkdir events" + eant -f fop-events/build-gentoo.xml # this creates the *event-model.xml file + java-pkg-simple_src_test + + einfo "Building the core-test.jar" + cd fop || die "cd fop" # fop-core/pom.xml line 322 + JAVA_GENTOO_CLASSPATH_EXTRA="${DISTDIR}/fop-hyph-2.0.jar" + JAVA_GENTOO_CLASSPATH_EXTRA+=":../fop-util.jar" + JAVA_GENTOO_CLASSPATH_EXTRA+=":../fop-events.jar" + JAVA_GENTOO_CLASSPATH_EXTRA+=":../fop-core.jar" + JAVA_JAR_FILENAME="core-test.jar" + JAVA_RESOURCE_DIRS=( ../fop-core/src/test/resources ) + JAVA_SRC_DIR=( ../fop-core -path */src/test/java/**.java ) + java-pkg-simple_src_compile + JAVA_GENTOO_CLASSPATH_EXTRA+=":core-test.jar" + + einfo "Testing fop-core" + JAVA_TEST_RESOURCE_DIRS=( ../fop-core/src/test/resources ) + # **TestCase.java according to fop-core/pom.xml line 319 + JAVA_TEST_SRC_DIR=( ../fop-core -path */src/test/java/**TestCase.java ) + java-pkg-simple_src_test +} + +src_install() { + java-pkg_dojar fop-{util,events}.jar # omit fop-core.jar + JAVA_JAR_FILENAME="fop-core.jar" # it will be installed by the eclass + JAVA_SRC_DIR=( fop-{util,events,core}/src/main/java ) # for 'use source' + java-pkg-simple_src_install + java-pkg_dolauncher "fop" --main "org.apache.fop.cli.Main" +} diff --git a/dev-java/openjdk-bin/openjdk-bin-28_alpha20.ebuild b/dev-java/openjdk-bin/openjdk-bin-28_alpha20.ebuild index eff740190099..cac869e6ec8d 100644 --- a/dev-java/openjdk-bin/openjdk-bin-28_alpha20.ebuild +++ b/dev-java/openjdk-bin/openjdk-bin-28_alpha20.ebuild @@ -71,7 +71,7 @@ S="${WORKDIR}/jdk-${MY_PV}" LICENSE="GPL-2-with-classpath-exception" SLOT=$(ver_cut 1) -# KEYWORDS="" # Not an LTS candidate +KEYWORDS="~amd64" # Not an LTS candidate IUSE="alsa cups headless-awt selinux source static-libs" RDEPEND=" diff --git a/dev-java/openjdk/openjdk-28_alpha20.ebuild b/dev-java/openjdk/openjdk-28_alpha20.ebuild index 05d1ee1ab663..1b8c48c55b17 100644 --- a/dev-java/openjdk/openjdk-28_alpha20.ebuild +++ b/dev-java/openjdk/openjdk-28_alpha20.ebuild @@ -68,7 +68,7 @@ S="${WORKDIR}/${JDK_REPO}-jdk-${MY_PV//+/-}" LICENSE="GPL-2-with-classpath-exception" SLOT="$(ver_cut 1)" -# KEYWORDS="" # Not an LTS candidate +KEYWORDS="~amd64" # Not an LTS candidate IUSE="alsa big-endian cups debug doc examples headless-awt +jbootstrap selinux source static-libs +system-bootstrap systemtap" diff --git a/dev-java/xmlgraphics-commons/Manifest b/dev-java/xmlgraphics-commons/Manifest index 0e4e361500e7..aee415a44092 100644 --- a/dev-java/xmlgraphics-commons/Manifest +++ b/dev-java/xmlgraphics-commons/Manifest @@ -1,2 +1,4 @@ DIST xmlgraphics-commons-2.10-src.tar.gz 1618901 BLAKE2B 4ce0bda4715d8d9326f2c42f4d2f0eac65ea59dd527c42927f1a0096526cd54e90e0d71087060a05bf9819f4bac17b14eee08dfce41b950edd26c7a4dcc9669a SHA512 49b055891e6b76f3c357b616c34f152033ccf807432d55dd57ac83c41234c224d1a716f77ae7ef24ff49d9f376dfa6b161cde18b258d94438a467a4ad91be21d DIST xmlgraphics-commons-2.10-src.tar.gz.asc 833 BLAKE2B 15a9aa963695863bf27624f3f1afc6f3b1277bc58a01a103f76a4c25060e31f38e3e155bf240c816839cc216834ee0d4e639c3b60a91de9a4dde39c29c54af12 SHA512 5f44396867501ab2ab58c1da8e6212d873457eb6d4de545058f84530f05f0bfddf319b2c4bb5a711bf48cca402d7c68e5efae3a0ada71742a706f3b14290dec3 +DIST xmlgraphics-commons-2.12-src.tar.gz 2102951 BLAKE2B 86add6a79b4d9a7357d2c6f43e27a6dd57a8e44738510576613a32f97bff3a7f42e0cb8a2d13d41bbcb7f0774d958c4792205bb0d7ed77412e9c3f3ebf8c3fb3 SHA512 46c9171ee6be8776bb7bee0ceb0684e811557b6b3be82f4a39b0389f554d0978bc6e73f532e85ba35ae1e81a44f487f079f8aed858244f1105b94f4fb8aa9401 +DIST xmlgraphics-commons-2.12-src.tar.gz.asc 833 BLAKE2B 96e903a6377a74bf80abdeef60f64d45ede1b5d028e9f2f152ea0a7ebe6af103ea4180f19b0720b46f7c861714dd7e7f9bfbab59b68aecff2a0c70a962cf3e28 SHA512 dcf0f92a2efbfe292acf17070a8aaf38e1e1b48c9cb850453c038be71cb110421d7341970724e0a950d0ec61b3413b26be30b2fbef949a214e77a39bab7539cd diff --git a/dev-java/xmlgraphics-commons/xmlgraphics-commons-2.12.ebuild b/dev-java/xmlgraphics-commons/xmlgraphics-commons-2.12.ebuild new file mode 100644 index 000000000000..fa3013afdb04 --- /dev/null +++ b/dev-java/xmlgraphics-commons/xmlgraphics-commons-2.12.ebuild @@ -0,0 +1,57 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=9 + +JAVA_PKG_IUSE="doc source test" +JAVA_TESTING_FRAMEWORKS="junit-4" + +inherit java-pkg-2 java-pkg-simple verify-sig + +DESCRIPTION="XML Graphics Commons" +HOMEPAGE="https://xmlgraphics.apache.org/commons/" +SRC_URI="mirror://apache/xmlgraphics/commons/source/${P}-src.tar.gz + verify-sig? ( mirror://apache/xmlgraphics/commons/source/${P}-src.tar.gz.asc )" +S="${WORKDIR}/${P}" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="~amd64 ~arm64 ~ppc64" + +BDEPEND="verify-sig? ( sec-keys/openpgp-keys-apache-xmlgraphics )" + +# Using older commons-io because of compilation error with newer versions: +# src/main/java/org/apache/xmlgraphics/image/loader/impl/ImageLoaderRawJPEG.java:231: +# error: exception IOException is never thrown in body of corresponding try statement +# } catch (IOException ioe) { +# ^ +CP_DEPEND=" + dev-java/commons-io:1 + >=dev-java/commons-logging-1.3.6:0 +" + +DEPEND=" + ${CP_DEPEND} + >=virtual/jdk-1.8:* + test? ( + >=dev-java/mockito-4.11.0:4 + dev-java/xml-commons-resolver:0 + ) +" + +RDEPEND=" + ${CP_DEPEND} + >=virtual/jre-1.8:* +" + +DOCS=( NOTICE README ) + +JAVA_AUTOMATIC_MODULE_NAME="org.apache.xmlgraphics.commons" +JAVA_RESOURCE_DIRS="src/main/resources" +JAVA_SRC_DIR="src/main/java" +JAVA_TEST_EXTRA_ARGS=( -ea ) +JAVA_TEST_GENTOO_CLASSPATH="junit-4,mockito-4,xml-commons-resolver" +# Some *.{png,tiff,txt,xmp} are misplaced in "src/test/java". +JAVA_TEST_RESOURCE_DIRS=( "src/test/resources" "src/test/java" ) +JAVA_TEST_SRC_DIR="src/test/java" +VERIFY_SIG_OPENPGP_KEY_PATH="/usr/share/openpgp-keys/apache-xmlgraphics.asc" diff --git a/dev-lang/algol68g/Manifest b/dev-lang/algol68g/Manifest index de6b9bdedffe..28e90e84db39 100644 --- a/dev-lang/algol68g/Manifest +++ b/dev-lang/algol68g/Manifest @@ -1 +1,2 @@ DIST algol68g-3.13.3.tar.gz 694386 BLAKE2B 3e3e31e02729b83838922655d3a4379d730d01912fd5608e375704f4d5152e17eb906cf938ee58b5e51e92494110865494217d4fe5cbefb51f7ec4bc5f17b22b SHA512 90b64911ee3b4011799425cf846fea4a26570182ec1ed9f65e6f0630e02b0623ed408fb6d074dcd2795b6412e209a39f5580d32acd079a3f3d419bf2440512a6 +DIST algol68g-3.13.4.tar.gz 706226 BLAKE2B 42401b17e685274804741441541834e9035e4dedd41df5d09343963e6ba0a693bb2b2f574f2a008a20ea4d9c6126099d162ff7af0da5524eb9a96edf9adfe54a SHA512 a8e63ac3974bf024299651dece72e47d16250d9a9684e5758c249429c0fb28cf5c2f9ed92b1ad08710f4f6ef1cac35b3baf4bb56fdc238a5135c88f4ebc32acc diff --git a/dev-lang/algol68g/algol68g-3.13.4.ebuild b/dev-lang/algol68g/algol68g-3.13.4.ebuild new file mode 100644 index 000000000000..13d9ffd70a25 --- /dev/null +++ b/dev-lang/algol68g/algol68g-3.13.4.ebuild @@ -0,0 +1,41 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=9 + +DESCRIPTION="Algol 68 Genie compiler-interpreter" +HOMEPAGE="https://algol68genie.nl/en/algol-68-genie/" +SRC_URI="https://algol68genie.nl/${P}.tar.gz" + +LICENSE="GPL-3+" +SLOT="0" +KEYWORDS="~amd64 ~x86" +IUSE="+curl +gsl +mpfr +ncurses plotutils postgres +readline" + +RDEPEND=" + curl? ( net-misc/curl ) + gsl? ( sci-libs/gsl:= ) + mpfr? ( dev-libs/mpfr:= ) + plotutils? ( media-libs/plotutils ) + postgres? ( dev-db/postgresql:* ) + readline? ( sys-libs/readline:= ) +" +DEPEND=" + ${RDEPEND} +" + +PATCHES=( "${FILESDIR}/${PN}-3.3.21-configure-implicit.patch" ) + +src_configure() { + local -a myconf=( + $(use_enable curl) + $(use_enable gsl) + $(use_enable mpfr) + $(use_enable ncurses curses) + $(use_enable plotutils) + $(use_enable postgres postgresql) + $(use_enable readline) + $(use_with ncurses) + ) + econf "${myconf[@]}" +} diff --git a/dev-lang/python/python-3.16.0_alpha1.ebuild b/dev-lang/python/python-3.16.0_alpha1.ebuild index d9b57bb0b7a7..f73786369199 100644 --- a/dev-lang/python/python-3.16.0_alpha1.ebuild +++ b/dev-lang/python/python-3.16.0_alpha1.ebuild @@ -33,7 +33,7 @@ S="${WORKDIR}/${MY_P}" LICENSE="PSF-2" SLOT="${PYVER}" -if [[ ${PV} != *_rc* ]]; then +if [[ ${PV} != *_@(alpha|beta|rc)* ]]; then KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" fi IUSE=" diff --git a/dev-lang/spidermonkey/Manifest b/dev-lang/spidermonkey/Manifest index 91ee99ed0ac1..c5e012b6a8d0 100644 --- a/dev-lang/spidermonkey/Manifest +++ b/dev-lang/spidermonkey/Manifest @@ -7,6 +7,7 @@ DIST firefox-140esr-patches-10.tar.xz 15052 BLAKE2B 1e9f1d401590958422f91d5df98d DIST firefox-140esr-patches-13.tar.xz 15628 BLAKE2B 824c3d42f2f6cf70932fe395cf11dfe9043e09e20d6dfd2bd64ce4567206def45424e82b738f27c71648f0b2d97608746d22565cf7aa35713dedd71a24cde449 SHA512 d4d59818944f6a8213598308a1095a0f3c9818e6c517587e4630056d432a0e9673ad627447ca18dfa884dbb35bc2921e54cfc63f9c5646765e0f66a75279584d DIST firefox-153.3.0esr.source.tar.xz 804997520 BLAKE2B a01a4e3efec1c81ca462b915e448750a4d76ec81fad4cb2b909db584cd14df7437154fde82bc6547689466e811dccf70dc8d27d1e76b00572f5137d653040319 SHA512 56a5e092f0aba91febf01d227e8068520513f632d09c5ffbad4cbb2de307d9066425f353a1b7fcf5648733e02a9b17cfc9aaed5e39d7562a6b143b1c9827a668 DIST firefox-153.5.0esr.source.tar.xz 801858032 BLAKE2B 6e674a6e0df2b40e14308f1b11cab342238414b08f87b831a4e23e3fac8621172205fe46a385c214319815ddcd1f5d79f89804f9c61bd868f39496e53605c7ab SHA512 9acb8d1a4b241767e39da36d6f520d4edaae0f237f311496beeed10e2383cf399b0af11cac6984d425b620af0ecba3e67808637ec74733c7d23186d0dd2bd5d4 +DIST firefox-153.5.1esr.source.tar.xz 801725652 BLAKE2B 91ea0afc3da04cfc1b37a873d06be26c5520f6f4efe19f89df3f73dd893b7633ee5e582c5ac946178fdfcc08505f2816fb6566d4a7a55e0128f95138d2ae4e7b SHA512 9dccf8c5383653ddcf807b500889a91f9b9d56a512dab88a2683755ae6cb0c4b5d0788faa9541e5709a2b783002ee9f434020a43c8a43653038a2c3decd9f2c1 DIST firefox-153esr-patches-03.tar.xz 13056 BLAKE2B 25dcb9db586180e2e695a148eabcf226a271abac110c632bfca0f8f659387c2d14c5a5fd5706eb300cf990a4f424e34abc3d7edb3db0e62b662aa0ade47b75a9 SHA512 686164839139581802e08cae254d84e2e5c13a8f0826b47be645c697024af89eec51175f823586851ff18de717a366362898bb5a42ecbaa863ccfaf11af66dd3 DIST firefox-153esr-patches-04.tar.xz 12232 BLAKE2B 3f792b89ec324437dbd8e6116eefca5986b0c9c2f286ec42e7faf887df106bbfb0266288fd981e3dc11b858920f13aa2a25f9f049af1cae18f1323e68976f4ec SHA512 5b0061a1ab8d90ffa2a18109718c16c7fbca0ad23ffa752a5a19746de8824592eea35471bfb3cf05d5cae890084431af100062648f44941f9689eee4a46ed20e DIST spidermonkey-128-patches-04.tar.xz 4188 BLAKE2B 0a758c1bac3f2285192ac4d0e442bd6a9fdae7d8f1a48251818d69932074862304b132f5c19e4144e6cc2529b7954fa10ae414559b5df4fc7c243a99d7772d64 SHA512 a983b6b5902ff14f294dce38bf12fea683daee2b58d02e7f697b774d750229b8337cd6509f2d51c430f23900aae396eb968b38d98237385bec7e8d53e79d51e8 diff --git a/dev-lang/spidermonkey/spidermonkey-153.5.1.ebuild b/dev-lang/spidermonkey/spidermonkey-153.5.1.ebuild new file mode 100644 index 000000000000..ea091bbaf6a0 --- /dev/null +++ b/dev-lang/spidermonkey/spidermonkey-153.5.1.ebuild @@ -0,0 +1,468 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI="8" + +FIREFOX_PATCHSET="firefox-153esr-patches-04.tar.xz" +SPIDERMONKEY_PATCHSET="spidermonkey-140-patches-02.tar.xz" + +LLVM_COMPAT=( 22 23 ) +RUST_NEEDS_LLVM=1 +RUST_MIN_VER=1.90.0 +# RUST_MAX_VER=1.94.1 + +PYTHON_COMPAT=( python3_{12..14} ) +PYTHON_REQ_USE="ncurses,ssl,xml(+)" + +WANT_AUTOCONF="2.1" + +inherit check-reqs flag-o-matic llvm-r1 multiprocessing python-any-r1 rust toolchain-funcs + +MY_PN="mozjs" +MY_PV="${PV/_pre*}" + +MY_MAJOR=$(ver_cut 1) + +MOZ_ESR=yes + +MOZ_PV=${PV} +MOZ_PV_SUFFIX= +if [[ ${PV} =~ (_(alpha|beta|rc).*)$ ]] ; then + MOZ_PV_SUFFIX=${BASH_REMATCH[1]} + + # Convert the ebuild version to the upstream Mozilla version + MOZ_PV="${MOZ_PV/_alpha/a}" # Handle alpha for SRC_URI + MOZ_PV="${MOZ_PV/_beta/b}" # Handle beta for SRC_URI + MOZ_PV="${MOZ_PV%%_rc*}" # Handle rc for SRC_URI +fi + +if [[ -n ${MOZ_ESR} ]] ; then + # ESR releases have slightly different version numbers + MOZ_PV="${MOZ_PV}esr" +fi + +MOZ_PN="firefox" +MOZ_P="${MOZ_PN}-${MOZ_PV}" +MOZ_PV_DISTFILES="${MOZ_PV}${MOZ_PV_SUFFIX}" +MOZ_P_DISTFILES="${MOZ_PN}-${MOZ_PV_DISTFILES}" + +MOZ_SRC_BASE_URI="https://archive.mozilla.org/pub/${MOZ_PN}/releases/${MOZ_PV}" + +if [[ ${PV} == *_rc* ]] ; then + MOZ_SRC_BASE_URI="https://archive.mozilla.org/pub/${MOZ_PN}/candidates/${MOZ_PV}-candidates/build${PV##*_rc}" +fi + +PATCH_URIS=( + https://dev.gentoo.org/~juippis/mozilla/patchsets/${FIREFOX_PATCHSET} + https://dev.gentoo.org/~juippis/mozilla/patchsets/${SPIDERMONKEY_PATCHSET} +) + +DESCRIPTION="Mozilla's JavaScript engine written in C and C++" +HOMEPAGE="https://spidermonkey.dev https://firefox-source-docs.mozilla.org/js/index.html" +SRC_URI="${MOZ_SRC_BASE_URI}/source/${MOZ_P}.source.tar.xz -> ${MOZ_P_DISTFILES}.source.tar.xz + ${PATCH_URIS[@]}" +S="${WORKDIR}/firefox-${PV%_*}" +LICENSE="MPL-2.0" +SLOT="$(ver_cut 1)/153.5.0" +KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc ~ppc64 ~riscv ~x86" + +IUSE="clang cpu_flags_arm_neon debug +jit test" + +#RESTRICT="test" +RESTRICT="!test? ( test )" + +BDEPEND="${PYTHON_DEPS} + $(llvm_gen_dep ' + llvm-core/llvm:${LLVM_SLOT} + clang? ( + llvm-core/clang:${LLVM_SLOT} + llvm-core/lld:${LLVM_SLOT} + ) + ') + >=dev-util/cbindgen-0.27.0 + virtual/pkgconfig + test? ( + $(python_gen_any_dep 'dev-python/six[${PYTHON_USEDEP}]') + )" +DEPEND=">=dev-libs/icu-78.1:= + >=dev-libs/nspr-4.39 + sys-libs/readline:0= + virtual/zlib:=" +RDEPEND="${DEPEND}" + +llvm_check_deps() { + if use clang ; then + if ! has_version -b "llvm-core/clang:${LLVM_SLOT}" ; then + einfo "llvm-core/clang:${LLVM_SLOT} is missing! Cannot use LLVM slot ${LLVM_SLOT} ..." >&2 + return 1 + fi + + if ! has_version -b "llvm-core/llvm:${LLVM_SLOT}" ; then + einfo "llvm-core/llvm:${LLVM_SLOT} is missing! Cannot use LLVM slot ${LLVM_SLOT} ..." >&2 + return 1 + fi + + if ! tc-ld-is-mold ; then + if ! has_version -b "llvm-core/lld:${LLVM_SLOT}" ; then + einfo "llvm-core/lld:${LLVM_SLOT} is missing! Cannot use LLVM slot ${LLVM_SLOT} ..." >&2 + return 1 + fi + fi + fi + + einfo "Using LLVM slot ${LLVM_SLOT} to build" >&2 +} + +mozconfig_add_options_ac() { + debug-print-function ${FUNCNAME} "$@" + + if [[ ${#} -lt 2 ]] ; then + die "${FUNCNAME} requires at least two arguments" + fi + + local reason=${1} + shift + + local option + for option in ${@} ; do + echo "ac_add_options ${option} # ${reason}" >>${MOZCONFIG} + done +} + +mozconfig_add_options_mk() { + debug-print-function ${FUNCNAME} "$@" + + if [[ ${#} -lt 2 ]] ; then + die "${FUNCNAME} requires at least two arguments" + fi + + local reason=${1} + shift + + local option + for option in ${@} ; do + echo "mk_add_options ${option} # ${reason}" >>${MOZCONFIG} + done +} + +mozconfig_use_enable() { + debug-print-function ${FUNCNAME} "$@" + + if [[ ${#} -lt 1 ]] ; then + die "${FUNCNAME} requires at least one arguments" + fi + + local flag=$(use_enable "${@}") + mozconfig_add_options_ac "$(use ${1} && echo +${1} || echo -${1})" "${flag}" +} + +python_check_deps() { + if use test ; then + python_has_version "dev-python/six[${PYTHON_USEDEP}]" + fi +} + +pkg_pretend() { + if use test ; then + CHECKREQS_DISK_BUILD="5600M" + else + CHECKREQS_DISK_BUILD="5500M" + fi + + check-reqs_pkg_pretend +} + +pkg_setup() { + # Get LTO from environment; export after this phase for use in src_configure (etc) + use_lto=no + + if [[ ${MERGE_TYPE} != binary ]] ; then + if tc-is-lto; then + use_lto=yes + # LTO is handled via configure + filter-lto + fi + + if [[ ${use_lto} = yes ]]; then + # -Werror=lto-type-mismatch -Werror=odr are going to fail with GCC, + # bmo#1516758, bgo#942288 + filter-flags -Werror=lto-type-mismatch -Werror=odr + fi + + if use test ; then + CHECKREQS_DISK_BUILD="5600M" + else + CHECKREQS_DISK_BUILD="5500M" + fi + + check-reqs_pkg_setup + llvm-r1_pkg_setup + rust_pkg_setup + python-any-r1_pkg_setup + + # Build system is using /proc/self/oom_score_adj, bug #604394 + addpredict /proc/self/oom_score_adj + + if ! mountpoint -q /dev/shm ; then + # If /dev/shm is not available, configure is known to fail with + # a traceback report referencing /usr/lib/pythonN.N/multiprocessing/synchronize.py + ewarn "/dev/shm is not mounted -- expect build failures!" + fi + + # Ensure we use C locale when building, bug #746215 + export LC_ALL=C + fi + + export use_lto +} + +src_prepare() { + if [[ ${use_lto} == "yes" ]]; then + rm -v "${WORKDIR}"/firefox-patches/*-LTO-Only-enable-LTO-*.patch || die + fi + + # Workaround for bgo #915651,915651,929013 and bmo#1988166 on musl + if use elibc_glibc ; then + rm -v "${WORKDIR}"/firefox-patches/*bgo-748849-RUST_TARGET_override.patch || die + rm -v "${WORKDIR}"/firefox-patches/*bgo-967694-musl-prctrl-exception-on-musl.patch || die + fi + + eapply "${WORKDIR}"/firefox-patches + eapply "${WORKDIR}"/spidermonkey-patches + + default + + # Make cargo respect MAKEOPTS + export CARGO_BUILD_JOBS="$(makeopts_jobs)" + + # Workaround for bgo #915651,915651,929013 on musl + if ! use elibc_glibc ; then + if use amd64 ; then + export RUST_TARGET="x86_64-unknown-linux-musl" + elif use x86 ; then + export RUST_TARGET="i686-unknown-linux-musl" + elif use arm64 ; then + export RUST_TARGET="aarch64-unknown-linux-musl" + elif use ppc64 ; then + export RUST_TARGET="powerpc64le-unknown-linux-musl" + else + die "Unknown musl chost, please post your rustc -vV along with emerge --info on Gentoo's bug #915651" + fi + fi + + # sed-in toolchain prefix + sed -i \ + -e "s/objdump/${CHOST}-objdump/" \ + python/mozbuild/mozbuild/configure/check_debug_ranges.py || + die "sed failed to set toolchain prefix" + + einfo "Removing pre-built binaries ..." + find third_party -type f \( -name '*.so' -o -name '*.o' \) -print -delete || die + + # Create build dir + BUILD_DIR="${WORKDIR}/${PN}_build" + mkdir -p "${BUILD_DIR}" || die +} + +src_configure() { + # Show flags set at the beginning + einfo "Current BINDGEN_CFLAGS:\t${BINDGEN_CFLAGS:-no value set}" + einfo "Current CFLAGS: ${CFLAGS}" + einfo "Current CXXFLAGS: ${CXXFLAGS}" + einfo "Current LDFLAGS: ${LDFLAGS}" + einfo "Current RUSTFLAGS: ${RUSTFLAGS}" + + local have_switched_compiler= + if use clang ; then + # Force clang + einfo "Enforcing the use of clang due to USE=clang ..." + + local version_clang=$(clang --version 2>/dev/null | grep -F -- 'clang version' | awk '{ print $3 }') + [[ -n ${version_clang} ]] && version_clang=$(ver_cut 1 "${version_clang}") + [[ -z ${version_clang} ]] && die "Failed to read clang version!" + + if tc-is-gcc; then + have_switched_compiler=yes + fi + + AR=llvm-ar + CC=${CHOST}-clang-${version_clang} + CXX=${CHOST}-clang++-${version_clang} + NM=llvm-nm + RANLIB=llvm-ranlib + READELF=llvm-readelf + OBJDUMP=llvm-objdump + + elif ! use clang && ! tc-is-gcc ; then + # Force gcc + have_switched_compiler=yes + einfo "Enforcing the use of gcc due to USE=-clang ..." + AR=gcc-ar + CC=${CHOST}-gcc + CXX=${CHOST}-g++ + NM=gcc-nm + RANLIB=gcc-ranlib + READELF=readelf + OBJDUMP=objdump + fi + + if [[ -n "${have_switched_compiler}" ]] ; then + # Because we switched active compiler we have to ensure + # that no unsupported flags are set + strip-unsupported-flags + fi + + # Ensure we use correct toolchain, + # AS is used in a non-standard way by upstream, #bmo1654031 + export HOST_CC="$(tc-getBUILD_CC)" + export HOST_CXX="$(tc-getBUILD_CXX)" + export AS="$(tc-getCC) -c" + + tc-export CC CXX LD AR AS NM OBJDUMP RANLIB READELF PKG_CONFIG + + # Pass the correct toolchain paths through cbindgen + if tc-is-cross-compiler ; then + export BINDGEN_CFLAGS="${SYSROOT:+--sysroot=${ESYSROOT}} --target=${CHOST} ${BINDGEN_CFLAGS-}" + fi + + # ../python/mach/mach/mixin/process.py fails to detect SHELL + export SHELL="${EPREFIX}/bin/bash" + + # Set state path + export MOZBUILD_STATE_PATH="${BUILD_DIR}" + + # Set MOZCONFIG + export MOZCONFIG="${S}/.mozconfig" + + # Initialize MOZCONFIG + mozconfig_add_options_ac '' --enable-project=js + + mozconfig_add_options_ac 'Gentoo default' \ + --host="${CBUILD:-${CHOST}}" \ + --target="${CHOST}" \ + --disable-ctype \ + --disable-jemalloc \ + --disable-smoosh \ + --disable-strip \ + --enable-packed-relative-relocs \ + --enable-readline \ + --enable-release \ + --enable-shared-js \ + --libdir="${EPREFIX}/usr/$(get_libdir)" \ + --prefix="${EPREFIX}/usr" \ + --with-intl-api \ + --with-system-icu \ + --with-system-nspr \ + --with-system-zlib \ + --with-toolchain-prefix="${CHOST}-" + + mozconfig_use_enable debug + mozconfig_use_enable jit + mozconfig_use_enable test tests + + if use debug ; then + mozconfig_add_options_ac '+debug' --disable-optimize + mozconfig_add_options_ac '+debug' --enable-debug-symbols + mozconfig_add_options_ac '+debug' --enable-real-time-tracing + else + mozconfig_add_options_ac '-debug' --enable-optimize + mozconfig_add_options_ac '-debug' --disable-debug-symbols + mozconfig_add_options_ac '-debug' --disable-real-time-tracing + fi + + # We always end up disabling this at some point due to newer rust versions. bgo#933372 + mozconfig_add_options_ac '--disable-rust-simd' --disable-rust-simd + + # Modifications to better support ARM, bug 717344 + if use cpu_flags_arm_neon ; then + mozconfig_add_options_ac '+cpu_flags_arm_neon' --with-fpu=neon + + if ! tc-is-clang ; then + # thumb options aren't supported when using clang, bug 666966 + mozconfig_add_options_ac '+cpu_flags_arm_neon' --with-thumb=yes + mozconfig_add_options_ac '+cpu_flags_arm_neon' --with-thumb-interwork=no + fi + fi + + # Tell build system that we want to use LTO + if [[ ${use_lto} == "yes" ]] ; then + if use clang ; then + if tc-ld-is-mold ; then + mozconfig_add_options_ac '+lto' --enable-linker=mold + else + mozconfig_add_options_ac '+lto' --enable-linker=lld + fi + mozconfig_add_options_ac '+lto' --enable-lto=cross + + else + mozconfig_add_options_ac '+lto' --enable-linker=bfd + mozconfig_add_options_ac '+lto' --enable-lto=full + fi + fi + + # LTO flag was handled via configure + filter-lto + + # Pass MAKEOPTS to build system + export MOZ_MAKE_FLAGS="${MAKEOPTS}" + + # Use system's Python environment + export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE="none" + export PIP_NETWORK_INSTALL_RESTRICTED_VIRTUALENVS=mach + + # Disable notification when build system has finished + export MOZ_NOSPAM=1 + + # Portage sets XARGS environment variable to "xargs -r" by default which + # breaks build system's check_prog() function which doesn't support arguments + mozconfig_add_options_ac 'Gentoo default' "XARGS=${EPREFIX}/usr/bin/xargs" + + # Set build dir + mozconfig_add_options_mk 'Gentoo default' "MOZ_OBJDIR=${BUILD_DIR}" + + # Show flags we will use + einfo "Build BINDGEN_CFLAGS:\t${BINDGEN_CFLAGS:-no value set}" + einfo "Build CFLAGS: ${CFLAGS}" + einfo "Build CXXFLAGS: ${CXXFLAGS}" + einfo "Build LDFLAGS: ${LDFLAGS}" + einfo "Build RUSTFLAGS: ${RUSTFLAGS}" + + ./mach configure || die +} + +src_compile() { + ./mach build --verbose || die +} + +src_test() { + if "${BUILD_DIR}/js/src/js" -e 'print("Hello world!")'; then + einfo "Smoke-test successful, continuing with full test suite" + else + die "Smoke-test failed: did interpreter initialization fail?" + fi + + cp "${FILESDIR}"/spidermonkey-${PV%%.*}-known-test-failures.txt "${T}"/known_test_failures.list || die + ./mach jstests --exclude-file="${T}"/known_test_failures.list || die +} + +src_install() { + cd "${BUILD_DIR}" || die + default + + # fix soname links + pushd "${ED}"/usr/$(get_libdir) &>/dev/null || die + mv lib${MY_PN}-${MY_MAJOR}.so lib${MY_PN}-${MY_MAJOR}.so.0.0.0 || die + ln -s lib${MY_PN}-${MY_MAJOR}.so.0.0.0 lib${MY_PN}-${MY_MAJOR}.so.0 || die + ln -s lib${MY_PN}-${MY_MAJOR}.so.0 lib${MY_PN}-${MY_MAJOR}.so || die + popd &>/dev/null || die + + # remove unneeded files + rm \ + "${ED}"/usr/bin/js${MY_MAJOR}-config \ + "${ED}"/usr/$(get_libdir)/libjs_static.ajs || die + + # fix permissions + chmod -x \ + "${ED}"/usr/$(get_libdir)/pkgconfig/*.pc \ + "${ED}"/usr/include/mozjs-${MY_MAJOR}/js-config.h || die +} diff --git a/dev-libs/libansilove/Manifest b/dev-libs/libansilove/Manifest index 83b61e147476..7e1a86064e4a 100644 --- a/dev-libs/libansilove/Manifest +++ b/dev-libs/libansilove/Manifest @@ -1 +1,2 @@ DIST libansilove-1.4.2.gh.tar.gz 62575 BLAKE2B 5e6f321248c7241a5e7cd6dc181537d215b8dc2d97817429d8152dc49259529a91ef01fed92cc0c27b7337fe52b7c14ec74e6ea346428263af88bb6e396b6962 SHA512 3c15c06dd66b1d92db6f144c6e89fda43229c92188ff5f8eb55e0d172be5c540835cd7e793a6fa094027575aacc19bac64a032f4e2f2e3dd6fb9ef1f8b775cfd +DIST libansilove-1.4.3.gh.tar.gz 63425 BLAKE2B c2b8e055e2e8f9cb728b7c622e26867310db8485ffda442fc3876a935246cecff91df55d28a8ddfd1c9f22b8245551f8d92f74eaf3dd8225e93c9d76f94e827b SHA512 98fdd0cf9fb14475bf071dbae363ad772a66d5b8da60f833f4a6dafc1896ede15ec28f4891bc25568b557dcda4ead3f53ae0726e7851b26885342108f5185b8c diff --git a/dev-libs/libansilove/libansilove-1.4.3.ebuild b/dev-libs/libansilove/libansilove-1.4.3.ebuild new file mode 100644 index 000000000000..cb51973e920f --- /dev/null +++ b/dev-libs/libansilove/libansilove-1.4.3.ebuild @@ -0,0 +1,33 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake + +DESCRIPTION="library to convert ANSi and artscene related file formats into PNG images" +HOMEPAGE="https://github.com/ansilove/libansilove/" + +if [[ "${PV}" == *9999* ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/ansilove/${PN}" +else + SRC_URI="https://github.com/ansilove/${PN}/archive/refs/tags/${PV}.tar.gz + -> ${P}.gh.tar.gz" + KEYWORDS="~amd64 ~arm ~arm64 ~riscv ~x86" +fi + +LICENSE="BSD-2" +SLOT="0" + +RDEPEND=" + media-libs/gd:2=[png] +" +DEPEND=" + ${RDEPEND} +" + +src_install() { + cmake_src_install + find "${ED}" -type f -iname "*.a" -delete || die +} diff --git a/dev-libs/libevdev/Manifest b/dev-libs/libevdev/Manifest index 3bb96032326b..c00ba38e7187 100644 --- a/dev-libs/libevdev/Manifest +++ b/dev-libs/libevdev/Manifest @@ -1,2 +1,3 @@ DIST libevdev-1.13.6.tar.xz 472152 BLAKE2B 18ebeb8915913d451372df8d58a6d8228df0416c87b507480562ebe144b073f4f03a5dcf6ab93ecaa3f32206bf91ebad64046ea722134ed221a479a2d59fe13a SHA512 367081bf587dd63845086714e0b51ec0f9eb107d4c32cc387cf60d3c41ad3cb31a64b1c74e25116b5bc900d6c34e82694697e942bb646f7161b0670b70d1b54d DIST libevdev-1.13.7.tar.xz 472444 BLAKE2B 8ffb11512ce89befdcb2e91e1aa144bbb25fcb23ca3270bbfffebb6c13e2b6bae0dd6f96917d24acf934956e4d3ba50faa3e84013f6d071f6bba3624289e2c7d SHA512 fd64ded32a7f303d45d545ebf293cc9d1a1e79672e1d5879d05e2c723b78709d1ecc972afb1f043eafe961cbded06d221a6fccc25ebba00a68fcf76e4ee3da8b +DIST libevdev-1.14.0.tar.xz 427752 BLAKE2B ac1f78e6c90325d3b44589f9e0308e6e51512d733fab28f806d14f3f3dd7e2610ab132a5c86e4ac681b94fcf39732456307e96d678dd3c2c05a78635b5c771c4 SHA512 527a643ec805618f04967c88105347966fc54726edbd884631255eb8f71cd4fdcb661b2051cd724a979372dbc5a7e47c8ab68098fa8cd3fcdd9a5413db7e062c diff --git a/dev-libs/libevdev/libevdev-1.14.0.ebuild b/dev-libs/libevdev/libevdev-1.14.0.ebuild new file mode 100644 index 000000000000..716471236929 --- /dev/null +++ b/dev-libs/libevdev/libevdev-1.14.0.ebuild @@ -0,0 +1,50 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +PYTHON_COMPAT=( python3_{13..14} ) + +inherit meson-multilib python-any-r1 + +DESCRIPTION="Handler library for evdev events" +HOMEPAGE="https://www.freedesktop.org/wiki/Software/libevdev/ https://gitlab.freedesktop.org/libevdev/libevdev" + +if [[ ${PV} == 9999* ]] ; then + EGIT_REPO_URI="https://gitlab.freedesktop.org/libevdev/libevdev.git" + inherit git-r3 +else + SRC_URI="https://www.freedesktop.org/software/libevdev/${P}.tar.xz" + KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" +fi + +LICENSE="MIT" +SLOT="0" +IUSE="doc test" + +DEPEND="test? ( dev-libs/check[${MULTILIB_USEDEP}] )" +BDEPEND=" + ${PYTHON_DEPS} + doc? ( app-text/doxygen ) + virtual/pkgconfig +" +RESTRICT="!test? ( test )" + +multilib_src_configure() { + local emesonargs=( + $(meson_feature doc documentation) + $(meson_feature test tests) + ) + meson_src_configure +} + +multilib_src_test() { + meson_src_test -t 100 +} + +multilib_src_install_all() { + if use doc; then + local HTML_DOCS=( doc/html/. ) + einstalldocs + fi +} diff --git a/dev-libs/libevdev/libevdev-9999.ebuild b/dev-libs/libevdev/libevdev-9999.ebuild index 791c3bb8f626..716471236929 100644 --- a/dev-libs/libevdev/libevdev-9999.ebuild +++ b/dev-libs/libevdev/libevdev-9999.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -15,7 +15,7 @@ if [[ ${PV} == 9999* ]] ; then inherit git-r3 else SRC_URI="https://www.freedesktop.org/software/libevdev/${P}.tar.xz" - KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" + KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" fi LICENSE="MIT" diff --git a/dev-libs/libratbag/Manifest b/dev-libs/libratbag/Manifest index 41d06358c506..469a9c3d555f 100644 --- a/dev-libs/libratbag/Manifest +++ b/dev-libs/libratbag/Manifest @@ -1,2 +1 @@ -DIST libratbag-0.17.tar.gz 279273 BLAKE2B 7252afc8bca5d95c24d61dbd68c4e486550df7d6d9fa74a8c9d2a58c6363ed2ee289c0d02cdd72c968618f5c08a0687c97836dd2c47b457ba4228f4716b50cb9 SHA512 80ab5c895d83e28b6b6adcd0669854944d7932ef037c8d64b923a7362399e861475e9d81c5ee2cf814f654d7796b00a81314cc177f34e13c8684d525974068c8 DIST libratbag-0.18.tar.gz 296737 BLAKE2B db691af43b3d2aeafcb31cdb4ce41504dd37a7a0a5a6d97bf2a3bee6840bbe49de25351990787436bc00a0dc80f5a20e31030069aa58ea8e38c7cad1029e39f8 SHA512 ae1796d738f7e448c6022c56cb876af5fb43e29b89fef31bbf42b9c838a07fd1d6cf94815dfde87dcd94a72bae159ca14624826b75cf83c988ba4c20340031cb diff --git a/dev-libs/libratbag/libratbag-0.17-r2.ebuild b/dev-libs/libratbag/libratbag-0.17-r2.ebuild deleted file mode 100644 index 8f6aaf51a5cb..000000000000 --- a/dev-libs/libratbag/libratbag-0.17-r2.ebuild +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright 1999-2025 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit linux-info meson python-single-r1 systemd udev - -DESCRIPTION="Library to configure gaming mice" -HOMEPAGE="https://github.com/libratbag/libratbag" - -if [[ ${PV} == 9999 ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/libratbag/libratbag.git" -else - SRC_URI="https://github.com/${PN}/${PN}/archive/v${PV}.tar.gz -> ${P}.tar.gz" - KEYWORDS="~amd64 ~x86" -fi - -LICENSE="MIT" -SLOT="0" -IUSE="doc elogind systemd test" -REQUIRED_USE=" - ${PYTHON_REQUIRED_USE} - ^^ ( elogind systemd ) -" -RESTRICT="!test? ( test )" - -BDEPEND=" - ${PYTHON_DEPS} - dev-lang/swig - virtual/pkgconfig - doc? ( - $(python_gen_cond_dep ' - dev-python/sphinx[${PYTHON_USEDEP}] - dev-python/sphinx-rtd-theme[${PYTHON_USEDEP}] - ') - ) - test? ( - dev-libs/check - >=dev-libs/gobject-introspection-1.82.0-r2 - dev-debug/valgrind - $(python_gen_cond_dep ' - dev-python/evdev[${PYTHON_USEDEP}] - dev-python/pygobject:3[${PYTHON_USEDEP}] - ') - ) -" -RDEPEND=" - ${PYTHON_DEPS} - acct-group/plugdev - dev-libs/glib:2 - dev-libs/json-glib - dev-libs/libevdev - dev-libs/libunistring:= - virtual/libudev:= - $(python_gen_cond_dep ' - dev-python/pygobject:3[${PYTHON_USEDEP}] - dev-python/evdev[${PYTHON_USEDEP}] - ') - elogind? ( sys-auth/elogind ) - systemd? ( sys-apps/systemd ) -" -DEPEND=" - ${RDEPEND} - >=dev-libs/gobject-introspection-1.82.0-r2 -" - -CONFIG_CHECK="~HIDRAW" - -PATCHES=( - "${FILESDIR}"/${PN}-0.17-python3.12-imp.patch - "${FILESDIR}"/${PN}-0.17-basename.patch -) - -pkg_setup() { - linux-info_pkg_setup - python-single-r1_pkg_setup -} - -src_prepare() { - default - - if use elogind ; then - # Fix systemd includes for elogind - sed -i -e 's@include <systemd@include <elogind@' \ - ratbagd/ratbag*.c || die - fi -} - -src_configure() { - python_setup - - local emesonargs=( - $(meson_use doc documentation) - $(meson_use systemd) - $(meson_use test tests) - -Ddbus-group="plugdev" - -Dlogind-provider=$(usex elogind elogind systemd) - -Dsystemd-unit-dir="$(systemd_get_systemunitdir)" - -Dudev-dir="${EPREFIX}$(get_udevdir)" - ) - - meson_src_configure -} - -src_install() { - meson_src_install - python_fix_shebang "${ED}"/usr/bin/ - newinitd "${FILESDIR}"/ratbagd.init ratbagd -} - -pkg_postinst() { - if [[ -z "${REPLACING_VERSIONS}" ]] ; then - elog 'You need to be in "plugdev" group in order to access the' - elog 'ratbagd dbus interface' - fi - elog 'You may be required to create and/or be part of the "games" group if you intend on using piper' -} diff --git a/dev-libs/nss/Manifest b/dev-libs/nss/Manifest index 7cc0da9a19ed..ecad08fcaed2 100644 --- a/dev-libs/nss/Manifest +++ b/dev-libs/nss/Manifest @@ -5,3 +5,4 @@ DIST nss-3.129.tar.gz 87255363 BLAKE2B 54b7ff270850a8ebe21f370312c7a7283753acc36 DIST nss-3.130.tar.gz 87281666 BLAKE2B 7de6e126a97b600fd7618c4e098a25afdf12ceb6b2ff2e9458cc752edfdbd26f009396601d8e9a110882f8cd23d4f340911a854413881bbb593809c6f8ae2b48 SHA512 e52d5d970acec1f88435140c119e36ba77644e95738bf90edb7b1f9cf9e5ae18466691913984b6546143aee8ab381f2588eca5c244b8e36ba2bc7c643ae1fe97 DIST nss-3.131.1.tar.gz 88058024 BLAKE2B 442d37f9b04fd3f78503f3f51365feeed980573b87dd6efbb9eb44c3e0ac4d6449afd6a5215d64828b71f2e0d89a73a6085d27f7cd5b5b7b9e339c5392042a37 SHA512 d76217d89da49bc5dbe1374a1d45bece66565dfe528ecffc8612b0a0c9aaa3060356add0b5e9f42445344c65b95a0a7e61bb6f125069f030fea2081d89aed0b5 DIST nss-3.131.tar.gz 88059889 BLAKE2B 407691e4e1d67dac04f9093618f9c1029aa6c7c38d55d24e7436428598598e2bd4578c657ae8981aabfadfa221e95b5216dff42a3d3292ba9cb270b11669c567 SHA512 acbc09c7fb127c74df5eb34cd0c3f65d19493d1e28baa6152333b0254261a77aef39ce151159b07e0e6bf0695423144d67b387b2a3afdc3ed785eb1cf904a4c5 +DIST nss-3.132.tar.gz 87788090 BLAKE2B cce13a84832be592b5dcda776e4022f122a6ddb2ff13c7c78bcfa7802ade728456c40bdf6e3221d9bd155b24a6a225b1e69def904f9e4ace349f0930ae255b07 SHA512 9e0936f1b3f1480351895b371e6571440ff0bfcd0c069f0d558006e07ce870b6bebb440bc96e3e1d45fdd3c288da11c7380f3f69646ab1e3c79311f648ce12f9 diff --git a/dev-libs/nss/nss-3.132.ebuild b/dev-libs/nss/nss-3.132.ebuild new file mode 100644 index 000000000000..74ec85d4e9c5 --- /dev/null +++ b/dev-libs/nss/nss-3.132.ebuild @@ -0,0 +1,437 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit dot-a flag-o-matic multilib toolchain-funcs multilib-minimal + +NSPR_VER="4.39" +RTM_NAME="NSS_${PV//./_}_RTM" + +# If the release is made in Github only, not released at the official archive.mozilla.org. These +# Github-only tarballs have a different directory structure. Leave the variable empty for +# archive.mozilla.org release: +# GH_ONLY_REL= +GH_ONLY_REL= + +DESCRIPTION="Mozilla's Network Security Services library that implements PKI support" +HOMEPAGE="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS" + +if [[ -n ${GH_ONLY_REL} ]] ; then + SRC_URI="https://github.com/nss-dev/nss/archive/refs/tags/${RTM_NAME}.tar.gz -> ${P}.tar.gz" + S="${WORKDIR}/${PN}-${RTM_NAME}" +else + SRC_URI="https://archive.mozilla.org/pub/security/nss/releases/${RTM_NAME}/src/${P}.tar.gz" + S="${WORKDIR}/${P}/${PN}" +fi +SRC_URI+=" cacert? ( https://dev.gentoo.org/~juippis/mozilla/patchsets/nss-3.104-cacert-class1-class3.patch )" + +LICENSE="|| ( MPL-2.0 GPL-2 LGPL-2.1 )" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-solaris" +IUSE="cacert test test-full +utils cpu_flags_ppc_altivec cpu_flags_x86_avx2 cpu_flags_x86_sse3 cpu_flags_ppc_vsx" + +REQUIRED_USE="test-full? ( test )" + +PROPERTIES="test_network" +RESTRICT="test" + +# pkg-config called by nss-config -> virtual/pkgconfig in RDEPEND +RDEPEND=" + >=dev-libs/nspr-${NSPR_VER}[${MULTILIB_USEDEP}] + >=dev-db/sqlite-3.8.2[${MULTILIB_USEDEP}] + >=virtual/zlib-1.2.8-r1:=[${MULTILIB_USEDEP}] + virtual/pkgconfig +" +DEPEND="${RDEPEND}" +BDEPEND="dev-lang/perl" + +MULTILIB_CHOST_TOOLS=( + /usr/bin/nss-config +) + +PATCHES=( + "${FILESDIR}"/nss-3.103-gentoo-fixes-add-pkgconfig-files.patch + "${FILESDIR}"/nss-3.21-gentoo-fixup-warnings.patch +) + +src_prepare() { + default + + if use cacert ; then + eapply -p2 "${DISTDIR}"/nss-3.104-cacert-class1-class3.patch + fi + + pushd coreconf >/dev/null || die + # hack nspr paths + echo 'INCLUDES += -I$(DIST)/include/dbm' \ + >> headers.mk || die "failed to append include" + + # modify install path + sed -e '/CORE_DEPTH/s:SOURCE_PREFIX.*$:SOURCE_PREFIX = $(CORE_DEPTH)/dist:' \ + -i source.mk || die + + # Respect LDFLAGS + sed -i -e 's/\$(MKSHLIB) -o/\$(MKSHLIB) \$(LDFLAGS) -o/g' rules.mk + + # Workaround make-4.4's change to sub-make, bmo#1800237, bgo#882069 + sed -i -e "s/^CPU_TAG = _.*/CPU_TAG = _$(nssarch)/" Linux.mk || die + + popd >/dev/null || die + + # Fix pkgconfig file for Prefix + sed -i -e "/^PREFIX =/s:= /usr:= ${EPREFIX}/usr:" \ + config/Makefile || die + + # use host shlibsign if need be #436216 + if tc-is-cross-compiler ; then + sed -i \ + -e 's:"${2}"/shlibsign:shlibsign:' \ + cmd/shlibsign/sign.sh || die + fi + + # dirty hack + sed -i -e "/CRYPTOLIB/s:\$(SOFTOKEN_LIB_DIR):../freebl/\$(OBJDIR):" \ + lib/ssl/config.mk || die + sed -i -e "/CRYPTOLIB/s:\$(SOFTOKEN_LIB_DIR):../../lib/freebl/\$(OBJDIR):" \ + cmd/platlibs.mk || die + + multilib_copy_sources + lto-guarantee-fat + + strip-flags +} + +multilib_src_configure() { + # Ensure we stay multilib aware + sed -i -e "/@libdir@/ s:lib64:$(get_libdir):" config/Makefile || die +} + +nssarch() { + # Most of the arches are the same as $ARCH + local t=${1:-${CHOST}} + case ${t} in + *86*-pc-solaris2*) echo "i86pc" ;; + aarch64*) echo "aarch64" ;; + hppa*) echo "parisc" ;; + i?86*) echo "i686" ;; + x86_64*) echo "x86_64" ;; + *) tc-arch ${t} ;; + esac +} + +# @USAGE: <pointer bytes> <tuple> +nssbits() { + case $1 in + 4) [[ $2 == x86_64* ]] && echo USE_X32=1 ;; + 8) echo USE_64=1 ;; + esac +} + +multilib_src_compile() { + tc-export_build_env + + local makeargs=( + CC="$(tc-getCC)" + CCC="$(tc-getCXX)" + AR="$(tc-getAR) rc \$@" + RANLIB="$(tc-getRANLIB)" + OPTIMIZER= + disable_ckbi=0 + $(nssbits "$(tc-get-ptr-size)" "${CHOST}") + ) + + # Take care of nspr settings #436216 + local myCPPFLAGS="${CPPFLAGS} $($(tc-getPKG_CONFIG) nspr --cflags) -D_FILE_OFFSET_BITS=64" + unset NSPR_INCLUDE_DIR + + export NSS_ALLOW_SSLKEYLOGFILE=1 + export NSS_ENABLE_WERROR=0 #567158 + export BUILD_OPT=1 + export NSS_USE_SYSTEM_SQLITE=1 + export NSDISTMODE=copy + export FREEBL_NO_DEPEND=1 + export FREEBL_LOWHASH=1 + export NSS_SEED_ONLY_DEV_URANDOM=1 + export USE_SYSTEM_ZLIB=1 + export ZLIB_LIBS=-lz + export ASFLAGS="" + # Fix build failure on arm64 + export NS_USE_GCC=1 + # Detect compiler type and set proper environment value + if tc-is-gcc; then + export CC_IS_GCC=1 + elif tc-is-clang; then + export CC_IS_CLANG=1 + + # bgo#927839 + export NSS_DISABLE_UNLOAD=1 + fi + + export NSS_DISABLE_GTESTS=$(usex !test 1 0) + + # Include exportable custom settings defined by users, #900915 + # Two examples uses: + # EXTRA_NSSCONF="MYONESWITCH=1" + # EXTRA_NSSCONF="MYVALUE=0 MYOTHERVALUE=1 MYTHIRDVALUE=1" + # e.g. + # EXTRA_NSSCONF="NSS_ALLOW_SSLKEYLOGFILE=0" + # or + # EXTRA_NSSCONF="NSS_ALLOW_SSLKEYLOGFILE=0 NSS_ENABLE_WERROR=1" + # etc. + if [[ -n "${EXTRA_NSSCONF}" ]]; then + ewarn "EXTRA_NSSCONF applied, please disable custom settings before reporting bugs." + read -a myextranssconf <<< "${EXTRA_NSSCONF}" + + for (( i=0; i<${#myextranssconf[@]}; i++ )); do + export "${myextranssconf[$i]}" + echo "exported ${myextranssconf[$i]}" + done + fi + + # explicitly disable altivec/vsx if not requested + # https://bugs.gentoo.org/789114 + case ${ARCH} in + ppc*) + use cpu_flags_ppc_altivec || export NSS_DISABLE_ALTIVEC=1 + use cpu_flags_ppc_vsx || export NSS_DISABLE_CRYPTO_VSX=1 + ;; + esac + + use cpu_flags_x86_avx2 || export NSS_DISABLE_AVX2=1 + use cpu_flags_x86_sse3 || export NSS_DISABLE_SSE3=1 + + local d + + # Disables calling shlibsign during the build #956431 and #436216 + tc-is-cross-compiler && makeargs+=( CROSS_COMPILE=1 ) + + # Build the host tools first. + LDFLAGS="${BUILD_LDFLAGS}" \ + XCFLAGS="${BUILD_CFLAGS} -D_FILE_OFFSET_BITS=64" \ + NSPR_LIB_DIR="${T}/fakedir" \ + emake -C coreconf \ + CC="$(tc-getBUILD_CC)" \ + $(nssbits "$(tc-get-build-ptr-size)" "${CBUILD}") + makeargs+=( NSINSTALL="${PWD}/$(find -type f -name nsinstall)" ) + + # Then build the target tools. + for d in . lib/dbm ; do + CPPFLAGS="${myCPPFLAGS}" \ + XCFLAGS="${CFLAGS} ${CPPFLAGS} -D_FILE_OFFSET_BITS=64" \ + NSPR_LIB_DIR="${T}/fakedir" \ + emake "${makeargs[@]}" -C ${d} OS_TEST="$(nssarch)" + done +} + +multilib_src_test() { + einfo "Tests can take a *long* time, especially on a multilib system." + einfo "~10 minutes per lib configuration with only 'standard' tests," + einfo "~40 minutes per lib configuration with 'full' tests. Bug #852755" + + # https://www.linuxfromscratch.org/blfs/view/svn/postlfs/nss.html + # https://firefox-source-docs.mozilla.org/security/nss/legacy/nss_sources_building_testing/index.html#running_the_nss_test_suite + # https://www-archive.mozilla.org/projects/security/pki/nss/testnss_32.html (older) + export BUILD_OPT=1 + export HOST="localhost" + export DOMSUF="localdomain" + export USE_IP=TRUE + export IP_ADDRESS="127.0.0.1" + + # Only run the standard cycle instead of full, reducing testing time from 45 minutes to 15 + # per lib implementation. + if use test-full ; then + # export NSS_CYCLES="standard pkix sharedb" + :; + else + export NSS_CYCLES="standard" + fi + + NSINSTALL="${PWD}/$(find -type f -name nsinstall)" + + cd "${BUILD_DIR}"/tests || die + # Hack to get current objdir (prefixed dir where built binaries are) + # Without this, at least multilib tests go wrong when building the amd64 variant + # after x86. + local objdir=$(find "${BUILD_DIR}"/dist -maxdepth 1 -iname Linux* | rev | cut -d/ -f1 | rev) + + # Can tweak to a subset of tests in future if we need to, but would prefer not + OBJDIR="${objdir}" DIST="${BUILD_DIR}/dist" MOZILLA_ROOT="${BUILD_DIR}" ./all.sh || die +} + +# Altering these 3 libraries breaks the CHK verification. +# All of the following cause it to break: +# - stripping +# - prelink +# - ELF signing +# http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn6.html +# Either we have to NOT strip them, or we have to forcibly resign after +# stripping. +#local_libdir="$(get_libdir)" +#export STRIP_MASK=" +# */${local_libdir}/libfreebl3.so* +# */${local_libdir}/libnssdbm3.so* +# */${local_libdir}/libsoftokn3.so*" + +export NSS_CHK_SIGN_LIBS="freebl3 nssdbm3 softokn3" + +generate_chk() { + local shlibsign="$1" + local libdir="$2" + einfo "Resigning core NSS libraries for FIPS validation" + shift 2 + local i + for i in ${NSS_CHK_SIGN_LIBS} ; do + local libname=lib${i}.so + local chkname=lib${i}.chk + "${shlibsign}" \ + -i "${libdir}"/${libname} \ + -o "${libdir}"/${chkname}.tmp \ + && mv -f \ + "${libdir}"/${chkname}.tmp \ + "${libdir}"/${chkname} \ + || die "Failed to sign ${libname}" + done +} + +cleanup_chk() { + local libdir="$1" + shift 1 + local i + for i in ${NSS_CHK_SIGN_LIBS} ; do + local libfname="${libdir}/lib${i}.so" + # If the major version has changed, then we have old chk files. + [ ! -f "${libfname}" -a -f "${libfname}.chk" ] \ + && rm -f "${libfname}.chk" + done +} + +multilib_src_install() { + pushd dist >/dev/null || die + + dodir /usr/$(get_libdir) + dolib.so */lib/*$(get_libname) + local i + for i in freebl nssb nssckfw ; do + dolib.a */lib/lib${i}.a + done + + # Install nss-config and pkgconfig file + dodir /usr/bin + cp -L */bin/nss-config "${ED}"/usr/bin || die + dodir /usr/$(get_libdir)/pkgconfig + cp -L */lib/pkgconfig/nss.pc "${ED}"/usr/$(get_libdir)/pkgconfig || die + + # create an nss-softokn.pc from nss.pc for libfreebl and some private headers + # bug 517266 + sed -e 's#Libs:#Libs: -lfreebl#' \ + -e 's#Cflags:#Cflags: -I${includedir}/private#' \ + */lib/pkgconfig/nss.pc >"${ED}"/usr/$(get_libdir)/pkgconfig/nss-softokn.pc \ + || die "could not create nss-softokn.pc" + + # all the include files + insinto /usr/include/nss + doins public/nss/*.{h,api} + insinto /usr/include/nss/private + doins private/nss/{blapi,alghmac,cmac}.h + + popd >/dev/null || die + + local f nssutils + # Always enabled because we need it for chk generation. + nssutils=( shlibsign ) + + if multilib_is_native_abi ; then + if use utils; then + # The tests we do not need to install. + #nssutils_test="bltest crmftest dbtest dertimetest + #fipstest remtest sdrtest" + # checkcert utils has been removed in nss-3.22: + # https://bugzilla.mozilla.org/show_bug.cgi?id=1187545 + # https://hg.mozilla.org/projects/nss/rev/df1729d37870 + # certcgi has been removed in nss-3.36: + # https://bugzilla.mozilla.org/show_bug.cgi?id=1426602 + # conflict removed in 3.122. + nssutils+=( + addbuiltin + atob + baddbdir + btoa + certutil + cmsutil + crlutil + derdump + digest + makepqg + mangle + modutil + multinit + nonspr10 + ocspclnt + oidcalc + p7content + p7env + p7sign + p7verify + pk11mode + pk12util + pp + rsaperf + selfserv + signtool + signver + ssltap + strsclnt + symkeyutil + tstclnt + vfychain + vfyserv + ) + # install man-pages for utils (bug #516810) + doman doc/nroff/*.1 + fi + pushd dist/*/bin >/dev/null || die + for f in ${nssutils[@]}; do + dobin ${f} + done + popd >/dev/null || die + fi + strip-lto-bytecode +} + +pkg_postinst() { + if [[ -n "${ROOT}" ]]; then + elog "You appear to to be installing in a seperate \$ROOT" + elog "to complete the setup and re-sign libraries please run:" + elog "emerge --config '=${CATEGORY}/${PF}'" + else + sign_libraries + fi +} + +pkg_config() { + sign_libraries +} + +sign_libraries() { + multilib_pkg_postinst() { + # We must re-sign the libraries AFTER they are stripped. + local shlibsign="${EROOT}/usr/bin/shlibsign" + # See if we can execute it (cross-compiling & such). #436216 + "${shlibsign}" -h >&/dev/null + if [[ $? -gt 1 ]] ; then + shlibsign="shlibsign" + fi + generate_chk "${shlibsign}" "${EROOT}"/usr/$(get_libdir) + } + + multilib_foreach_abi multilib_pkg_postinst +} + +pkg_postrm() { + multilib_pkg_postrm() { + cleanup_chk "${EROOT}"/usr/$(get_libdir) + } + + multilib_foreach_abi multilib_pkg_postrm +} diff --git a/dev-ml/uucp/Manifest b/dev-ml/uucp/Manifest index ee58f0769859..4f5c42ba2bcc 100644 --- a/dev-ml/uucp/Manifest +++ b/dev-ml/uucp/Manifest @@ -1,2 +1,3 @@ DIST uucp-16.0.0.tbz 509732 BLAKE2B a9618c15c7460d350707c34b0413fc472999dd2b597f543564e3d26db13d03c28288ea37ba10046d514a5508e6c2050f0a8504033d35be65bb3c0143fc91e120 SHA512 5c06d8cadb2b011b1e4ac52e14732044f6ab8e9c11e1184950ff8629b26bd173f1264247623a635b8aa4033e287bfe42d709994f19a3d79f7cbfd20158aa4992 DIST uucp-17.0.0.tbz 516263 BLAKE2B f29fb762002c14b86486d574438d064183b03c4f42d7d5853764ce1b708ab456af54ef4fa3748272d1e3e57048248ac3a9a7971a86e5a1f07c467204e72c2ca6 SHA512 c2b5c883c2ed1ee8e7bc9102339dfbca5940e68cd2af59a10abc827fb64c91ad03f85cfc12d4c79605c10e9b3a90743267af8b069143fefeeff6658ec8ed843f +DIST uucp-18.0.0.tbz 527774 BLAKE2B 6669e960f5c4e7191d7dadb6c73711cf8b33543888dead0f2cc44316c7f65993adf63bdcdb338dd924aa39dcc25cc2ec687324a796b7ecb1dda5063da7282f8e SHA512 89047b5586244c6636e8ebef64c911cb830bfe4e3cb62a344a1938a934ef51fdde75418785dc46da6e9a8d0eec1bdd22d508b6c4a4b29f6a48a220c8f718ad9f diff --git a/dev-ml/uucp/uucp-18.0.0.ebuild b/dev-ml/uucp/uucp-18.0.0.ebuild new file mode 100644 index 000000000000..c132967878de --- /dev/null +++ b/dev-ml/uucp/uucp-18.0.0.ebuild @@ -0,0 +1,34 @@ +# Copyright 2019-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit opam + +DESCRIPTION="Unicode character properties for OCaml" +HOMEPAGE="https://erratique.ch/software/uucp https://github.com/dbuenzli/uucp" +SRC_URI="https://erratique.ch/software/uucp/releases/${P}.tbz" + +LICENSE="ISC" +SLOT="0/${PV}" +KEYWORDS="~amd64 ~arm64 ~x86" + +DEPEND=" + dev-ml/cmdliner:= + dev-ml/uunf:= +" +RDEPEND=" + ${DEPEND} +" +BDEPEND=" + dev-ml/findlib + dev-ml/ocamlbuild + dev-ml/topkg +" + +OPAM_FILE="opam" + +src_compile() { + ocaml pkg/pkg.ml build --with-uunf true --with-cmdliner true || + die "failed to run the pkg/pkg.ml ocaml compilation script" +} diff --git a/dev-ml/uunf/Manifest b/dev-ml/uunf/Manifest index 477ff147f838..9e87894b8799 100644 --- a/dev-ml/uunf/Manifest +++ b/dev-ml/uunf/Manifest @@ -1,3 +1,4 @@ DIST uunf-15.1.0.tbz 43837 BLAKE2B 1af584fd25f1a1c0f15ba999ab11f155c607fdb54b208094d5586a274b54a823dab94a655ae0d80f6f75b90e1ef42d36295dc03dfb82d4c91fd9b6230689cb11 SHA512 1df1edbcb37da80e6d96eedd5e01c43c81275eca727a53d91a777d01f30cf0b964968c7be1d943e574e40ad4acb75c86e42976b3048dacb2c798b38475d0a6d0 DIST uunf-16.0.0.tbz 44311 BLAKE2B 568801f6c2732f83cb9463a1de24a205380305980da8fd9d09463ddb0f5d070a90044416387de30c1ebd1801e64034beee5296013305558f123cc38f351b1f37 SHA512 55e6aa2c0190667467744991839ae1024aa539fc94d9b8dcbaf8fdefed4f77a274acd22f79354b48b4a7582f308dbaadf14991ffee0c2aaf6e16f8efd538b756 DIST uunf-17.0.0.tbz 44269 BLAKE2B 92a77e86347c629f1312aebe081e15c263b94e7da754bb96b150ff0a403965b58ce361672f531dd607ab929b6ea6a55e487cceac8b563b552ac404d696d358d8 SHA512 1ec4d7229bb473ab2dd146ec732c8c70f357f73211e71d32625cd2410a35766e684bbeb1bd4e186b7403d4af8d597d46b7ccf58b23ae95aac32a96c0b4321691 +DIST uunf-18.0.0.tbz 44562 BLAKE2B 4d36c237ee606f9ff3c90f2e515ceb0f148dc6b87a5d634d0dd7bbaafe94b7ef227b9385a1447ff4a13d6435daa0a2565647bd27513cde395e231a4986415611 SHA512 739434317d84fb2f60f195d5645093fce5cfeb8ebb7e491345c80799528768a255cc39be05d12f873f028a302bbe0716b0591b8f47c6fe04fe309c6e6c93069c diff --git a/dev-ml/uunf/uunf-18.0.0.ebuild b/dev-ml/uunf/uunf-18.0.0.ebuild new file mode 100644 index 000000000000..28188f9c1f6f --- /dev/null +++ b/dev-ml/uunf/uunf-18.0.0.ebuild @@ -0,0 +1,38 @@ +# Copyright 2019-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit opam + +DESCRIPTION="Unicode text normalization" +HOMEPAGE="https://erratique.ch/software/uunf https://github.com/dbuenzli/uunf" +SRC_URI="https://erratique.ch/software/uunf/releases/${P}.tbz" + +LICENSE="ISC" +SLOT="0/${PV}" +KEYWORDS="~amd64 ~arm64 ~x86" + +DEPEND=" + dev-ml/cmdliner:= + dev-ml/uchar:= + dev-ml/uutf:= +" +RDEPEND=" + ${DEPEND} +" +BDEPEND=" + dev-ml/findlib + >=dev-ml/topkg-1.1.0 +" + +OPAM_FILE="opam" + +src_compile() { + # Increase stack limit to 11GiB to avoid stack overflow error. + # bug #798270 + ulimit -s 11530000 + + ocaml pkg/pkg.ml build --with-cmdliner true --with-uutf true \ + || die "failed to run the pkg/pkg.ml ocaml compilation script" +} diff --git a/dev-python/aiohttp-theme/aiohttp-theme-0.1.7.ebuild b/dev-python/aiohttp-theme/aiohttp-theme-0.1.7.ebuild index ba28ef791f30..594cc6aff04f 100644 --- a/dev-python/aiohttp-theme/aiohttp-theme-0.1.7.ebuild +++ b/dev-python/aiohttp-theme/aiohttp-theme-0.1.7.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/dev-python/alembic/Manifest b/dev-python/alembic/Manifest index 9fc3b03dd868..8475220be205 100644 --- a/dev-python/alembic/Manifest +++ b/dev-python/alembic/Manifest @@ -1,2 +1,2 @@ -DIST alembic-1.19.2.tar.gz 2082643 BLAKE2B 649f08694658c0974c59a04ac059fee1520e58975bc264c7f2c2834934be855c5b22eab636d23845baebacf929a46fb67a99ac5c3a1660205f83df93e61c93b1 SHA512 c6ae515303f5106bf357c92ff979c9960a682b086c91423d467565613e8011be17db90b13c298f237e37d5a598b32fa9aa0251e5499e4aaf6626b66ea4e41bc8 DIST alembic-1.20.0.tar.gz 2093272 BLAKE2B 85ed0b3703a6080548a974ade247f60fe322d1b959aceecbce9120cae42b59c995e60e9f413d3afedce59a6f36ce06f5143899f1be477ae05cf83ebbe69618b0 SHA512 d81b45783104ed5a349ae53dfbb07dd90bd9a3bd7f2f9c91e9f5005733949a684afec0cd03518475308b527f146164ded4160b5d26c096f6c595d1ffd2d4bab2 +DIST alembic-1.20.1.tar.gz 2103576 BLAKE2B 6d8de94f9773539db9a63b43df08a6b1d1c7509a79c4fabfab1e0252b7b516c181a3d00ba4f5a04599f2b4fe8852257278a1d7a880d34d6f7f55aad3c0c190a5 SHA512 5adb134698a25ee773e2dbc45fb59d4b81414bb26da903f463afb6dcdf32adf0c0b2ef19a77361ebdedf6946b24d8620f7fd646a1c23f028c13c0af3acb39762 diff --git a/dev-python/alembic/alembic-1.19.2.ebuild b/dev-python/alembic/alembic-1.20.1.ebuild index 4500ba72f4a7..0dead21950f2 100644 --- a/dev-python/alembic/alembic-1.19.2.ebuild +++ b/dev-python/alembic/alembic-1.20.1.ebuild @@ -16,11 +16,11 @@ HOMEPAGE=" LICENSE="MIT" SLOT="0" -KEYWORDS="amd64 arm64 ~riscv x86" +KEYWORDS="~amd64 ~arm64 ~riscv ~x86" IUSE="doc" RDEPEND=" - >=dev-python/sqlalchemy-1.4.23[${PYTHON_USEDEP}] + >=dev-python/sqlalchemy-2.0[${PYTHON_USEDEP}] dev-python/mako[${PYTHON_USEDEP}] >=dev-python/typing-extensions-4.12[${PYTHON_USEDEP}] " diff --git a/dev-python/b2sdk/Manifest b/dev-python/b2sdk/Manifest index 9a99b7283c87..a55dfe86f292 100644 --- a/dev-python/b2sdk/Manifest +++ b/dev-python/b2sdk/Manifest @@ -1,2 +1,2 @@ -DIST b2sdk-2.13.0.gh.tar.gz 497308 BLAKE2B c34f4144bd54305668f041a61a77b08871857ad08746e4deeaa83ca499ba09d985c4fcfa86d761c589248258296bd24a21093e7b52cad45296d011001fe62e22 SHA512 7e615a1fa43ff1288d5f34ea7530dc0ed90195db331f21eeaa26f32083a27fdbc5d18c81f30a591d7d10c84637af24bcc67db2597b23a8697ba04001c2c23923 DIST b2sdk-2.13.1.gh.tar.gz 556425 BLAKE2B 660ee67f87060a8b3168afb60006449641ab182d113ee88b765269b84f44eaafaccb2ba3b5cae34e91885decfb8610b42abec8ee546c7204b7ade5a89310a2b0 SHA512 04efb0da37a96451f390713585072f1cec00e9139fb6c4481cc6e06977f823faa8f483fe210bbed76b4fcd0c21fe1714baef70b27305555ea2111724de8d550e +DIST b2sdk-2.13.2.gh.tar.gz 556998 BLAKE2B 1283d73077635347fd849947c831079257902d4e210bcb61e1e1b698c097d559928c5e9286bd8936644b0c68c7755d2cea1a1b22c80d0fea66c970e5b2f0cd6d SHA512 814382606b387a6020c9cbd8675ed8f52d41e6f93f9cbfe3f633f458b1bda6e1f6e5bdfcb0d8eaa059bed48431ba4e29026368f41d3da3f38541c9086abb7e91 diff --git a/dev-python/b2sdk/b2sdk-2.13.0.ebuild b/dev-python/b2sdk/b2sdk-2.13.2.ebuild index 1c369300e6f9..4a89df5755bf 100644 --- a/dev-python/b2sdk/b2sdk-2.13.0.ebuild +++ b/dev-python/b2sdk/b2sdk-2.13.2.ebuild @@ -24,6 +24,7 @@ KEYWORDS="~amd64" # pydantic can be used but it has a fallback RDEPEND=" >=dev-python/annotated-types-0.5.0[${PYTHON_USEDEP}] + >=dev-python/certifi-2026.10.19[${PYTHON_USEDEP}] >=dev-python/logfury-1.0.1[${PYTHON_USEDEP}] >=dev-python/requests-2.33.0[${PYTHON_USEDEP}] >=dev-python/tenacity-9.1.2[${PYTHON_USEDEP}] diff --git a/dev-python/bpython/bpython-0.26.ebuild b/dev-python/bpython/bpython-0.26.ebuild index ccd9fc7adc95..8ab4c79e1ea9 100644 --- a/dev-python/bpython/bpython-0.26.ebuild +++ b/dev-python/bpython/bpython-0.26.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/dev-python/bracex/Manifest b/dev-python/bracex/Manifest index 42199b83d543..7800f5df69c7 100644 --- a/dev-python/bracex/Manifest +++ b/dev-python/bracex/Manifest @@ -2,3 +2,5 @@ DIST bracex-3.0.1.tar.gz 44019 BLAKE2B 12a8d7510c80e48b2461efbc85078aee51f0bf93b DIST bracex-3.0.1.tar.gz.provenance 9626 BLAKE2B 24fee61e3b213b97bc72a053c8377ea82c397eb2e1361c639c040a68dc81e25a318e41c3699cfad9f9d8f7ba934f163c7bac5d2a78edbf5e54223b7924bd4daf SHA512 377c69a0f72fb83cfb74659b6c76c6a733f92b85174dd42952746e7e905a2b9d60e4ca8d76df126857d685de557daa6da192f50d1903124addf7bf48efa77861 DIST bracex-3.1.tar.gz 29040 BLAKE2B 63c4440f9a4b2e374cf576c912b064240010d664be6bf578ea6b58110c2e4a72142693651fc0bbf692d9cc4caef23e192bdca6acbed0fab3e7e6c4d6a5d2ac5c SHA512 ea08acb29cabe0a0903af382b73c9e63ec02c4af6784728cd332949a0c19db44d1beef487455075f66b08599132ae3524d616b1646f41c39e86f9c691739a8c3 DIST bracex-3.1.tar.gz.provenance 9559 BLAKE2B 784da80e4cc9a89a66dcbc3cfb6cc9482762c53598d60608c7d88f7332d17b87128b647f78d202a993eb98d9b7874fe23f5eb29b8293a4bd64bdb0a1401efbd8 SHA512 440ee763aa7b6ff243e48db7584faa2e7b73604b95354c19fa93bd72eec4b95f0b87da952b85b225ec0236f3ff281aa09921161a23a9101901d8f5092524745f +DIST bracex-3.2.tar.gz 29771 BLAKE2B aff9fed33dcae630bad4050f6b431be59357b8ba1591ea31af95a98f9d04550f47909fe27cfa0d6bce1adf925c5b8786037197bce4e8dd1cd16e66908b5b7cca SHA512 bde1249d3d599a6b5ae3ca1c0d5b14314d758394ea96981a043b4db2bfb4c82b1bdfb6e4800c2a41b58471497eb120b4a69bfbc4a436cb88773872f81fd0e7ba +DIST bracex-3.2.tar.gz.provenance 9515 BLAKE2B d2478c25e923a95820fc04a775ed7fa9e70dbb295f22b30c89b4d128f2a0003a0e0686522ca7e16d9bcb2bb2235f448a81877f4570172327da086cbe07d53a90 SHA512 179af3706a336581c9b96bf5254f09184f491ca55cabf7cd69d3dad6cf36b2228d174bc9a76737894da566845e985e846fea94e0d3145bbd47b444a11bf52f9a diff --git a/dev-python/bracex/bracex-3.2.ebuild b/dev-python/bracex/bracex-3.2.ebuild new file mode 100644 index 000000000000..06fae3104a7d --- /dev/null +++ b/dev-python/bracex/bracex-3.2.ebuild @@ -0,0 +1,29 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=hatchling +PYPI_VERIFY_REPO=https://github.com/facelessuser/bracex +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 pypi + +DESCRIPTION="Bash style brace expansion for Python" +HOMEPAGE=" + https://github.com/facelessuser/bracex/ + https://pypi.org/project/bracex/ +" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" + +BDEPEND=" + test? ( + dev-vcs/git + ) +" + +EPYTEST_PLUGINS=() +distutils_enable_tests pytest diff --git a/dev-python/cliff/Manifest b/dev-python/cliff/Manifest index ecbfc109a49d..0285edbb1f82 100644 --- a/dev-python/cliff/Manifest +++ b/dev-python/cliff/Manifest @@ -1,2 +1,2 @@ -DIST cliff-4.15.0.tar.gz 89822 BLAKE2B 07b76a574654aff3d9d663968a7294fa11ba45655ff6e31d9304f91121a643872c8949d5bda8e6ec1f4b877f35a1073c50e40b8be4e40a1e9888a9a93354ebc4 SHA512 1a3353b5aa4313afe6e82c300fb8caa5530227992d123467c42b10db049b968e3219892e1b438057fa7afb50b7f38ef2b4d4d0241c632cc0ea2944bb8bb635c1 DIST cliff-4.16.0.tar.gz 90041 BLAKE2B beb71dada65a83ede4c16e1a0bdaea66a62b16fa7a5d94384aa30fb71e3a3730d456172d323679dc62d481f13b557821468f89de130b1a5fcaaea73f2f78ee13 SHA512 70f1d3d2b25ff7e4567175c0ff70d2f3fcf7dd5aa148340df5a13e321b152bca21f1f618f00858beeb289659f289fe240f45d9ff407fe0d192a933fe0e11c7d9 +DIST cliff-4.17.0.tar.gz 93300 BLAKE2B b731eac4855ba0c8c17982f3f66141799b26ef1719a91d049e4053efba04da4dd7e8ecc47f26d130a29739e4b40179361b01d06a507411268267a9eb90b8e8b5 SHA512 aa2142821cd5eaf85c559851e32db2c3a33819b1ea25c7e7d015b3b09ec6ac3008b903fd6ddc8dc4fb44ba7648268ef242226c74b252abfd509fa8aa977358bc diff --git a/dev-python/cliff/cliff-4.15.0.ebuild b/dev-python/cliff/cliff-4.17.0.ebuild index 33b5866a17db..f98f991409c0 100644 --- a/dev-python/cliff/cliff-4.15.0.ebuild +++ b/dev-python/cliff/cliff-4.17.0.ebuild @@ -17,11 +17,11 @@ HOMEPAGE=" LICENSE="Apache-2.0" SLOT="0" -KEYWORDS="~alpha amd64 ~arm arm64 ~hppa ~mips ~ppc64 ~riscv ~s390 x86" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~mips ~ppc64 ~riscv ~s390 ~x86" RDEPEND=" >=dev-python/autopage-0.4.0[${PYTHON_USEDEP}] - >=dev-python/cmd2-3.0.0[${PYTHON_USEDEP}] + >=dev-python/cmd2-4.0.0[${PYTHON_USEDEP}] dev-python/prettytable[${PYTHON_USEDEP}] >=dev-python/pyparsing-2.1.0[${PYTHON_USEDEP}] >=dev-python/pyyaml-3.12.0[${PYTHON_USEDEP}] diff --git a/dev-python/easy-thumbnails/easy-thumbnails-2.10.1.ebuild b/dev-python/easy-thumbnails/easy-thumbnails-2.10.1.ebuild index 431adb8ab1b3..b047bffb6b57 100644 --- a/dev-python/easy-thumbnails/easy-thumbnails-2.10.1.ebuild +++ b/dev-python/easy-thumbnails/easy-thumbnails-2.10.1.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/dev-python/fastapi/Manifest b/dev-python/fastapi/Manifest index b77ca9390610..507a763c986d 100644 --- a/dev-python/fastapi/Manifest +++ b/dev-python/fastapi/Manifest @@ -1,4 +1,2 @@ -DIST fastapi-0.143.3.tar.gz 469834 BLAKE2B 6384f8b551b38c1fc83779ef2a4a318e0cb4b01067e852e876b809d8dc8c2bf59dcbb109780337df3dd83b70ff61b30676fceb95745478439f580de2ad544c16 SHA512 f70e98e10e6d5d5b1db0fd6795179dff1bf7f737a0fee452732a49a22dca092efded647d27cf8fdb61b5eeba29311230fcca9d974ac258dc131d05080a134e7e -DIST fastapi-0.146.0.tar.gz 471622 BLAKE2B dc332799da2b2c3da0c0374bd4dba73d5189357664dbb4bdc34a0facb229fb13507835e8a04d829ef80eae1c1ea8e8657f73ba2d15c222d4a1119d5e2abd4e1e SHA512 bf7c8a7e137ac5541bc36e7cd0c0042ca02da6d8b7bd6bd5b7fea11578d69854bbfde980981a81735b5dc889611a3a21364be4c5388add324d1552550a2736cc -DIST fastapi-0.150.0.tar.gz 483799 BLAKE2B e3c7522a21be4a890abc9344a090e3d005791d318ea2a6616bb4dd39bace457c630cfd8d9c96df6166f98fe74e221e2d5912a904cc39a068428ab26a09bc0ca2 SHA512 926b2e6261725efb705ebb3a2048b36a9c18efc55598208c3de1541ce5de130d0c418cdb63d5be98ea20d1e7ec4959903308655f4154f40567ee00755bd1ce32 DIST fastapi-0.156.4.tar.gz 500884 BLAKE2B b566123362fbd40001981e13d6df980956c8d2b58ffc494caa724eca4c6910cd5b7dd784a6191c90610e12ed8ac5ce69d9790b66b94338b02a1a96711b381ded SHA512 443976e73fd51878d6f6dc5d92fdecad0d7f31734eecbf5bbf64b32dd4281253ad2bb3f90cf3b5aa288ef4386b30aeddd509903c31204f7439657714ba35e5fc +DIST fastapi-0.159.1.tar.gz 522004 BLAKE2B e96c6265c9b2cfe56136a492360f4aee38477bb6bd1df293d9f8627b7d49bb71766bc753875cb4339770d730682a650d15e636d4eca7314bf235642c4079fc73 SHA512 cc63af68adf7b1c94a66d1a35dbd8f06eaa99a67d06de1fa5a04c30916f8c261556e0d0489f163dce600f4ee9a97f30093a21fdf30a4f9f1b04da0a382161f5b diff --git a/dev-python/fastapi/fastapi-0.146.0.ebuild b/dev-python/fastapi/fastapi-0.146.0.ebuild deleted file mode 100644 index c7146bfca44a..000000000000 --- a/dev-python/fastapi/fastapi-0.146.0.ebuild +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright 2025-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -DISTUTILS_USE_PEP517=pdm-backend -PYTHON_COMPAT=( python3_{13..14} ) -#may be not stricly required -PYTHON_REQ_USE="threads(+)" - -inherit distutils-r1 optfeature pypi - -DESCRIPTION="High performance framework, easy to learn, fast to code, ready for production" -HOMEPAGE=" - https://fastapi.tiangolo.com/ - https://github.com/fastapi/fastapi/ - https://pypi.org/project/fastapi/ -" - -LICENSE="MIT" -SLOT="0" -KEYWORDS="~amd64" - -RDEPEND=" - >=dev-python/annotated-doc-0.0.4[${PYTHON_USEDEP}] - >=dev-python/opentelemetry-api-1.44.0[${PYTHON_USEDEP}] - >=dev-python/pydantic-2.9.0[${PYTHON_USEDEP}] - >=dev-python/starlette-0.46.0[${PYTHON_USEDEP}] - >=dev-python/typing-extensions-4.8.0[${PYTHON_USEDEP}] - >=dev-python/typing-inspection-0.4.2[${PYTHON_USEDEP}] -" - -#FIXME: add missing deps -BDEPEND=" - test? ( - dev-python/a2wsgi[${PYTHON_USEDEP}] - dev-python/dirty-equals[${PYTHON_USEDEP}] - dev-python/flask[${PYTHON_USEDEP}] - dev-python/httpx[${PYTHON_USEDEP}] - dev-python/httpx2[${PYTHON_USEDEP}] - dev-python/pydantic-settings[${PYTHON_USEDEP}] - dev-python/python-multipart[${PYTHON_USEDEP}] - dev-python/sqlalchemy[${PYTHON_USEDEP}] - dev-python/typer[${PYTHON_USEDEP}] - ) -" - -EPYTEST_PLUGINS=( anyio inline-snapshot pytest-timeout ) -# workaround broken toml config -: ${EPYTEST_TIMEOUT:=30} -# the test suite is flaky as hell -EPYTEST_RERUNS=5 -# 3k tests+, so use xdist to speed them up -EPYTEST_XDIST=1 -distutils_enable_tests pytest - -# There are some JSON response tests that will exercise orjson or ujson if they are available. -# These are properly skipped if the optional dependencies are not installed at the cost of a little spam. -python_test() { - local EPYTEST_IGNORE=( - # python/strawberry-graphql (in guru, test dep only) - tests/test_tutorial/test_graphql/test_tutorial001.py - # dev-python/sqlmodel (in guru, test dep only) - tests/test_tutorial/test_sql_databases/test_tutorial001.py - tests/test_tutorial/test_sql_databases/test_tutorial002.py - # dev-python/fastapi-cli (missing, test and optfeature) - tests/test_fastapi_cli.py - # dev-python/pwdlib (in guru, test dep only) - tests/test_tutorial/test_security/test_tutorial004.py - tests/test_tutorial/test_security/test_tutorial005.py - # Wants dev-python/multipart _just_ to complain that it's not the right multipart lib. - tests/test_multipart_installation.py - # scripts/tests use CliRunner.isolated_filesystem() removed in recent typer - scripts/tests - ) - local EPYTEST_DESELECT=( - # Requires investigation - missing ./image.png test file (?) - tests/test_tutorial/test_additional_responses/test_tutorial004.py::test_path_operation_img - - # Broken by brotli being available, also fragile to random ordering - tests/test_tutorial/test_header_param_models/test_tutorial001.py::test_header_param_model_invalid - tests/test_tutorial/test_header_param_models/test_tutorial003.py::test_header_param_model_invalid - tests/test_tutorial/test_header_param_models/test_tutorial003.py::test_header_param_model_no_underscore - # opentelemetry-* - tests/test_telemetry/test_background.py::test_background_task_integrations - tests/test_telemetry/test_data.py::test_request_objects_are_not_exported - tests/test_telemetry/test_environment_integrations.py - tests/test_telemetry/test_integrations.py::test_current_contrib - tests/test_telemetry/test_integrations.py::test_current_logfire - tests/test_telemetry/test_integrations.py::test_current_sentry - tests/test_telemetry/test_integrations.py::test_inactive_sentry_does_not_disable_native - tests/test_telemetry/test_integrations.py::test_logfire_global_provider_without_fastapi_instrumentor - tests/test_telemetry/test_native_integrations.py - tests/test_telemetry/test_runtime.py::test_concurrent_provider_owner_wins - tests/test_telemetry/test_runtime.py::test_environment_export_initializes_after_fork - tests/test_telemetry/test_runtime.py::test_failed_registration_closes_new_exporter - tests/test_telemetry/test_runtime.py::test_real_otlp_exception_export_without_traces_or_metrics - tests/test_telemetry/test_runtime.py::test_real_otlp_export_and_repeated_lifespans - tests/test_telemetry/test_runtime.py::test_registration_provider_prefers_public_metric_reader - tests/test_telemetry/test_runtime.py::test_unsupported_provider_warns_without_preventing_startup - ) - epytest -} - -pkg_postinst() { - # not part of any upstream extra - optfeature "alternative JSON library" "dev-python/ujson" # orjson, too but it's deprecated - - # pyproject.toml:project.optional-dependencies.standard - # Following optfeatures should be exposed, but missing deps - # optfeature "fastAPI CLI" "dev-python/fastapi-cli" - missing - depends on this package! - # optfeature "compressed file support" "dev-python/fastar" - missing - # optfeature "pydantic extra types support" "dev-python/pydantic-extra-types" - in guru - optfeature_header "standard extras" - optfeature "ASGI server support" "dev-python/uvicorn" - optfeature "email validation support" "dev-python/email-validator" - optfeature "TestClient for testing FastAPI applications" "dev-python/httpx" - optfeature "templating support" "dev-python/jinja2" - optfeature "multipart form data support" "dev-python/python-multipart" - optfeature "settings management support" "dev-python/pydantic-settings" - - # pyproject.toml:project.optional-dependencies.all - includes all the above plus: - optfeature_header "all extras" - optfeature "session middleware support" "dev-python/itsdangerous" - optfeature "YAML support" "dev-python/pyyaml" -} diff --git a/dev-python/fastapi/fastapi-0.150.0.ebuild b/dev-python/fastapi/fastapi-0.150.0.ebuild deleted file mode 100644 index 6c8cef00f625..000000000000 --- a/dev-python/fastapi/fastapi-0.150.0.ebuild +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2025-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -DISTUTILS_USE_PEP517=pdm-backend -PYTHON_COMPAT=( python3_{13..14} ) -#may be not stricly required -PYTHON_REQ_USE="threads(+)" - -inherit distutils-r1 optfeature pypi - -DESCRIPTION="High performance framework, easy to learn, fast to code, ready for production" -HOMEPAGE=" - https://fastapi.tiangolo.com/ - https://github.com/fastapi/fastapi/ - https://pypi.org/project/fastapi/ -" - -LICENSE="MIT" -SLOT="0" -KEYWORDS="~amd64" - -RDEPEND=" - >=dev-python/annotated-doc-0.0.4[${PYTHON_USEDEP}] - >=dev-python/opentelemetry-api-1.44.0[${PYTHON_USEDEP}] - >=dev-python/pydantic-2.9.0[${PYTHON_USEDEP}] - >=dev-python/starlette-0.46.0[${PYTHON_USEDEP}] - >=dev-python/typing-extensions-4.8.0[${PYTHON_USEDEP}] - >=dev-python/typing-inspection-0.4.2[${PYTHON_USEDEP}] -" - -#FIXME: add missing deps -BDEPEND=" - test? ( - dev-python/a2wsgi[${PYTHON_USEDEP}] - dev-python/dirty-equals[${PYTHON_USEDEP}] - dev-python/flask[${PYTHON_USEDEP}] - dev-python/httpx[${PYTHON_USEDEP}] - dev-python/httpx2[${PYTHON_USEDEP}] - dev-python/pydantic-settings[${PYTHON_USEDEP}] - dev-python/python-multipart[${PYTHON_USEDEP}] - dev-python/sqlalchemy[${PYTHON_USEDEP}] - dev-python/typer[${PYTHON_USEDEP}] - ) -" - -EPYTEST_PLUGINS=( anyio inline-snapshot pytest-timeout ) -# workaround broken toml config -: ${EPYTEST_TIMEOUT:=30} -# the test suite is flaky as hell -EPYTEST_RERUNS=5 -# 3k tests+, so use xdist to speed them up -EPYTEST_XDIST=1 -distutils_enable_tests pytest - -# There are some JSON response tests that will exercise orjson or ujson if they are available. -# These are properly skipped if the optional dependencies are not installed at the cost of a little spam. -python_test() { - local EPYTEST_IGNORE=( - # python/strawberry-graphql (in guru, test dep only) - tests/test_tutorial/test_graphql/test_tutorial001.py - # dev-python/sqlmodel (in guru, test dep only) - tests/test_tutorial/test_sql_databases/test_tutorial001.py - tests/test_tutorial/test_sql_databases/test_tutorial002.py - # dev-python/fastapi-cli (missing, test and optfeature) - tests/test_fastapi_cli.py - # dev-python/pwdlib (in guru, test dep only) - tests/test_tutorial/test_security/test_tutorial004.py - tests/test_tutorial/test_security/test_tutorial005.py - # Wants dev-python/multipart _just_ to complain that it's not the right multipart lib. - tests/test_multipart_installation.py - # scripts/tests use CliRunner.isolated_filesystem() removed in recent typer - scripts/tests - ) - local EPYTEST_DESELECT=( - # Requires investigation - missing ./image.png test file (?) - tests/test_tutorial/test_additional_responses/test_tutorial004.py::test_path_operation_img - # Broken by brotli being available, also fragile to random ordering - tests/test_tutorial/test_header_param_models/test_tutorial001.py::test_header_param_model_invalid - tests/test_tutorial/test_header_param_models/test_tutorial003.py::test_header_param_model_invalid - tests/test_tutorial/test_header_param_models/test_tutorial003.py::test_header_param_model_no_underscore - # opentelemetry-* - tests/test_telemetry/test_background.py::test_background_task_integrations - tests/test_telemetry/test_data.py::test_request_objects_are_not_exported - tests/test_telemetry/test_environment_integrations.py - tests/test_telemetry/test_integrations.py::test_current_contrib - tests/test_telemetry/test_integrations.py::test_current_logfire - tests/test_telemetry/test_integrations.py::test_current_sentry - tests/test_telemetry/test_integrations.py::test_inactive_sentry_does_not_disable_native - tests/test_telemetry/test_integrations.py::test_logfire_global_provider_without_fastapi_instrumentor - tests/test_telemetry/test_native_integrations.py - tests/test_telemetry/test_runtime.py::test_concurrent_provider_owner_wins - tests/test_telemetry/test_runtime.py::test_environment_export_initializes_after_fork - tests/test_telemetry/test_runtime.py::test_failed_registration_closes_new_exporter - tests/test_telemetry/test_runtime.py::test_real_otlp_exception_export_without_traces_or_metrics - tests/test_telemetry/test_runtime.py::test_real_otlp_export_and_repeated_lifespans - tests/test_telemetry/test_runtime.py::test_registration_provider_prefers_public_metric_reader - tests/test_telemetry/test_runtime.py::test_unsupported_provider_warns_without_preventing_startup - # TODO - 'tests/test_tutorial/test_testing_dependencies/test_tutorial001.py::test_override_in_items_run[tutorial001_py310]' - 'tests/test_tutorial/test_testing_dependencies/test_tutorial001.py::test_override_in_items_run[tutorial001_en_py310]' - ) - epytest -} - -pkg_postinst() { - # not part of any upstream extra - optfeature "alternative JSON library" "dev-python/ujson" # orjson, too but it's deprecated - - # pyproject.toml:project.optional-dependencies.standard - # Following optfeatures should be exposed, but missing deps - # optfeature "fastAPI CLI" "dev-python/fastapi-cli" - missing - depends on this package! - # optfeature "compressed file support" "dev-python/fastar" - missing - # optfeature "pydantic extra types support" "dev-python/pydantic-extra-types" - in guru - optfeature_header "standard extras" - optfeature "ASGI server support" "dev-python/uvicorn" - optfeature "email validation support" "dev-python/email-validator" - optfeature "TestClient for testing FastAPI applications" "dev-python/httpx" - optfeature "templating support" "dev-python/jinja2" - optfeature "multipart form data support" "dev-python/python-multipart" - optfeature "settings management support" "dev-python/pydantic-settings" - - # pyproject.toml:project.optional-dependencies.all - includes all the above plus: - optfeature_header "all extras" - optfeature "session middleware support" "dev-python/itsdangerous" - optfeature "YAML support" "dev-python/pyyaml" -} diff --git a/dev-python/fastapi/fastapi-0.143.3.ebuild b/dev-python/fastapi/fastapi-0.159.1.ebuild index c7146bfca44a..1e1bf586041a 100644 --- a/dev-python/fastapi/fastapi-0.143.3.ebuild +++ b/dev-python/fastapi/fastapi-0.159.1.ebuild @@ -76,7 +76,6 @@ python_test() { local EPYTEST_DESELECT=( # Requires investigation - missing ./image.png test file (?) tests/test_tutorial/test_additional_responses/test_tutorial004.py::test_path_operation_img - # Broken by brotli being available, also fragile to random ordering tests/test_tutorial/test_header_param_models/test_tutorial001.py::test_header_param_model_invalid tests/test_tutorial/test_header_param_models/test_tutorial003.py::test_header_param_model_invalid @@ -98,6 +97,11 @@ python_test() { tests/test_telemetry/test_runtime.py::test_real_otlp_export_and_repeated_lifespans tests/test_telemetry/test_runtime.py::test_registration_provider_prefers_public_metric_reader tests/test_telemetry/test_runtime.py::test_unsupported_provider_warns_without_preventing_startup + # TODO + tests/test_dependency_contextmanager.py::test_{a,}sync_state + tests/test_dependency_contextmanager.py::test_{a,}sync_raise_other + # tutorial tests are flaky + tests/test_tutorial/ ) epytest } diff --git a/dev-python/fastbencode/Manifest b/dev-python/fastbencode/Manifest index d43ee83ec364..37d4e60f4a6f 100644 --- a/dev-python/fastbencode/Manifest +++ b/dev-python/fastbencode/Manifest @@ -2,17 +2,30 @@ DIST fastbencode-0.3.10.tar.gz 17005 BLAKE2B bfd1f207260bb3dd435c01160b8d3868534 DIST fastbencode-0.3.10.tar.gz.provenance 9673 BLAKE2B f6ae595eed244d5f730d6aff79cf2c0e82ae5f387cc590deeed83273508eae157f84f03f0d77f03412e7878257e6f889e93b1c4b9c73f29053678920bd302a32 SHA512 1bcafc77ae527a3c8532cab32d4119e25f3b8f44538614023b0d6a5f69ed3f31927f8b0808a482e682f3f8078d5ea4ea6da235ff44ece416d67c45327a197c65 DIST fastbencode-0.3.11.tar.gz 20011 BLAKE2B 04d82eafc36dad08ec2af0b0749ed5120bd545ee240d030fb6e2f9bdc86dc0cce873871f4dd5072c65165510402e9151dfe4409b63d6e0024c14bfb93a5d3e23 SHA512 e21b7e5a9333f1e350f359145716656ebf824ef9a607007cb8268b8884b90a4f5b8507f4523457ff7ac89e6b34e071bc7c53aa57f4118d1f43f8418e3604af0a DIST fastbencode-0.3.11.tar.gz.provenance 10049 BLAKE2B de896250abe4d4c2ff3cb93a16134041acbd3960ea6f940ff9ff0743518f0124074774681807fab4c4f390bb951b08c58ebdd8879b2af371aa9dd9c1a0c3ae79 SHA512 4f631ea12506c03f593d182c70a227a9feab80be6792e7a6320f5be9f86c6e6ef855a8e25dd8f47f1e1135643bde9c544bfd8fdbc6ef6a05652d94f86782fc67 +DIST fastbencode-0.3.12.tar.gz 20074 BLAKE2B 1bf676880d98138539b51de2a3c4d10187fdb1cdbf026869015f3463d097b28d900bfffd073d994922af2e226c6532ef3b99b21d023792e88132394b22e18170 SHA512 d07796dc4ba51cb5188586f19abac1feb86397de2a9ff289a8fbb77f635052870a721cb480606557df4acf12b3e24cd2db7fae85d0b7a0ea37a058f50e6def00 +DIST fastbencode-0.3.12.tar.gz.provenance 10033 BLAKE2B c49dee70bcf5a78a15d6d5998bfc17ca4baa36f58bdbe7c342a1bb2922ac69366599dfc71385f04aa8c77317f03ba5159f2c3059fd97159da6de3fa4c082f0c3 SHA512 61ce1cf099a2ef95a8ab35bbc2afb1b40fba208e1c7f5f4f2893af54f86ab314132fb2d8f51504036e0bad31d148247d83e216afc418dc8e45732498cf2c5743 DIST heck-0.5.0.crate 11517 BLAKE2B 5365ec43b2239a76b33a174f1a4292ece4147f9d382a68c6c60db78fdc8bad0afb1d51a65bcb25e96675372faa4ea37c318265030b0546ba51942f7c929e1835 SHA512 f044fc9c3d22466629fd8f772ec0555350fd611c0cfadca51d99a3d2f10e155f77c1091916c8a95a6b9b499f366c2e99a5fbf45b010f988bfb9b2501bf9f6a76 DIST libc-0.2.183.crate 816918 BLAKE2B d0c26ca25b0808575aa540a5f0f43acaaa5a972269c5f101122538a1733b37b6aac3696a0d7298fe16974b06efb0b92072252eef40b17b977af24cc857963daf SHA512 017986d4fa8c362f8900c1c82c7381066382a7272cfe114243b8d47637d042c87b5ebef7881cf46727e00e541a0a8270a32db694ce8d78372672cf08224a90b2 +DIST libc-0.2.191.crate 855401 BLAKE2B 04788709363e27f063bd30d2e568d27b8a217d2702ccc32c28ac0f82c5f786e534f053d9133aa086d2186c060724adf0bd2c6fdf705ac6cde563561ecaba4e94 SHA512 f90bff0e667425a6680514097c44ad99454dfe9d2bec02a72633e94f300a6e39cba0a7887a7124ee3c2235d41836faff706649fa86b9970df7a7f72a460ea964 DIST once_cell-1.21.4.crate 35010 BLAKE2B 9117ed1d478e626b7b7be2e3ff8b68a382b051112c7ca4425367bdae86977671f8b5c576131c82fb437740b6d98b72501f718f4172d39781decac2385e8fbd4d SHA512 af67669b0107f44268ba74c355200cb2ed1aab235a6989a8bc54323eb2c9a45677010f8672e7790edadd4c981e939436e0c3a099d33c06dce9c14fd5ede86155 DIST portable-atomic-1.13.1.crate 197001 BLAKE2B 6d0898c2a537a9bf204b7749cca5f5fddf280772b56eb7ce8fdd9cf7d71561137b26ef89f415c40277a1c89981333eef78e5bb4624515a294ba28fdaeb4c70ea SHA512 2a1b31ac9814af884640b3398ab824a9795c72d260527a0966b193113808cfbb3345d50cd1beaebb45863437c3d06c8706d34b26efecceefc649319a4bc274a3 +DIST portable-atomic-1.15.0.crate 202705 BLAKE2B bf956c2bf08953b0ae1384d5c1703edf12c4a9b9d1f035b49043f4fb5e0a1ad005263affaf02331a77f098471fe2da8eee7b16861c33df9ef0d76444c614e291 SHA512 257cd168d0362b7b1d5e6ed9032cde9b0ac6e69d90b7e27347c39702a5ea020159c271e37e8045cf10661170f9354638791cfc972587bff3cba01da137f5a2c0 DIST proc-macro2-1.0.106.crate 59765 BLAKE2B 87f95795621c4c992c370dc3a1240a2d5580e3362356525efe640f8f26ad0492b3c88755d575c080ff7db11376b0d82f31141bf6f86c895249b85e46c081220f SHA512 b726e2c92af434bfa88cd4f53c3fe6db647503567675fb439890dee3d15f5111137e3242b28d164114ce081c10acf3fd11950753ddb349190c87ee04e7d97744 +DIST proc-macro2-1.0.107.crate 59588 BLAKE2B 91df0c573e3e1f09c1aeedcad0381cf18186d72c3d50b077b1d232fbb67d1c87ed09f3c6dc515140041c2dab5ce7706042c8235b93099df7d1c962d4a3a99160 SHA512 c109d5509624a57156e0eae22a3aec89a1a163623e8b4e376a13d872a46e4bf6855ee3cd4428c07cdf5c22f2284b6ce755f9cc7e075480e4587ce4f2d40fa903 DIST pyo3-0.28.2.crate 1202843 BLAKE2B 18158e22631ab35348eab1f117f16b311932de832d910f37a71cf481ec04ced03c37baaf63f1c8afc7ecccef039fc8f9072cbe0dd5109a74e7fb9f6b3ddb18a4 SHA512 fd4a03eb1cdfa40c1ed67b588cd4da31c90972f95c669b20a1099fb9304cd9506337f674e0bc9efd1d22a453162aac79a36ab18ca97eaca3515d640e2e9b923f +DIST pyo3-0.30.1.crate 1345706 BLAKE2B 42f28e632d23a90b6e1ec4eae54ee17c1c75312f3f53d896dcfc43e047f0fe72d94e33c793f47f108a5b3c98885bc78d978e347a68f771d710228c3e1d97ba9e SHA512 68799fc448f68b204d4825ce4e1a0b000febf18d968740fa9f94df9507b3a2697fb1c4b642d88ad1f1fb2b214fcd1b9a3a8089eccd8b0418c08b4ae69b7bfdec DIST pyo3-build-config-0.28.2.crate 35958 BLAKE2B 87163e11ff52061e11943f54e0fea1101ec6f877b4e6ecda33e32a19d1a8af3a6a83ab23d17f744fb0176e35f819669e931f88c55d611c8f7ea52eacd1c683a3 SHA512 f9b4863baffdf474ba9121edc19323d91565dd1411acabda03a307c8aae4969657167a80e197d4c8656e711e424053aced6a1de7978cedd38815304835fc5dbb +DIST pyo3-build-config-0.30.1.crate 42996 BLAKE2B e49f6b13e5a4379f3a0db2c305a96cd0b19955f22b3fd08fba4373a1ec886847abf619beddb7fc111e99bc174b47211d9de68230db8ea03e12e534bb5ceab7bf SHA512 9801d582931f1d6b835792ffa04439310544b068c9f94f1329487328d43010646dd81233575a4338c44e7fe86714a579186c04ee07604b94f8af5066730eb699 DIST pyo3-ffi-0.28.2.crate 80837 BLAKE2B ac17690399e5cf84602a974860d84856d9489c7d94ec618cdb3b4c5bcee81d3767a727f22dca0a06d492ee28ecf24c84f48cd391a6d81229b1464fbb17654ac5 SHA512 f6b4fdea320922b81395e593fe135fd4b4f483250fe94d84019c97a3a9f98b6e2122134e09613021f194782038ffea1d32d3c65b1458e15ce3021d39fd582738 +DIST pyo3-ffi-0.30.1.crate 93717 BLAKE2B 383cac05d9e768c9059cfd5f714e30848c87584bfcb0b8e421805f2da9f3b5a9a3f27d467cf9432b0f41b68003f9141dfdfeb29cdb1248cc68b01072dc01ec34 SHA512 32945d4b6f08723ae1129810ac71e12f5dc9c5de3710cadedb91bba17cbcf21acbf269598241117b45dc36141d28e6d1a58f1c07957b8664de62e991f30c82e9 DIST pyo3-macros-0.28.2.crate 8960 BLAKE2B 54fdb3f2d04c1f04d3cdb4d8422cd9fe1098df2f2740eaf61e2868e19f0a93c677486cef56576e39ba2010e426901c95846b7b81285858094ef1fdf48edc65b1 SHA512 7125a41bd5ceaa5c85a2ac5ed1bf07708cf95f58dd18a8ba9000a3139ebc06e90776b4254820410e940c80ed45f7c09165c8fe57d8f79069622849c6c0861bcb +DIST pyo3-macros-0.30.1.crate 9055 BLAKE2B f49eedcd8922df4e0b9b0e119a6b5d06be3afe3f0d6d3fcd332716aae593fffc8fea94b02a4de7410bd678f7ab0ffb499c3e11bc45f72c2d8fa4e41c400dca9b SHA512 26f3b4d1aa86da4a305b01d780ce7de3359d3bfeadcbdbb39b9803d0a7634cf3611dfd5a285d21e97d51f61a6c590fdef8ddda45ec9abe870575519e6bbb4f33 DIST pyo3-macros-backend-0.28.2.crate 87939 BLAKE2B c70e46d44c935e58957cd53a76a1e6d2e97ee1bdc39c926b1a23ce391785f212c30e150286fc78724e945679ac1ff04a10ec958b6d3dfe30c4acee795f6c2e63 SHA512 24203c47d91be9acc949cfc70ec8b4aa32e089275cf9b0061a9909f5e60895c56446692be0056e13cc4df8efc0698e0655626f9524241b54cd172d8a25e42f98 +DIST pyo3-macros-backend-0.30.1.crate 97593 BLAKE2B 6ff2e6e445f40b15ab8347a13b4786359875d16621ada11c0b8a5fc0efca84e306f95c1f2d5e87431bbd6303ead0d80aa469e3c53e0cb4360d6ea9524e0c9eb7 SHA512 29f96ff877d55ea3141435a30b8482743590f2dfd3fd9d24291cba8c834148cc2e0790125c8c0c3af650bf1c9b5e521a1a2ff3874dfa58bb3673dc11876ddf89 DIST quote-1.0.45.crate 31564 BLAKE2B 1b2c2c3e6ebabfe10affa4c43ca0bfd1f52d8fd49e1df463e13e63a82de41272089c8e09e4c02e8a33cfee768ace26d4d42a50752de8fb5a776c59806139ad5d SHA512 df96ede01ebd0ed642b758bf1d0862d0e116fc2b3343e14edb657ce5438ccabe743dec4a0a40de4dee98c134d169af9aba459faadf9c7a832b26377e755d49b0 +DIST quote-1.0.47.crate 31622 BLAKE2B d912f17bdb5f2db29f5349e976e12da52db9d2197dd9900cb00e907d1e9b06ab177e9b020c1d53f6a6cd087e3a973aed5af63eac282da77792b2a6d6e1b35b07 SHA512 544f4324d425ae011905e0d1b52e367d4660bc34457b2f802af94e1def1e6c4e39ffba3beeb40d75753967d15b6dccce18fea273e7f3d46b63d5c89898d19441 DIST syn-2.0.117.crate 306011 BLAKE2B bbaec02c4322f522bdfc87caa4ce6fd1e09bbf8f4a102d9ed8728efacdd843a2750f616dce5b2d3a3e46bbb7d57439fbd10e328bc1d4a01e980986cd815798af SHA512 035c2e63542592b6a4058a2a82d4c2c1f5793f84fb1b04cbd393337a26fcf661a1f3bea439ce98a1fe943b1c4d36d2bda16df3bbc3fa6ca35e1548d530dc8416 +DIST syn-3.0.9.crate 313970 BLAKE2B 63ba393b429909b75de710e1da70bbf5b5d4ba76917786ae5ac165ad57806a02b54af3fb7eaa5213f1e59e086ddf6807f08d4e37de2fc4b0a7270a290850fa91 SHA512 94ff1cfd429e4ca9c3a72c7e7012c5ccac2178134d4fb82683601bf466094e6178dd5bbc558e973a7f03e890e4e08cf8622dba2a2898fdde608a33b244e42cc5 DIST target-lexicon-0.13.5.crate 26928 BLAKE2B f8c62cb91f68a635e6ce3fc254df9ce378b5e8f62c1147404c967484b9ed97fb33d7993b4671611db76566063e999d658d201f856f70196ec678d72cc758cf6d SHA512 956df27c01e1186c5356117ffd07810fc0b62fb919d5bfd215955ba85be00ef675212a3c0dc36679986be7b825442fec5c93c7643b7e1e19c04572e6463546fc DIST unicode-ident-1.0.24.crate 49298 BLAKE2B 34ba2906a3ebc06f52a4ba77dff96a8f3fb977718d260886fa9e051c5830765641b65a63977110f3a79f31fa177b385f81b01d56c80f6a0324d36eae72e2ebf0 SHA512 7be712d8d0075ac9341790180e312f66264d3e0255c91b38df0951baa5e446dec24551bb2125a66a1cb9b6ca2f1ea37c0cc15fa3dd74f398ecf4d5302c7dc259 +DIST unicode-ident-1.0.27.crate 48807 BLAKE2B 2f756c8e8a8a4653e616e15c217d72a01af3e6538105c6cfbad6e89b1cf6bb5ba591855b5b7580989b44eef37279789a550331778ed924ee4906cd68dc8d2aa8 SHA512 c952d72be2eea9d70114072a1aabd17573ac672e23f3a549d2a2b6375d14215eb1f0a2dc00c89819eb40f26341a6e24c54cb7be1fe92494d9c01b5941ce25670 diff --git a/dev-python/fastbencode/fastbencode-0.3.12.ebuild b/dev-python/fastbencode/fastbencode-0.3.12.ebuild new file mode 100644 index 000000000000..e134f9d7d233 --- /dev/null +++ b/dev-python/fastbencode/fastbencode-0.3.12.ebuild @@ -0,0 +1,92 @@ +# Copyright 2025-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +CARGO_OPTIONAL=1 +RUST_MIN_VER=1.83.0 + +DISTUTILS_EXT=1 +DISTUTILS_USE_PEP517=setuptools +PYPI_VERIFY_REPO=https://github.com/breezy-team/fastbencode +PYTHON_COMPAT=( python3_{13..14} ) + +CRATES=" + heck@0.5.0 + libc@0.2.191 + once_cell@1.21.4 + portable-atomic@1.15.0 + proc-macro2@1.0.107 + pyo3-build-config@0.30.1 + pyo3-ffi@0.30.1 + pyo3-macros-backend@0.30.1 + pyo3-macros@0.30.1 + pyo3@0.30.1 + quote@1.0.47 + syn@3.0.9 + target-lexicon@0.13.5 + unicode-ident@1.0.27 +" + +inherit cargo distutils-r1 pypi + +DESCRIPTION="Implementation of bencode with Rust implementation" +HOMEPAGE=" + https://github.com/breezy-team/fastbencode/ + https://pypi.org/project/fastbencode/ +" +SRC_URI+=" + native-extensions? ( + ${CARGO_CRATE_URIS} + ) +" + +LICENSE="Apache-2.0" +LICENSE+=" native-extensions? (" +# Dependent crate licenses +LICENSE+=" + Apache-2.0-with-LLVM-exceptions Unicode-3.0 + || ( Apache-2.0 MIT ) +" +LICENSE+=" )" +SLOT="0" +KEYWORDS="~amd64 ~arm64 ~x86" +IUSE="+native-extensions" + +BDEPEND=" + native-extensions? ( + ${RUST_DEPEND} + dev-python/setuptools-rust[${PYTHON_USEDEP}] + ) +" + +distutils_enable_tests unittest + +QA_FLAGS_IGNORED="usr/lib.*/py.*/site-packages/fastbencode/_bencode_rs.*.so" + +pkg_setup() { + use native-extensions && rust_pkg_setup +} + +src_unpack() { + pypi_src_unpack + cargo_crate_unpack + cargo_gen_config +} + +src_prepare() { + distutils-r1_src_prepare + + # treat build failures as fatal + sed -i -e '/optional/d' setup.py || die + + if ! use native-extensions; then + # setup.py is only used for setuptools-rust + rm setup.py || die + fi +} + +src_test() { + rm -r fastbencode || die + distutils-r1_src_test +} diff --git a/dev-python/httpx2/Manifest b/dev-python/httpx2/Manifest index e7bdaf9964db..7ff96488307b 100644 --- a/dev-python/httpx2/Manifest +++ b/dev-python/httpx2/Manifest @@ -4,3 +4,4 @@ DIST httpx2-2.13.0.gh.tar.gz 2103962 BLAKE2B 6d940bdcc39f36ddcf50377457931ee9e2c DIST httpx2-2.13.1.gh.tar.gz 2105524 BLAKE2B d9f3c016df2add101bc785582ccd14e5ef400e6b6f6cb8c470a4dce89bfecd73fff210a279cefe0fb316d478f2ba6a10e2579157f18647b769e5c1c520a77e0d SHA512 7be45b81164b24e800c2de533c4c7ef2fdc2cb6e9d24d95287c15ca1efb79eabdce33fcf50b85d288727bea3a9dc6c8365190c587c50ba1d327a36b31c614a8c DIST httpx2-2.14.0.gh.tar.gz 2133484 BLAKE2B 1bf1b2dd3a368dde55f318ba48b14d85397a6181723de5326f818b5421531a0dabea246dc6f240565d4d1881c7e9634d0a3bbc3f27ad4384cee71f952a68f337 SHA512 9b85631b99be2e0ee89ba9ed30405edc71c811e31587c03adf97dc45069e25b3eebad4ef6b83cb4c10c3c6cde9eb0f46d1151c1674b048aaac19b33ec3f05083 DIST httpx2-2.15.1.gh.tar.gz 2095535 BLAKE2B 16c619d85792eb7533d157e8b298869f1ea2e50c63324bf6cf144ef0d06d496154a63a277ceef0dc9f6cba2c36adc43d9dee5b5bba1b4230387526a00ea96080 SHA512 f034a31a762b637d67e280d54c13f355ce5cb6d609dc18bbe3ac510fe8b35207acada045c7e3296e8de89c948348318af6f6bfec6ee6a2a2afbb3a09fd9d80c0 +DIST httpx2-2.16.0.gh.tar.gz 2097489 BLAKE2B c7dc51bcc515b303c8488161bba1ae994050d669a6af40f02ba36d65fe342d92a00ca29e527740e3283b82c2be4995e5bdb628ff710b80fca1b384f552ceb920 SHA512 1c95223e18d394ce75fc3dfae44374198fa9e0dace3d6e375a9d1f64fac80ad4af9146f8e591ed7eb60dc583478a3db1a04e3ba9f2385c2e74d65fa97ee0a51a diff --git a/dev-python/httpx2/httpx2-2.16.0.ebuild b/dev-python/httpx2/httpx2-2.16.0.ebuild new file mode 100644 index 000000000000..157aa4c1af39 --- /dev/null +++ b/dev-python/httpx2/httpx2-2.16.0.ebuild @@ -0,0 +1,103 @@ +# Copyright 2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=hatchling +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 + +# This package combines httpx2 and httpcore2 (because of exact version pin). +DESCRIPTION="The next generation HTTP client (Pydantic fork)" +HOMEPAGE=" + https://pypi.org/project/httpx2/ + https://pypi.org/project/httpcore2/ + https://github.com/pydantic/httpx2/ +" +SRC_URI=" + https://github.com/pydantic/httpx2/archive/v${PV}.tar.gz + -> ${P}.gh.tar.gz +" + +LICENSE="BSD" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" +IUSE="cli" + +RDEPEND=" + >=dev-python/anyio-4.10[${PYTHON_USEDEP}] + >=dev-python/idna-3.18[${PYTHON_USEDEP}] + >=dev-python/h11-0.16[${PYTHON_USEDEP}] + >=dev-python/truststore-0.10[${PYTHON_USEDEP}] + $(python_gen_cond_dep ' + >=dev-python/typing-extensions-4.5.0[${PYTHON_USEDEP}] + ' 3.12) + cli? ( + >=dev-python/click-8.4[${PYTHON_USEDEP}] + =dev-python/pygments-2*[${PYTHON_USEDEP}] + >=dev-python/rich-10[${PYTHON_USEDEP}] + ) +" +BDEPEND=" + dev-python/hatch-fancy-pypi-readme[${PYTHON_USEDEP}] + dev-python/uv-dynamic-versioning[${PYTHON_USEDEP}] + test? ( + >=app-arch/brotli-1.2.0[python,${PYTHON_USEDEP}] + >=dev-python/brotlicffi-1.2.0.0[${PYTHON_USEDEP}] + dev-python/chardet[${PYTHON_USEDEP}] + dev-python/cryptography[${PYTHON_USEDEP}] + dev-python/h2[${PYTHON_USEDEP}] + dev-python/starlette[${PYTHON_USEDEP}] + dev-python/socksio[${PYTHON_USEDEP}] + dev-python/trio[${PYTHON_USEDEP}] + dev-python/trustme[${PYTHON_USEDEP}] + dev-python/typing-extensions[${PYTHON_USEDEP}] + dev-python/uvicorn[${PYTHON_USEDEP}] + dev-python/websockets[${PYTHON_USEDEP}] + >=dev-python/wsproto-1.2[${PYTHON_USEDEP}] + $(python_gen_cond_dep ' + >=dev-python/backports-zstd-1.0.0[${PYTHON_USEDEP}] + ' 3.12 3.13) + ) +" + +EPYTEST_PLUGINS=( anyio flaky pytest-{httpbin,trio} ) +EPYTEST_RERUNS=5 +EPYTEST_XDIST=1 +distutils_enable_tests pytest + +python_compile() { + local -x UV_DYNAMIC_VERSIONING_BYPASS=${PV} + + cd src/httpcore2 || die + distutils-r1_python_compile + cd ../httpx2 || die + distutils-r1_python_compile + cd ../.. || die +} + +python_test() { + local EPYTEST_DESELECT=( + # Internet + tests/httpx2/client/test_proxies.py::test_async_proxy_close + tests/httpx2/client/test_proxies.py::test_sync_proxy_close + # random charset case mismatch + tests/httpx2/models/test_responses.py::test_response_decode_text_using_autodetect + tests/httpx2/client/test_client.py::test_client_decode_text_using_autodetect + tests/httpx2/client/test_client.py::test_client_decode_text_using_explicit_encoding + # *shrug* looks like assertion misfiring + tests/httpx2/test_alias.py::test_alias_finder_handles_top_level_import + tests/httpx2/test_alias.py::test_alias_httpcore_top_level_import + tests/httpx2/test_alias.py::test_alias_top_level_import + ) + + local EPYTEST_IGNORE=() + if ! use cli; then + EPYTEST_IGNORE+=( + tests/httpx2/test_main.py + ) + fi + + epytest tests/{httpcore2,httpx2} +} diff --git a/dev-python/hypothesis-gentoo/Manifest b/dev-python/hypothesis-gentoo/Manifest index 6d6ef0d64df3..01587df49337 100644 --- a/dev-python/hypothesis-gentoo/Manifest +++ b/dev-python/hypothesis-gentoo/Manifest @@ -1,2 +1 @@ -DIST hypothesis_gentoo-3.tar.gz 2201 BLAKE2B 7fc480dbbf559ffca71be6eb5cc5c6186e892ad53776bc5d84ddc649be8d1fe9a181cecacb82806c161078d57a9b286434b921f15cd4b57a6ad6005baa300e05 SHA512 2b3df0b18bf3513de77712e3fdda1c14ac049f957c85fe50da4126146e2a95354079a69ebc3c8af962c30808ba97df21a8084ce0c6c5013c0a45fb8e83e9c02a DIST hypothesis_gentoo-4.tar.gz 2183 BLAKE2B 1037b30bf586eba5fa682bac338296ad43add9ce81422cf2b7a1ed44fb531e65c696f1cf3af04d1d94103162f9273a67165b830e359031083386c70c575c04c2 SHA512 68df70dbcdad5f53de0851730896883549686ad4b50be9663fb8ff4e7d9133e44c44158df2bf1741a91bd8b80fd5e0c064b141ecbda3c068d111ec70bc46d4a6 diff --git a/dev-python/hypothesis-gentoo/hypothesis-gentoo-3.ebuild b/dev-python/hypothesis-gentoo/hypothesis-gentoo-3.ebuild deleted file mode 100644 index 18025c328ad0..000000000000 --- a/dev-python/hypothesis-gentoo/hypothesis-gentoo-3.ebuild +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2025 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -DISTUTILS_USE_PEP517=flit -PYTHON_COMPAT=( python3_{13..14} ) - -inherit distutils-r1 pypi - -DESCRIPTION="Plugin to create 'gentoo' hypothesis profile, disabling health checks" -HOMEPAGE=" - https://github.com/projg2/hypothesis-gentoo/ - https://pypi.org/project/hypothesis-gentoo/ -" - -LICENSE="MIT" -SLOT="0" -KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" - -# No RDEP on hypothesis -- it is only imported in the hypothesis hook - -EPYTEST_PLUGINS=( hypothesis ) -distutils_enable_tests pytest diff --git a/dev-python/hypothesis-gentoo/hypothesis-gentoo-4.ebuild b/dev-python/hypothesis-gentoo/hypothesis-gentoo-4.ebuild index 0c052f77302a..9f042eabb145 100644 --- a/dev-python/hypothesis-gentoo/hypothesis-gentoo-4.ebuild +++ b/dev-python/hypothesis-gentoo/hypothesis-gentoo-4.ebuild @@ -10,7 +10,8 @@ inherit distutils-r1 pypi DESCRIPTION="Plugin to create 'gentoo' hypothesis profile, disabling health checks" HOMEPAGE=" - https://github.com/projg2/hypothesis-gentoo/ + https://gitweb.gentoo.org/proj/hypothesis-gentoo.git/ + https://github.com/gentoo/hypothesis-gentoo/ https://pypi.org/project/hypothesis-gentoo/ " diff --git a/dev-python/inflect/Manifest b/dev-python/inflect/Manifest index 4b9be661ac51..06ffe1addaaf 100644 --- a/dev-python/inflect/Manifest +++ b/dev-python/inflect/Manifest @@ -1 +1,2 @@ DIST inflect-7.5.0.tar.gz 73751 BLAKE2B a17c5cb5bfcd10530f29537335ffd1b4725fb1f53e78ef7ce1f40a3031fa486baae3400878e575c8ce6a77b4953332f051ec65dcba024a14c527301e51079edb SHA512 ee9f69dff451017a2aad2226d8c6ae02b4b7b4bc4d4c49f3efee50d85eeef43c49c6c6ef3e7f30fa2c5ef21e065ff5242140d5a98bc71af17c7e70d9e75e54c1 +DIST inflect-7.6.0.tar.gz 83035 BLAKE2B 618f5d773cae5abdad423fbf8c439c2ddbc56b3dee974550f837c49264f6d14c65e5f50a71770f181494a96c055ded8e2d4dde0c90cc6edbe5dd25d0dca19f36 SHA512 8a768c5a096548c4b7cbde75a07ead131813d4ed5478ecdb6e722cee9ed6e073afaf171d353096872362017e4854aba02400a8e9af05590cc2e4770f4d21d44a diff --git a/dev-python/inflect/inflect-7.6.0.ebuild b/dev-python/inflect/inflect-7.6.0.ebuild new file mode 100644 index 000000000000..1a23334f4b1c --- /dev/null +++ b/dev-python/inflect/inflect-7.6.0.ebuild @@ -0,0 +1,31 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=setuptools +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 pypi + +DESCRIPTION="Correctly inflect words and numbers" +HOMEPAGE=" + https://pypi.org/project/inflect/ + https://github.com/jaraco/inflect/ +" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64 ~arm ~arm64 ~loong ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~arm64-macos ~x64-macos ~x64-solaris" + +RDEPEND=" + dev-python/jaraco-functools[${PYTHON_USEDEP}] + >=dev-python/typeguard-4.0.1[${PYTHON_USEDEP}] + >=dev-python/more-itertools-8.5.0[${PYTHON_USEDEP}] +" +BDEPEND=" + >=dev-python/setuptools-scm-3.4.1[${PYTHON_USEDEP}] +" + +EPYTEST_PLUGINS=() +distutils_enable_tests pytest diff --git a/dev-python/pbs-installer/Manifest b/dev-python/pbs-installer/Manifest index 6165021d6a82..1ba02fb1252c 100644 --- a/dev-python/pbs-installer/Manifest +++ b/dev-python/pbs-installer/Manifest @@ -1,8 +1,6 @@ -DIST pbs_installer-2026.10.1.tar.gz 81120 BLAKE2B f439ff09203b8b628184b587fdcb6e59c296406a22d6dc0d2b49eaab8d55f9621acef6e1d9b533a1be6fb417cf2d8a64d940fc57e4f681f256d4a907df41741c SHA512 2ee04905365d3cc627dd606a64ea4b9be96dc63508f9a86b2f7b944ab4cbb07a09f8336366a0780aecfd2cab90b66416ee01a6b9d7e72581ec7ec3ad0cd594cd -DIST pbs_installer-2026.10.1.tar.gz.provenance 9959 BLAKE2B dbd0e4d6682ea9f692f7c372e1136cfffeaa5dca96c15d56f297359b24bc7fbc12ae54b97750febe8130cc92bb09d2b46054bd942c05ba4d7978d8114769c84f SHA512 f9a8af41b085d413989577c992be521c6237135811b6a2a83af04d1526188b9c4293087a246ef99d61548ec33b169bf5a9dca97b708a7284af8e9ea7e1b12272 DIST pbs_installer-2026.10.13.tar.gz 82375 BLAKE2B a84a7e86b809cc2600b6ac5fd87f214519d3d866c01c421976b3496f6ec05139aeac4707c1b550d20146720211556a62168a7c7a21b8d38e9e95e770e913cceb SHA512 9fd739f02ee1617915670b88527333fdf0ad727640c1475cce60c1855b9065566b7d7fdebc141773f6c037c870950034e710a58e4a909d90a545611581ac1dca DIST pbs_installer-2026.10.13.tar.gz.provenance 9835 BLAKE2B d7990b681753b6bb50fd3e06190d064636021b511e3c013d61a8a7d908d476a1329cdd188f91b9647bf2dd9db15bcb7e397d3364a52d9e18db0daa05ace28b16 SHA512 2b6355efcc9022e5a2275b3cb6fcc6d50d8a0d1db0bd4ad4d7696f577267fd71bb64cef4007acf6003227e7c5a487d8f028c69d7727b1269d672ef550a900c02 +DIST pbs_installer-2026.10.21.tar.gz 84379 BLAKE2B 244a6e13c84704ddc85f82692badf187130c5742274ed1b420a3db0286c5da01c44c1b7d6eb6585b588ef05cb85230bd65d0374d1be443cff478e5890a6c9611 SHA512 0dc4878a9933699be4d530123220e7080772dc6b68dc85fb32431a584cbb33b84154693c5eec4804840259b0aed38d8255e4063ccd112f74efe5eb674b1f915d +DIST pbs_installer-2026.10.21.tar.gz.provenance 9835 BLAKE2B 8c2d73ba0b5e1068d24dbde4ef3a7624cb4190cd6a2c7c4401e545c1f0758fbf9bab595889bce8b8a61538265271447682c441d3f558d85b95b398a84495092a SHA512 4f9e11ac6ba51a5ae1e0e8dc22bdeba242694ea2032b65b379524b309b79df4146403620440b2ac03f097f9cd304a70d47245fe2f6c6868352b3ba91b16a4c3f DIST pbs_installer-2026.10.3.tar.gz 81131 BLAKE2B cc8e52bc72ea442ba1854d9a1f9aeada361def24b145f5cc68885740f8472abf34e48619f780bc11bceb0131a6c1da0dae593df91d1777380554a3059547d23a SHA512 d05194ac0b36f0c510cbe90263bf7b5d4a8cfa7f55a170c128905cd291d026a64a757217a41b1a9c36aed51c353a32ed8c27cfda210f51686d603eee4c18a2fa DIST pbs_installer-2026.10.3.tar.gz.provenance 9994 BLAKE2B eb89d404d27042c7428187a46ddd036d6c4df7730e85519bc0e8fc8aeea2d30e3e34d396e0f5d37ad59a6c7899f9c244ede7da68e32f36a016af9e0f385f9c6e SHA512 4912cdd2730b36c2f015df898c2d230b3e0db2253e27177edf09f4f628146aef721e770b8d5da49a288f554d37e1633956bdfeb97b54d5cc76c47de63ac0cb28 -DIST pbs_installer-2026.9.29.tar.gz 77902 BLAKE2B cf42ecc98a55c39a0a90cc82f8fa082b856cf15e74bca224627defa2113ad6b662d57bcf0dc1dcfb86a9cf8ae87555c2585c029e3260b8b55c4dba163c45bf07 SHA512 108c2d89e94ffaefd297ce372b8d3805f46f7555b53f95863e086b853f23832a342ae5271ecc712612e376687808b6ab8957731b8f317613ad34df8b46ed8998 -DIST pbs_installer-2026.9.29.tar.gz.provenance 10037 BLAKE2B e31a0b3a56944ce3a99b09757664250dde0f79999e6648457a3869ad9647259d10466576f2896e5e456687d18477b52da961b9fe0a94362f5d9416eded202a3b SHA512 a8ffe65f3015b6c17b5b35c3a7c985ced3399a93ca7da74e1a0b8a05a1e022117e618445b0fc51f8c453617a199380b1ba337f913c733ec6525578e56cd0e0aa diff --git a/dev-python/pbs-installer/pbs-installer-2026.10.1.ebuild b/dev-python/pbs-installer/pbs-installer-2026.10.1.ebuild deleted file mode 100644 index ad138f3a6f5d..000000000000 --- a/dev-python/pbs-installer/pbs-installer-2026.10.1.ebuild +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2024-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -DISTUTILS_USE_PEP517=pdm-backend -PYPI_VERIFY_REPO=https://github.com/frostming/pbs-installer -PYTHON_COMPAT=( python3_{13..14} ) - -inherit distutils-r1 pypi - -DESCRIPTION="Installer for Python Build Standalone" -HOMEPAGE=" - https://pypi.org/project/pbs-installer/ - https://github.com/frostming/pbs-installer/ -" - -LICENSE="MIT" -SLOT="0" -KEYWORDS="~amd64 ~arm64 ~x86" - -# httpx is needed to download builds -# zstandard is needed to install them -RDEPEND=" - $(python_gen_cond_dep ' - >=dev-python/backports-zstd-1.0.0[${PYTHON_USEDEP}] - ' 3.12 3.13) - <dev-python/httpx-1[${PYTHON_USEDEP}] - >=dev-python/httpx-0.27.0[${PYTHON_USEDEP}] -" - -EPYTEST_PLUGINS=() -distutils_enable_tests import-check diff --git a/dev-python/pbs-installer/pbs-installer-2026.9.29.ebuild b/dev-python/pbs-installer/pbs-installer-2026.10.21.ebuild index 6d8f79ea02ba..6d8f79ea02ba 100644 --- a/dev-python/pbs-installer/pbs-installer-2026.9.29.ebuild +++ b/dev-python/pbs-installer/pbs-installer-2026.10.21.ebuild diff --git a/dev-python/pylru/Manifest b/dev-python/pylru/Manifest index 8746f0228234..74b210b3f359 100644 --- a/dev-python/pylru/Manifest +++ b/dev-python/pylru/Manifest @@ -1 +1,3 @@ DIST pylru-1.3.1.tar.gz 10582 BLAKE2B 5c176e06195220bb0fcc6d8736bdae146a5129266b2f73d55f268eebb35c0cd537947bd9e326c730a3f3388870e60793c5e4bf1fafcdcc34b7403ce84053e8dd SHA512 9ae5c103f230a80dfe6222075509e446a48c87be9adaa1c7bad6de9ef257b746f9f5e2809cf45509b78b2e0f6b1affe597662071845f8e031f3d7fd5b3a36497 +DIST pylru-1.3.2.tar.gz 10898 BLAKE2B 35b9d84bdcc05655d278bf12282c1414f08111fecdc94271a659a86909587bf1c3dc877c6a8f34891fc9591bee2aacca23b57c8c2d5b7223a12f46cb1a616c54 SHA512 f3c7b8e190af583838db49066c3c1ff8a1cb2fe949376ef958e139bfbaa6c196115703a6f6a1a8928f4523ba1b7206da75c9a3675a69fc73632d566c5cbce450 +DIST pylru-1.3.2.tar.gz.provenance 9590 BLAKE2B 214e03923dc998575effea03fea02af4c78055e16be5af2ebc2ce3f5c1f9b8f68db7d945a2caa54a71f4a60ed2660b695b668718bc03791f7617277ebf3218f1 SHA512 d30bb9e6c3803a7fd6d6293887bc78c5249a2d4a8902abc2453d6dc90ca66374d014422fc9421700f78d04cf76d8048d88ca71642e0e692dce78e99eeb234160 diff --git a/dev-python/pylru/pylru-1.3.2.ebuild b/dev-python/pylru/pylru-1.3.2.ebuild new file mode 100644 index 000000000000..7356feb4dfe2 --- /dev/null +++ b/dev-python/pylru/pylru-1.3.2.ebuild @@ -0,0 +1,24 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=setuptools +PYPI_VERIFY_REPO=https://github.com/jlhutch/pylru +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 pypi + +DESCRIPTION="A least recently used (LRU) cache for Python" +HOMEPAGE=" + https://github.com/jlhutch/pylru/ + https://pypi.org/project/pylru/ +" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~amd64 ~arm ~arm64" + +python_test() { + "${EPYTHON}" test.py || die "tests failed under ${EPYTHON}" +} diff --git a/dev-python/pypdf/Manifest b/dev-python/pypdf/Manifest index e87d1d65c43f..5af33842c8b2 100644 --- a/dev-python/pypdf/Manifest +++ b/dev-python/pypdf/Manifest @@ -8,4 +8,6 @@ DIST pypdf-6.21.0.tar.gz 7088285 BLAKE2B d88a8775a88855a3d3c68ad428d2e3cc71287e2 DIST pypdf-6.21.0.tar.gz.provenance 9668 BLAKE2B ae490f77ca9169550fb48cdbd1b159e0f5bcef6a09a9e838f0d1bfb741dc41fc0fdc96a871f5d287cb4658894ac9014bffc28a1956013350422485b2da1dfeb8 SHA512 619a8e31272eae7b659cf7374a2409db6a287c8b6c7241d51b526f6cf1826f3d4f9f955a117b2eee8d04b1fea8e98d5e83ca61947923a92964a23ce1b84fd6fa DIST pypdf-6.21.1.tar.gz 7088597 BLAKE2B e3cdea6c02aca7c3f16c2531ef3c8250dbd1a0faaaf3895797a9ede3be33b7fd1bc37e2e60c42425868b0c0d5b0c3f1a399ed93095b9d38a5605488ae69136f4 SHA512 55c7729de15efcc03cea0f368f08cc51ad7236f898bc71f972b02345303e544bf4f5084104189a36b3c7f2813434ab400a0034d2e5763582b67ef008923f27bb DIST pypdf-6.21.1.tar.gz.provenance 9621 BLAKE2B 34db44b123f964f28d53fa1e9e9d514196943e2e1e64cba2ba77bbff26a31d51cf9ef2a0f6ddffe162a10f40e45b56184bc569eab8336938dd7fff7b2fa4270b SHA512 9b34342f0cb640e5f7c293f8199d1d6a15a0422f21901651d483237983fa9c6b7d5ab44c0c0a44eecc6366822d4ccaea25903f78d758cf9de4abde69d9868b5a +DIST pypdf-6.21.2.tar.gz 7144571 BLAKE2B c50233a589b15580d36bb26f91570b117af093ee58d6d47a1874fdf5c82771c64c854465189b08f9f08a1e6df530b3cde4ad2d46fe82d91aab925c5fc3660f67 SHA512 3c2ecd1837ee6786139f40a8b9360f70752addfeb2d9696f38f86c5c5d7193842e5ce44372673fa300e913c05fda51f97118cb9833557de1384c836dd2c1e402 +DIST pypdf-6.21.2.tar.gz.provenance 9715 BLAKE2B 1f06c2ee2b6d11d72b6fdd2ee246e13b9c925705089632fff0c3af666f1fd3dc7917166cd0960dae066fac3255abac6eb84c9a1b14919e147ea6a34d93678283 SHA512 bb6b414fa2d278195c4c4c0ea3e431ff309fd6632d8b8d4474ebc624448ddfbdcc0770aecba814e60f48ecf5fda9d0fca9e5a1bc0e872f87706601e9dbc72e19 DIST pypdf-sample-files-818dc013ad1f537198e9fcfae8a6b0dffe25ffa3.gh.tar.gz 11850954 BLAKE2B fbd0af91de44f2d224b3266727f0dfc462efb4d23176f4ebff147cbfa37337d891859c468b545d0c00796048584e6b621dc622d667e3d45c0b89b92f29ca3030 SHA512 bc601bfaf17f31c6358f594eaf879479e97028d7f5e5825c309a5d1eb843bfea5080c9e151287a0d915ecbc3f4d4755e326ac8fa1ceeb696c76b9f5ecc0d7e9f diff --git a/dev-python/pypdf/pypdf-6.21.2.ebuild b/dev-python/pypdf/pypdf-6.21.2.ebuild new file mode 100644 index 000000000000..716a7acd1c4e --- /dev/null +++ b/dev-python/pypdf/pypdf-6.21.2.ebuild @@ -0,0 +1,66 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=flit-core +PYPI_VERIFY_REPO=https://github.com/py-pdf/pypdf +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 pypi + +SAMPLE_COMMIT=818dc013ad1f537198e9fcfae8a6b0dffe25ffa3 +DESCRIPTION="Python library to work with PDF files" +HOMEPAGE=" + https://pypi.org/project/pypdf/ + https://github.com/py-pdf/pypdf/ +" +SRC_URI+=" + test? ( + https://github.com/py-pdf/sample-files/archive/${SAMPLE_COMMIT}.tar.gz + -> ${PN}-sample-files-${SAMPLE_COMMIT}.gh.tar.gz + ) +" + +LICENSE="BSD" +SLOT="0" +KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc ~ppc64 ~riscv ~x86" + +BDEPEND=" + test? ( + >=dev-python/cryptography-3.1[${PYTHON_USEDEP}] + dev-python/fonttools[${PYTHON_USEDEP}] + >=dev-python/pillow-8.0.0[jpeg,jpeg2k,tiff,zlib,${PYTHON_USEDEP}] + dev-python/pyyaml[${PYTHON_USEDEP}] + media-libs/jbig2dec[png] + ) +" + +EPYTEST_PLUGINS=( pytest-timeout ) +EPYTEST_XDIST=1 +distutils_enable_tests pytest + +EPYTEST_DESELECT=( + # probably requires extra deps + tests/test_appearance_stream.py::test_appearance_stream_rtl + # speed test, uses tons of memory + tests/test_filters.py::test_flatedecode__decode_png_prediction__speed + # apparently we get half the limit somehow? + tests/generic/test_data_structures.py::test_dictionary_object__read_from_stream__missing_length__limit + tests/generic/test_data_structures.py::test_dictionary_object__read_from_stream__read_unsized__limit + tests/test_configuration.py::test_apply_configuration + tests/test_configuration.py::test_apply_configuration__nested + tests/test_configuration.py::test_get_configuration + tests/test_configuration.py::test_overwrite_configuration +) + +src_unpack() { + default + if use test; then + mv "sample-files-${SAMPLE_COMMIT}" "${S}"/sample-files || die + fi +} + +python_test() { + epytest -o addopts= -m "not enable_socket" +} diff --git a/dev-python/python-dateutil/Manifest b/dev-python/python-dateutil/Manifest index 82d26649de2c..75b164019f13 100644 --- a/dev-python/python-dateutil/Manifest +++ b/dev-python/python-dateutil/Manifest @@ -1 +1,3 @@ DIST python-dateutil-2.9.0.post0.tar.gz 342432 BLAKE2B b3469ff9900afd98f474f162083570c28ac93378efc772b5f365fa0d5d4354a96867a024065adc430b71d7bde6909195ac4ebe3ad1d17e638fded0b4b40f9954 SHA512 f76522de0ff21547327eaf6966e80a15c57f8f92588d520eabd354a732e5c4b51d9c3ac5effd9eaa6dd451d1bce329a54a3f4c6bf4f1bd08ff06b0305c994e5a +DIST python_dateutil-3.0.0.tar.gz 271066 BLAKE2B e544a0b27bd0aa7454a3009ee656bfb3ba585e8b3dff09387ff36ce0fd85e6fb5aee9b59b81a7a6858ef60978d47a7e59e5c0a942278618a53b0819aab76a79f SHA512 638dcea7a840163bfd79d174b3ccd43254d712b92c1739dc93f937ecce122994ff25d19a579705cd6ba77336901e879ab4c7f68ca599b5576656a3b6783c2c69 +DIST python_dateutil-3.0.0.tar.gz.provenance 9820 BLAKE2B 07fde7908756488b73ee54def14c4a9078cb0874b94d663b12c41bc09099431795879af08f9ca89051874fdf9ad78b432a274703cb097d65cec9f29c17318765 SHA512 12931ab7f8c33f03339fae727acb592ed858940f8bb13b8512955ea62c1d9fa6266c597e2df24e149d68fcd4356cdb321fbb54640bb6a049152ea82f4213fef8 diff --git a/dev-python/python-dateutil/python-dateutil-3.0.0.ebuild b/dev-python/python-dateutil/python-dateutil-3.0.0.ebuild new file mode 100644 index 000000000000..1b282f70b2ad --- /dev/null +++ b/dev-python/python-dateutil/python-dateutil-3.0.0.ebuild @@ -0,0 +1,59 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=setuptools +PYPI_VERIFY_REPO=https://github.com/dateutil/dateutil +PYTHON_FULLY_TESTED=( python3_{12..15} python3_{14..15}t ) +# TODO: restore pypy3.12 somehow +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 pypi + +DESCRIPTION="Extensions to the standard Python datetime module" +HOMEPAGE=" + https://dateutil.readthedocs.io/ + https://pypi.org/project/python-dateutil/ + https://github.com/dateutil/dateutil/ +" + +LICENSE="BSD" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~arm64-macos ~x64-macos" + +RDEPEND=" + >=dev-python/six-1.5[${PYTHON_USEDEP}] + dev-python/tzdata[${PYTHON_USEDEP}] +" +BDEPEND=" + dev-python/setuptools-scm[${PYTHON_USEDEP}] + test? ( + dev-python/attrs[${PYTHON_USEDEP}] + dev-python/freezegun[${PYTHON_USEDEP}] + $(python_gen_cond_dep ' + dev-python/hypothesis[${PYTHON_USEDEP}] + ' "${PYTHON_FULLY_TESTED[@]}") + ) +" + +PATCHES=( + "${FILESDIR}/python-dateutil-2.9.0-no-pytest-cov.patch" +) + +EPYTEST_PLUGINS=() +distutils_enable_tests pytest + +python_test() { + local EPYTEST_IGNORE=() + local EPYTEST_PLUGINS=( "${EPYTEST_PLUGINS[@]}" ) + if has_version "dev-python/hypothesis[${PYTHON_USEDEP}]"; then + EPYTEST_PLUGINS+=( hypothesis ) + else + EPYTEST_IGNORE+=( + tests/property + ) + fi + + epytest +} diff --git a/dev-python/shtab/Manifest b/dev-python/shtab/Manifest index a2594b11f23a..fed2b87b91b1 100644 --- a/dev-python/shtab/Manifest +++ b/dev-python/shtab/Manifest @@ -1,6 +1,4 @@ -DIST shtab-1.10.0.tar.gz 56370 BLAKE2B e4312d778aced10e250fa6a50717ed0aac8e13351f9fc6740019689ff893e9665bcbb23fa9e4d18305fc7b1ef5ce0fe1e283434eec5992cb74b8f86956aef90a SHA512 831b2913339e1f6861bebb6f60528d5aeadd2ffbaf4725993dd47cd628201b2c79e781cdbeb630bed17513ea32e3db99dad3457b958392009206eefe0a6c68e8 -DIST shtab-1.10.0.tar.gz.provenance 9014 BLAKE2B f1adca976da5202048c371d19225fbdf45204cedae04b6a6b72f90cdd6c1348e812fbad32299469d35d86155d2998e2daf742aa8b0a6459fc689b09a26c60d83 SHA512 23bc47830f5dc2f3cef04d243c325efacc117d162ff092c20b856ac5c9eba97efb96ac7b586b4038d349ad0fff3fd83ddb0fc90ab18eb68c36a463182ba7a1b8 -DIST shtab-1.11.0.tar.gz 58653 BLAKE2B 8e93804da4bd3122e7e0b4618a89bb775f15c50759008e2e5546df7ddff5dd8f9ac0951a8df71d9a8ff0db4b6efa3cc2d160c369f5ef40923de975261062ddd3 SHA512 eac92034b92297acafc57c6d2ad6d40084294fd7ee05392fbad3657185f78848c39f4cb73a6fff034dec0df886bfcc552a6367c50e58cf779b74f679d04ffd8b -DIST shtab-1.11.0.tar.gz.provenance 9263 BLAKE2B 938172b2866834a73845731006fb74279f372b85290beb651d5ee5e35ef80b924b222b17bf1fbc30fc2e73d93d68f5c2584b83ea3d08ee64865febf9c3f7c783 SHA512 90f85d016c37fc3411f9e8a124866ff6096b7833adebca3b16fdedc6eea12020784459ed3508a5c0e9bf169f1d7a2872eebcfccac8d6a43307f16c49bd559c28 DIST shtab-1.12.1.tar.gz 66195 BLAKE2B dd884496a7c7feb07b91913376776c379d6e475f71109dc335f1d0737de0cd6737b19179d050bb01c1457ac44e0e53e8a41d2708ef333cf631fd3938fa9e324a SHA512 3b7efd1c8e5985912eab61a6ceb7609031e0b1a29c5fc28d809db36c87774ed3b32cc8cd816b71cf306015e250012c400c456b1138cf69666f2aca568e5b41b6 DIST shtab-1.12.1.tar.gz.provenance 9310 BLAKE2B 97a746947bb7b03c06d2692c7351ea4fd1a12616b07cec67d6ee89c0f788b4a7d064afb97c47b8dbd0e627aac370cd9a594b912b31e0257bebb1708f4a90896b SHA512 6f6cdf627b74ef55a85ff27abb745a1e6ef63d48df99645cac74c335416d2aa4caeaca077242928401fe2bd5c4a79ffc20a0fdf6a882c74c409cb3f52982b393 +DIST shtab-1.13.0.tar.gz 67655 BLAKE2B 770ac69b625bf19fd3adcda3ab03c95acefb81940d0b6a7321da53cfa6b2bc43026220bc2704f6769448a32a61bf8e14239b700bd1f5b72a92ec2de8ada2e981 SHA512 21b241a07d0bdc7c3a89ae45bc0ab53fc6d5c9515cd14bcfb1167e6d8349d8ccd7027b519d3ee85c4c4ec5a6f8f0db7eed436c145832c2fe86e75926441379df +DIST shtab-1.13.0.tar.gz.provenance 9480 BLAKE2B a9f4d0dcf37d781dd761a2b0d32240d573fb48e02b2a2c94f4423b986f918b4c264c6f2cf53572889260629bec8271119764cd5391cc0819c3efba005b75fd7e SHA512 9948247769709a786bce6cfe45a5c84f06842b32f6557b4201ab2947cd6066d82c57d05d5438e7b01d2a25285252afbe6a08d29eae49964a6d429ec028306587 diff --git a/dev-python/shtab/shtab-1.10.0.ebuild b/dev-python/shtab/shtab-1.10.0.ebuild deleted file mode 100644 index 54cd635fb7e8..000000000000 --- a/dev-python/shtab/shtab-1.10.0.ebuild +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2024-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -DISTUTILS_USE_PEP517=setuptools -PYPI_VERIFY_REPO=https://github.com/tqdm/shtab -PYTHON_COMPAT=( python3_{13..14} ) - -inherit distutils-r1 pypi - -DESCRIPTION="Automagic shell tab completion for Python CLI applications" -HOMEPAGE=" - https://github.com/tqdm/shtab/ - https://pypi.org/project/shtab/ -" - -LICENSE="Apache-2.0" -SLOT="0" -KEYWORDS="amd64 ~arm arm64 ~loong ~ppc64 ~riscv x86" - -BDEPEND=" - dev-python/setuptools-scm[${PYTHON_USEDEP}] -" - -EPYTEST_PLUGINS=() -distutils_enable_tests pytest - -python_test() { - epytest -o addopts= -} diff --git a/dev-python/shtab/shtab-1.11.0.ebuild b/dev-python/shtab/shtab-1.13.0.ebuild index 54cd635fb7e8..6799197f5470 100644 --- a/dev-python/shtab/shtab-1.11.0.ebuild +++ b/dev-python/shtab/shtab-1.13.0.ebuild @@ -17,10 +17,13 @@ HOMEPAGE=" LICENSE="Apache-2.0" SLOT="0" -KEYWORDS="amd64 ~arm arm64 ~loong ~ppc64 ~riscv x86" +KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc64 ~riscv ~x86" BDEPEND=" dev-python/setuptools-scm[${PYTHON_USEDEP}] + test? ( + dev-python/click[${PYTHON_USEDEP}] + ) " EPYTEST_PLUGINS=() diff --git a/dev-python/soupsieve/Manifest b/dev-python/soupsieve/Manifest index d2c311bfa9a5..dde29782db88 100644 --- a/dev-python/soupsieve/Manifest +++ b/dev-python/soupsieve/Manifest @@ -3,3 +3,4 @@ DIST soupsieve-2.9.2.gh.tar.gz 157707 BLAKE2B b57eef6a67ddf33c03bdd418a22229877b DIST soupsieve-3.0.1.gh.tar.gz 119766 BLAKE2B 01a1f300d336182a3e055fcad4ee5d321c311ace28363f5c800e36cc5c7c813b66786b3e8f464c90511be236ff64709122759a5332c7127f7d4c11fc27dab461 SHA512 4609a5c9701e1b51a1dfa447bb33993a2ce3a781c9f0b8fa1fdee1fed3bdd05b9e79f25967d82f64252be8c66b823f652365236dcfd4a82c5b835a5fc8504955 DIST soupsieve-3.0.2.gh.tar.gz 119863 BLAKE2B 0a3f4628f5727e1b757ab7c91ceb95aace7be3e8606d6bd3d965444203dcd595a4acd8b4b45ede24da4bc6ff52843942c45c675082d25c1b6bff43f4dde7f7b3 SHA512 891bdf03ffc4497141aecc888ee5c55b3d138031e24a79562b38d2c17bfc364fd6025fcd15210bdce238b5534882ef9088e1b6f2e887ed09d86ee432a8177aa5 DIST soupsieve-3.0.3.gh.tar.gz 120295 BLAKE2B 65a48c2a450c2026cb60c404ea6bd375e02af65d7f57cedb3931d7349ad1178fc78068d6ba803b2fad4c37f8cfcedecf1cd41f1280bf472ed76fab596a73e274 SHA512 64dd71a019cad6e7279f09c4342aa0d0a8cf4fbc7ed0ab12ae7fb3bb0e9e6ba22b2f2c42c44738322a2c17ed3ec8bfb1bd01bc20f62b384edd6804a9fe1e7cdb +DIST soupsieve-3.0.4.gh.tar.gz 123598 BLAKE2B cb2bcda9aa825fea667a8099a6996135dd5ff579d68401c01f6fa3b92400b24bcc8e383d82b17ccb90844251fc20fc51da60238c99abbbbe920e1ec4eba7bc2e SHA512 00b35d354395668c55304dfbc83172415b1d553e29ad4fe95d67f47607f86567127b7109fde93cd3251d8dcc0803eb42a40146e5e03249356c15c6646bb9b377 diff --git a/dev-python/soupsieve/soupsieve-3.0.4.ebuild b/dev-python/soupsieve/soupsieve-3.0.4.ebuild new file mode 100644 index 000000000000..a386cbdf6652 --- /dev/null +++ b/dev-python/soupsieve/soupsieve-3.0.4.ebuild @@ -0,0 +1,34 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=hatchling +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 + +DESCRIPTION="A modern CSS selector implementation for BeautifulSoup" +HOMEPAGE=" + https://github.com/facelessuser/soupsieve/ + https://pypi.org/project/soupsieve/ +" +SRC_URI=" + https://github.com/facelessuser/soupsieve/archive/${PV}.tar.gz + -> ${P}.gh.tar.gz +" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" + +BDEPEND=" + test? ( + dev-python/beautifulsoup4[${PYTHON_USEDEP}] + dev-python/lxml[${PYTHON_USEDEP}] + dev-python/html5lib[${PYTHON_USEDEP}] + ) +" + +EPYTEST_PLUGINS=() +distutils_enable_tests pytest diff --git a/dev-python/sphinx-copybutton/sphinx-copybutton-0.5.2.ebuild b/dev-python/sphinx-copybutton/sphinx-copybutton-0.5.2.ebuild index f149ea0b05a0..21e4b99e5df7 100644 --- a/dev-python/sphinx-copybutton/sphinx-copybutton-0.5.2.ebuild +++ b/dev-python/sphinx-copybutton/sphinx-copybutton-0.5.2.ebuild @@ -1,4 +1,4 @@ -# Copyright 2022-2025 Gentoo Authors +# Copyright 2022-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/dev-python/sphinx/Manifest b/dev-python/sphinx/Manifest index 9004e97b29a8..4fa9dddaa082 100644 --- a/dev-python/sphinx/Manifest +++ b/dev-python/sphinx/Manifest @@ -1,2 +1,3 @@ DIST sphinx-9.1.0.gh.tar.gz 8840512 BLAKE2B 54f680ff84b43b3508c1c9208ff41dc9fba1ae8f8dd608287511fb16e28dc7fa72419165b41f556af266e00c737cd8cc23a6617776fa88f0dbb8379fbbf59397 SHA512 3b17a3d4123d19ed47c7ab7786edcbe7eb0566f298d9de22efb3f4f0476b6c0ddf858b57812f2dac718458d98ddc742a443b7c94771693a1ed3bf868850639b1 DIST sphinx-9.1.1.gh.tar.gz 8840940 BLAKE2B 786a8f87dd28dae7ef5799fd39ff5ae2bbcdd6f2b100aa7d9f5a9c1c4b732fad1866431436013c6d540426e6ca5d76c4856479426e21a7aeb31e79a79d04d495 SHA512 b5b82e8f9694ff5b7238ed357ddb0bd2f938f9cefd366859af9d92ec55cc40892a56259bd87dddacfb91500c73d628e8aa97ddd5ea0fe53509ec372952f28f27 +DIST sphinx-9.2.0.gh.tar.gz 8876769 BLAKE2B 085b97cb8d33d47974a44bf14bd54ef6944b4880f0f6609b7d653b34df028d553ce5288efae7306f0a1a76cd2e79a950e8e5c29d4579c14efcd1ef7a62bf1116 SHA512 12f0339ca56893e2eb66c18816cb38e52eb5631053ce45693b8d9ee03e9d1249bf3a19c92cb3b4c3826615338582445600721755a2c449d85c78905073d7e14e diff --git a/dev-python/sphinx/sphinx-9.2.0.ebuild b/dev-python/sphinx/sphinx-9.2.0.ebuild new file mode 100644 index 000000000000..f28b6aaac66a --- /dev/null +++ b/dev-python/sphinx/sphinx-9.2.0.ebuild @@ -0,0 +1,104 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=flit-core +PYTHON_COMPAT=( python3_{13..14} ) +PYTHON_REQ_USE="threads(+)" + +inherit distutils-r1 + +MY_P=${P/_} +DESCRIPTION="Python documentation generator" +HOMEPAGE=" + https://www.sphinx-doc.org/ + https://github.com/sphinx-doc/sphinx/ + https://pypi.org/project/Sphinx/ +" +SRC_URI=" + https://github.com/sphinx-doc/sphinx/archive/v${PV/_}.tar.gz + -> ${MY_P}.gh.tar.gz +" +S=${WORKDIR}/${MY_P} + +LICENSE="BSD-2" +SLOT="0" +if [[ ${PV} != *_rc* ]]; then + KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" +fi +IUSE="doc latex" + +RDEPEND=" + >=dev-python/alabaster-1.0[${PYTHON_USEDEP}] + >=dev-python/babel-2.18[${PYTHON_USEDEP}] + >=dev-python/docutils-0.21[${PYTHON_USEDEP}] + >=dev-python/imagesize-1.3[${PYTHON_USEDEP}] + >=dev-python/jinja2-3.1[${PYTHON_USEDEP}] + >=dev-python/packaging-25.0[${PYTHON_USEDEP}] + >=dev-python/pygments-2.19[${PYTHON_USEDEP}] + >=dev-python/requests-2.32.5[${PYTHON_USEDEP}] + >=dev-python/roman-numerals-3.0[${PYTHON_USEDEP}] + >=dev-python/snowballstemmer-3.0.1[${PYTHON_USEDEP}] + >=dev-python/sphinxcontrib-applehelp-1.0.7[${PYTHON_USEDEP}] + >=dev-python/sphinxcontrib-devhelp-1.0.6[${PYTHON_USEDEP}] + >=dev-python/sphinxcontrib-htmlhelp-2.0.6[${PYTHON_USEDEP}] + >=dev-python/sphinxcontrib-jsmath-1.0.1[${PYTHON_USEDEP}] + >=dev-python/sphinxcontrib-qthelp-1.0.6[${PYTHON_USEDEP}] + >=dev-python/sphinxcontrib-serializinghtml-1.1.9[${PYTHON_USEDEP}] + latex? ( + dev-texlive/texlive-latexextra + dev-texlive/texlive-luatex + app-text/dvipng + ) +" +BDEPEND=" + doc? ( + dev-python/sphinxcontrib-websupport[${PYTHON_USEDEP}] + media-gfx/graphviz + ) + test? ( + app-text/dvipng + >=dev-python/cython-3.1[${PYTHON_USEDEP}] + >=dev-python/defusedxml-0.7.1[${PYTHON_USEDEP}] + dev-python/pytest-rerunfailures[${PYTHON_USEDEP}] + >=dev-python/setuptools-80.0[${PYTHON_USEDEP}] + >=dev-python/typing-extensions-4.15[${PYTHON_USEDEP}] + dev-texlive/texlive-fontsextra + dev-texlive/texlive-latexextra + dev-texlive/texlive-luatex + virtual/imagemagick-tools[jpeg,png,svg] + ) +" + +EPYTEST_PLUGINS=() +EPYTEST_RERUNS=5 +# EPYTEST_XDIST breaks stuff +distutils_enable_tests pytest + +python_prepare_all() { + local PATCHES=( + "${FILESDIR}/sphinx-3.2.1-doc-link.patch" + ) + + distutils-r1_python_prepare_all + + # disable internet access + sed -i -e 's:^intersphinx_mapping:disabled_&:' \ + doc/conf.py || die + # unpin dependencies + sed -i -e 's:,<[0-9.]*::' pyproject.toml || die +} + +python_compile_all() { + # we can't use distutils_enable_sphinx because it would + # introduce a dep on itself + use doc && build_sphinx doc +} + +python_test() { + mkdir -p "${BUILD_DIR}/sphinx_tempdir" || die + local -x SPHINX_TEST_TEMPDIR="${BUILD_DIR}/sphinx_tempdir" + + epytest +} diff --git a/dev-python/sphinxcontrib-autoprogram/sphinxcontrib-autoprogram-0.1.9.ebuild b/dev-python/sphinxcontrib-autoprogram/sphinxcontrib-autoprogram-0.1.9.ebuild index 356f51dd140f..8f8f488deca3 100644 --- a/dev-python/sphinxcontrib-autoprogram/sphinxcontrib-autoprogram-0.1.9.ebuild +++ b/dev-python/sphinxcontrib-autoprogram/sphinxcontrib-autoprogram-0.1.9.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/dev-python/sphinxcontrib-log-cabinet/sphinxcontrib-log-cabinet-1.0.1-r1.ebuild b/dev-python/sphinxcontrib-log-cabinet/sphinxcontrib-log-cabinet-1.0.1-r1.ebuild index a645f154a99a..d07aa4f802dd 100644 --- a/dev-python/sphinxcontrib-log-cabinet/sphinxcontrib-log-cabinet-1.0.1-r1.ebuild +++ b/dev-python/sphinxcontrib-log-cabinet/sphinxcontrib-log-cabinet-1.0.1-r1.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/dev-python/sqlalchemy/Manifest b/dev-python/sqlalchemy/Manifest index c145e350d5d0..94add9e2fb05 100644 --- a/dev-python/sqlalchemy/Manifest +++ b/dev-python/sqlalchemy/Manifest @@ -7,3 +7,4 @@ DIST sqlalchemy-2.1.1.tar.gz 10503203 BLAKE2B 18c76e58bb243bc9fff745f3c04435a407 DIST sqlalchemy-2.1.3.tar.gz 10525778 BLAKE2B 7093aecf1b69b410852a6926d0cb4e4c2e0b7b091c9eda4cd6e5a224dbdbe1a29f17ed350abfdf5d0565a2dcfa7f1a00ea31f3c5e553b70652b56be6fac8ad7b SHA512 abce5dac76e71c7bd059da30b0b61291f5fb1f49417fe1c02220ea4903b142a2b58ec2d2c288b65113420e0ddf9e7b09b89a38a8093686f9ee2d872b5e1d791d DIST sqlalchemy-2.1.4.tar.gz 10544216 BLAKE2B 70414f34f9b1007ca105873c2c7c1789f44656340a6ed6d9fbb28a276f6db0afef69e21fb90abd0f325d2f53a4475f45a7da87db670fc600ae904ab20ef66647 SHA512 fbdafec12fb50654ef73eec69a4bbe885a5fce15cf4a48a26ee322a7e758a6aacab5efb441cff0bbd921065b85dab45b327818c01a1dc856294cf7781a2c1e60 DIST sqlalchemy-2.1.5.tar.gz 10569593 BLAKE2B 8c483c039900d5535af9ed430d918d9421b81627bcb29b4c7ac32ac1249ff75d08ed76e83c62034940a27a520ec9af049f4364ed21a3b247a5a1c145c0c98cb1 SHA512 2961597ee9bf72fa6cfd1a325a74d5ca1f52066483b6cf343a88c90bdd3084e0e87bcc877743528bbfd1808f11a37c37b4d6cb699a3478840879eb2a010a5b3a +DIST sqlalchemy-2.1.6.tar.gz 10589700 BLAKE2B 7d0ca8d28d9fa29a9214af70bc7adfa4ee5b5400fe2fc3ae262f55ec9b3962367d2b1afb830939fab8bf901ad7faf78b562cdd70660d49c961a5963fef7b574c SHA512 f575cba9dfb34482dfe9edf64963fc1228be1eb315552c9c37f9f2a47f9e7fdb224a54582c6e41a88d3e58574d06c6b17172e98ebf7543adc5a645790594def7 diff --git a/dev-python/sqlalchemy/sqlalchemy-2.1.6.ebuild b/dev-python/sqlalchemy/sqlalchemy-2.1.6.ebuild new file mode 100644 index 000000000000..d9cf5b3c5b56 --- /dev/null +++ b/dev-python/sqlalchemy/sqlalchemy-2.1.6.ebuild @@ -0,0 +1,103 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_EXT=1 +DISTUTILS_USE_PEP517=setuptools +PYPI_PN=SQLAlchemy +PYTHON_COMPAT=( python3_{13..14} ) +PYTHON_REQ_USE="sqlite?" + +inherit distutils-r1 optfeature pypi + +DESCRIPTION="Python SQL toolkit and Object Relational Mapper" +HOMEPAGE=" + https://www.sqlalchemy.org/ + https://pypi.org/project/SQLAlchemy/ + https://github.com/sqlalchemy/sqlalchemy/ +" + +LICENSE="MIT" +SLOT="0" +if [[ ${PV} != *_beta* ]]; then + KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-macos ~x64-solaris" +fi +IUSE="examples +sqlite test" + +RDEPEND=" + >=dev-python/typing-extensions-4.6.0[${PYTHON_USEDEP}] +" +BDEPEND=" + dev-python/cython[${PYTHON_USEDEP}] + test? ( + $(python_gen_impl_dep sqlite) + ) +" + +EPYTEST_PLUGINS=() +EPYTEST_RERUNS=5 +EPYTEST_XDIST=1 +distutils_enable_tests pytest + +python_test() { + local EPYTEST_IGNORE=( + test/ext/mypy/test_mypy_plugin_py3k.py + test/typing/test_mypy.py + # hardcode call counts specific to Python versions + test/aaa_profiling + ) + local EPYTEST_DESELECT=( + # warning tests are unreliable + test/base/test_warnings.py + # TODO: flaky? xdist? + test/base/test_concurrency_py3k.py::TestAsyncioCompat::test_await_fallback_no_greenlet + ) + local sqlite_version=$(sqlite3 --version | cut -d' ' -f1) + case ${EPYTHON} in + python3.15) + EPYTEST_DESELECT+=( + # repr() changes + test/orm/declarative/test_dc_transforms.py::DCTransformsTest::test_basic_constructor_repr_base_cls + test/orm/declarative/test_dc_transforms_future_anno_sync.py::DCTransformsTest::test_basic_constructor_repr_base_cls + # exception message changes + test/engine/test_processors.py::PyDateProcessorTest::test_no_string + ) + ;; + esac + if ! has_version "dev-python/greenlet[${PYTHON_USEDEP}]"; then + EPYTEST_DESELECT+=( + test/ext/asyncio/test_engine_py3k.py::TextSyncDBAPI::test_sync_driver_execution + test/ext/asyncio/test_engine_py3k.py::TextSyncDBAPI::test_sync_driver_run_sync + "test/engine/test_pool.py::PoolEventsTest::test_checkin_event_gc[False-True]" + "test/engine/test_pool.py::PoolEventsTest::test_checkin_event_gc[True-True]" + "test/engine/test_pool.py::PoolEventsTest::test_checkin_event_gc[has_terminate-is_asyncio]" + "test/engine/test_pool.py::PoolEventsTest::test_checkin_event_gc[not_has_terminate-is_asyncio]" + "test/engine/test_pool.py::QueuePoolTest::test_userspace_disconnectionerror_weakref_finalizer[True-_exclusions0]" + "test/engine/test_pool.py::QueuePoolTest::test_userspace_disconnectionerror_weakref_finalizer[True]" + ) + fi + + # upstream's test suite is horribly hacky; it relies on disabling + # the warnings plugin and turning warnings into errors; this also + # means that any DeprecationWarnings from third-party plugins cause + # everything to explode + epytest --reruns-delay=2 -m "not gc_intensive and not timing_intensive and not mypy" +} + +python_install_all() { + if use examples; then + docompress -x "/usr/share/doc/${PF}/examples" + dodoc -r examples + fi + + distutils-r1_python_install_all +} + +pkg_postinst() { + optfeature "asyncio support" dev-python/greenlet + optfeature "MySQL support" \ + dev-python/mysqlclient \ + dev-python/pymysql + optfeature "postgresql support" dev-python/psycopg:2 +} diff --git a/dev-python/tornado/Manifest b/dev-python/tornado/Manifest index 62d409037691..ffdb1c51e433 100644 --- a/dev-python/tornado/Manifest +++ b/dev-python/tornado/Manifest @@ -1,6 +1,4 @@ DIST tornado-6.5.10.tar.gz 537910 BLAKE2B 6ec1bcba8c7dc2175206528b97be64e6dbbfb51ae6c9cdd1d0dc0bdc4eff55c1a9a0ebe9c700ea18e88fef3197b0d7efe1812fab5196dfab5cdfd71b6d1568bc SHA512 0a3a38162a91d7d82f0d790d37788c5761bd9663af0356641bd6bd0ec5f6d490e5013965db1f91f28959d925127640cffbd4b661415b43ca5b50ee812841f445 DIST tornado-6.5.10.tar.gz.provenance 9454 BLAKE2B 4a8faa5670534337aead096a06c10b3f77c0ba8dc13e70d94dc92fae59a28e072333ef37635cf2db12fe20de17e175e96da6b575e73c8426103320b6a58f71ac SHA512 c03ebd606a6543a816ffff74217061702ffbdfbeb209ac7541f50d6beca4caf8568e32b40712a3b9ef567f15d50453fe789a84f6e4c2420201216a50776c6951 -DIST tornado-6.5.8.tar.gz 520493 BLAKE2B e48ca20fe0551b8536162071e0b9267ccd167df617c25f930481668e733c656904109014e65462e561d47acc557cfe32f86a422e8f7e840b4262acbafb2b720a SHA512 88c096ca30357644c07b6013cd514b5e64135b26a77d9ff17640951632d6f68c9a68d7fa80224743f7900fbf5ddde838088bd9838395651d87d876c8d1d1a2fe -DIST tornado-6.5.8.tar.gz.provenance 9416 BLAKE2B 1f4d1d9289f8aa8fdde1dda19454da15e9056111f0225e0701eb385ef42b0fae26a6bb9dc5df3ffb9796e4d805db5aa7406aa466ec9389802df1033cdb5231a6 SHA512 b1c364aed55ecb3cbab7343de9ebfaa181579ca81a57e498953c7d0e657bd8b08ee9fb6e5f72d8205f379fbd25237054bfecdb9982fc973e4bb55a00a0411444 -DIST tornado-6.5.9.tar.gz 536145 BLAKE2B 13071c50a8f0701d750f26aa5fb3669ee5c78c4d249d570b519a672d8fdfa76d38f90633ad21fa1c7d551c50c5f6d0dab53ebb694685d52f1cc1269cb0728564 SHA512 e497f0cacca0ca0d34b65117a72e9ebe0056f77ced09eb5d200dea24da8c28182d5704d1b17510fc139841718fce6a19c7f154d416e59292f4236eb717a0a75c -DIST tornado-6.5.9.tar.gz.provenance 9473 BLAKE2B 9c58b5c0753dee0f9e111cd5007512fffc91964cad3eeed7e11758aff96f239692492f35f220f08ef10bfd249013d3d1c550a4f79a3669c86abd5ef5fd2922a7 SHA512 20cb384accabf2c8659f90446c2f39d3b3535662114e0707521a870e291879da15a8c6c0fac61a6e64a26e4f64ccfc8d8f1dd3db60717b3121a52263422fb061 +DIST tornado-6.6.tar.gz 617998 BLAKE2B 988b37f28f57a5aee35e068d93bbbf3b28ab9c52bc830ecf9474e48b47b68c944a1fac7309531f9da1cd253fd49eae079c01b4e8bc2896a59fdf77299ef16c7d SHA512 8c679f13e158f2cd9a7106e057d74c8a6a8c3ed53bfd0fc7963f876e814b0604db3a75e55de376dbf4c644130de9a7d9d64145fbce276533a05787dec1e60f79 +DIST tornado-6.6.tar.gz.provenance 9611 BLAKE2B 71c82dae3d52f8fcce94c75054192a7b4fa47d507a1161892cd15d373ea3c6efa810419b3381d9942982e635e8df778992d0cecee0c48aab263c850eb94704dc SHA512 466427fad2a37ee372bf268e899745b031a6c1c16463d42e3a3f74a2aa9207b21d62164cdc476c5523b8e0dece8c39847753a6f47a97d69442cddc257f331a70 diff --git a/dev-python/tornado/tornado-6.5.8.ebuild b/dev-python/tornado/tornado-6.5.8.ebuild deleted file mode 100644 index f73a5bc3e333..000000000000 --- a/dev-python/tornado/tornado-6.5.8.ebuild +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -DISTUTILS_EXT=1 -DISTUTILS_USE_PEP517=setuptools -PYPI_VERIFY_REPO=https://github.com/tornadoweb/tornado -PYTHON_COMPAT=( python3_{13..14} ) -PYTHON_REQ_USE="threads(+)" - -inherit distutils-r1 pypi - -DESCRIPTION="Python web framework and asynchronous networking library" -HOMEPAGE=" - https://www.tornadoweb.org/ - https://github.com/tornadoweb/tornado/ - https://pypi.org/project/tornado/ -" - -LICENSE="Apache-2.0" -SLOT="0" -KEYWORDS="~alpha amd64 arm arm64 ~hppa ~loong ~m68k ~mips ppc ppc64 ~riscv ~s390 ~sparc x86 ~arm64-macos ~x64-macos" -IUSE="test" -RESTRICT="!test? ( test )" - -RDEPEND=" - >=dev-python/twisted-16.0.0[${PYTHON_USEDEP}] -" -BDEPEND=" - test? ( - ${RDEPEND} - $(python_gen_cond_dep ' - >=dev-python/pycurl-7.19.3.1[${PYTHON_USEDEP}] - ' 'python*') - ) -" - -PATCHES=( - "${FILESDIR}/${PN}-6.3.2-ignore-deprecationwarning.patch" -) - -src_prepare() { - # network-sandbox? ipv6? - sed -i -e 's:test_localhost:_&:' \ - tornado/test/netutil_test.py || die - - distutils-r1_src_prepare -} - -python_test() { - local -x ASYNC_TEST_TIMEOUT=60 - # Avoid time-sensitive tests - # https://github.com/tornadoweb/tornado/blob/10974e6ebee80a26a2a65bb9bd715cf858fafde5/tornado/test/util.py#L19 - local -x TRAVIS=1 - local -x NO_NETWORK=1 - - cd "${T}" || die - "${EPYTHON}" -m tornado.test.runtests --verbose || - die "tests failed under ${EPYTHON}" -} diff --git a/dev-python/tornado/tornado-6.5.9.ebuild b/dev-python/tornado/tornado-6.6.ebuild index 34eff013d738..f2af848e67e8 100644 --- a/dev-python/tornado/tornado-6.5.9.ebuild +++ b/dev-python/tornado/tornado-6.6.ebuild @@ -30,9 +30,8 @@ RDEPEND=" BDEPEND=" test? ( ${RDEPEND} - $(python_gen_cond_dep ' - >=dev-python/pycurl-7.19.3.1[${PYTHON_USEDEP}] - ' 'python*') + dev-python/hypothesis[${PYTHON_USEDEP}] + >=dev-python/pycurl-7.19.3.1[${PYTHON_USEDEP}] ) " diff --git a/dev-python/types-python-dateutil/Manifest b/dev-python/types-python-dateutil/Manifest index b6bbe1776292..317e31638100 100644 --- a/dev-python/types-python-dateutil/Manifest +++ b/dev-python/types-python-dateutil/Manifest @@ -1 +1,2 @@ DIST types_python_dateutil-2.9.0.20260807.tar.gz 17618 BLAKE2B 17d3866132f2501a5a78574386279cd49a77a555e20e68085e238d7f0851895c70b1cdbe8560158cb3f122fc312914e158170e04721d0a2909f2a4010a7c92a1 SHA512 3903f9d2295885416e9f1de5c67f55dd4f243d734267e3197b0788275fc5124837fedf27c964045cf47da355ee83fef654540d80ba82ed78501539fc2f151999 +DIST types_python_dateutil-2.9.0.20261021.tar.gz 17792 BLAKE2B 9333a25b887891916311a5f5a446d36ea1d28e6cddf33fd700e99efc706b350356d2c8dc31a32730c8f099c62710b5763ba4f983c4066a61894de78d2c01f08e SHA512 06f753afe882c48e5b78b573528c9dffa4f06208018898db5f10f514f6c154ba3bec3e98650a634210265c0b0f9fbf1d7424a303d21b971aadca67c41313beba diff --git a/dev-python/types-python-dateutil/types-python-dateutil-2.9.0.20261021.ebuild b/dev-python/types-python-dateutil/types-python-dateutil-2.9.0.20261021.ebuild new file mode 100644 index 000000000000..866a75258277 --- /dev/null +++ b/dev-python/types-python-dateutil/types-python-dateutil-2.9.0.20261021.ebuild @@ -0,0 +1,23 @@ +# Copyright 2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=setuptools +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 pypi + +DESCRIPTION="Typing stubs for python-dateutil" +HOMEPAGE=" + https://github.com/python/typeshed/ + https://pypi.org/project/types-python-dateutil/ +" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~arm64-macos ~x64-macos" + +BDEPEND=" + >=dev-python/setuptools-82.0.1[${PYTHON_USEDEP}] +" diff --git a/dev-python/uv-build/Manifest b/dev-python/uv-build/Manifest index a199ed0a136a..670371f241cc 100644 --- a/dev-python/uv-build/Manifest +++ b/dev-python/uv-build/Manifest @@ -1,2 +1,3 @@ DIST uv_build-0.12.17.tar.gz 434807 BLAKE2B 8823907abc14637a684723b683284cc8b6a40b2467adbab74059b139388aeb1ff8870bdc09a497bad3e06af60c4e9c360c8d57cc6dd62e26e9239f4cf041f827 SHA512 a833a0578c6d543eabe51089d330f0d93d141bea5ff2bc1a194ad27a7ad4a1c7189321cdcf6012465b819bffd620affb42d4fb148bce69beeee0e32537a74668 DIST uv_build-0.13.4.tar.gz 464427 BLAKE2B 899bd8d7628bc5a291a6f974e569484a61d1aff51745e004cb663167128470147f2dae454457e010fa495e9d9de19527e9185e90c88018e51b4352359ceca349 SHA512 2e74b38cd01468281f481fa3b1ea289e1d262986e97bf3bfd27812c08676825567c547144bc9ed090bede4eb8db3114e6e8330eeaf96a91602bdd5212ae76433 +DIST uv_build-0.13.5.tar.gz 466761 BLAKE2B 85333e905fa03c1615a2ce67b19d4266d84757419bd2c04737ceee3fc000ce543aebb0e51f7bccdf7a681ab691aeee1491f8c335f4ffc3b1d52ebe75d5fb4ad7 SHA512 a7e3c3029179dc9a2c91519f7d161f707cce9b87e0b6a1802b257a63b8a468ba0fa64ea2010571426b30746ed3f83742c26c2c3045f06b587375c60231e52f9f diff --git a/dev-python/uv-build/uv-build-0.13.5.ebuild b/dev-python/uv-build/uv-build-0.13.5.ebuild new file mode 100644 index 000000000000..0250a02341f9 --- /dev/null +++ b/dev-python/uv-build/uv-build-0.13.5.ebuild @@ -0,0 +1,100 @@ +# Copyright 2025-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +# Maturin compiles uv-build executable for every impl, we do not want +# that, so we use another backend. And since we use another backend, +# why not dogfood it in the first place? +DISTUTILS_USE_PEP517=standalone +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 pypi + +DESCRIPTION="PEP517 uv build backend" +HOMEPAGE=" + https://github.com/astral-sh/uv/ + https://pypi.org/project/uv-build/ +" + +LICENSE="|| ( Apache-2.0 MIT )" +SLOT="0" +KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc64 ~riscv ~s390 ~x86" +IUSE="test" +RESTRICT="!test? ( test )" + +RDEPEND=" + >=dev-python/uv-${PV} +" +BDEPEND=" + test? ( + app-arch/unzip + dev-python/build[${PYTHON_USEDEP}] + ) +" + +src_prepare() { + distutils-r1_src_prepare + + # use the executable from dev-python/uv instead of building + # a largely overlapping uv-build executable (at least for now) + sed -i -e '/USE_UV_EXECUTABLE/s:False:True:' python/uv_build/__init__.py || die + + # replace the build-system section + sed -i -e '/\[build-system\]/,$d' pyproject.toml || die + cat >> pyproject.toml <<-EOF || die + [build-system] + requires = ["uv_build<9999"] + build-backend = "uv_build" + backend-path = ["src"] + EOF + + # rename to make uv-build find it + mv python src || die +} + +python_test() { + "${EPYTHON}" -m build -n || die "Self-build failed with ${EPYTHON}" + + local zip_result=$( + unzip -t "dist/uv_build-${PV}-py3-none-any.whl" || die + ) + local zip_expected="\ +Archive: dist/uv_build-${PV}-py3-none-any.whl + testing: uv_build/ OK + testing: uv_build/__init__.py OK + testing: uv_build/__main__.py OK + testing: uv_build/py.typed OK + testing: uv_build-${PV}.dist-info/ OK + testing: uv_build-${PV}.dist-info/WHEEL OK + testing: uv_build-${PV}.dist-info/METADATA OK + testing: uv_build-${PV}.dist-info/RECORD OK +No errors detected in compressed data of dist/uv_build-${PV}-py3-none-any.whl.\ +" + if [[ ${zip_result} != ${zip_expected} ]]; then + eerror ".zip result:\n${zip_result}" + eerror ".zip expected:\n${zip_expected}" + die ".whl result mismatch" + fi + + local tar_result=$( + tar -tf "dist/uv_build-${PV}.tar.gz" || die + ) + local tar_expected="\ +uv_build-${PV}/PKG-INFO +uv_build-${PV}/pyproject.toml +uv_build-${PV}/pyproject.toml.orig +uv_build-${PV}/ +uv_build-${PV}/README.md +uv_build-${PV}/src +uv_build-${PV}/src/uv_build +uv_build-${PV}/src/uv_build/__init__.py +uv_build-${PV}/src/uv_build/__main__.py +uv_build-${PV}/src/uv_build/py.typed\ +" + if [[ ${tar_result} != ${tar_expected} ]]; then + eerror ".tar.gz result:\n${tar_result}" + eerror ".tar.gz expected:\n${tar_expected}" + die ".tar.gz result mismatch" + fi +} diff --git a/dev-python/uv/Manifest b/dev-python/uv/Manifest index 7158f373e5c9..3b0355c8f40b 100644 --- a/dev-python/uv/Manifest +++ b/dev-python/uv/Manifest @@ -2,3 +2,5 @@ DIST uv-0.12.17-crates.tar.xz 43846848 BLAKE2B f899f093a14a7195cb93d81aa71c31d68 DIST uv-0.12.17.gh.tar.gz 8706123 BLAKE2B e45aae4ea550252440e8f646a505ba4d295e1eafe94092798e79e5db26320dae4b899cc7d04d96387791996e55e4ced2cbcff6ffcd9edc056c9580f5e175751f SHA512 ae554aa9d096afa7e1c35a1e50ba11c0553cd182ba75e5dd425613add092d38c46b3cbd47b026f03674394d6d0852162d5953c4cacef511c7157378d1790b8a1 DIST uv-0.13.4-crates.tar.xz 44003088 BLAKE2B 671f829d9f64a3f5aa2d1ec7e3f705eadb63529c43f65796a25ef0307f763ce4611f2ee41ee38ffeb19a2eac91d918129be4deb128c6a879c5bcd204862854ea SHA512 94d07b0fa8edbf6364366268be678055125e7baa854c8bc30aaf6721210fc63a1ef0dd5f07e0a511b2cd132f2fcf25690b6c2fa828b97b3893785ed18d40a51e DIST uv-0.13.4.gh.tar.gz 8930524 BLAKE2B 6db88a4d14037a65f0c2f1a5e98a1a3a6c0697300906af3f753d7ce87320b4086067abe13a60fd335d713f427fc1e2bda7a4917eb1d985daf03aa795672eb188 SHA512 06bc16f6e10b31cbdf1265623990cfe52bc0e040a53edd63b952807b340aec274d412ff7f17111e235596c7f054186fb7024abb0e563f43abb03309df7070f8a +DIST uv-0.13.5-crates.tar.xz 44020328 BLAKE2B 054a712a0df9ab235bdc65da39218ec4e76bdf9df7946ab137e1744359fb5b6ebf35e3b8f91cc17f3ab484654e4ab35aaa2f071e3a9034d395d99b14a5133c5f SHA512 272bd68eb45f516d885c587392b08857e5e6db825878a9b686686e588e7a87c1944b38ae063f780060d87615eec51e0a9cff786480adb9e1e4d13bd17c2b23a6 +DIST uv-0.13.5.gh.tar.gz 8966770 BLAKE2B 18e1ad6a5a68f5750ca3c2e97856418a0fc04dd0e59dacd4cc04fecf09c9c4e1dece02959e96d2d86cf41c83b4da5f5f767279058cf34845f76420172bd5d613 SHA512 22ae38ec22e9aa1f816e39516357a8f05c296cbf7cffe1f48657a10d3b80252492397ba5dd6dace15846626c6cdcc0c456a38c4ba51d856dd72d3ad025b9e6c7 diff --git a/dev-python/uv/uv-0.13.5.ebuild b/dev-python/uv/uv-0.13.5.ebuild new file mode 100644 index 000000000000..c3bba465dc41 --- /dev/null +++ b/dev-python/uv/uv-0.13.5.ebuild @@ -0,0 +1,161 @@ +# Copyright 2024-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +CRATES=" +" +RUST_MIN_VER="1.97.0" + +inherit cargo check-reqs + +CRATE_PV=${PV} +DESCRIPTION="A Python package installer and resolver, written in Rust" +HOMEPAGE=" + https://github.com/astral-sh/uv/ + https://pypi.org/project/uv/ +" +# pypi sdist misses scripts/, needed for tests +SRC_URI=" + https://github.com/astral-sh/uv/archive/${PV}.tar.gz + -> ${P}.gh.tar.gz + ${CARGO_CRATE_URIS} +" +if [[ ${PKGBUMPING} != ${PVR} ]]; then + SRC_URI+=" + https://github.com/gentoo-crate-dist/uv/releases/download/${CRATE_PV}/uv-${CRATE_PV}-crates.tar.xz + " +fi + +# most of the code +LICENSE="|| ( Apache-2.0 MIT )" +# crates/pep508-rs is || ( Apache-2.0 BSD-2 ) which is covered below +# Dependent crate licenses +LICENSE+=" + Apache-2.0 Apache-2.0-with-LLVM-exceptions BSD-2 BSD CC0-1.0 + CDLA-Permissive-2.0 ISC MIT MPL-2.0 Unicode-3.0 Unicode-DFS-2016 + ZLIB +" +# ring crate +LICENSE+=" openssl" +SLOT="0" +KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc64 ~riscv ~s390 ~x86" +IUSE="test" +RESTRICT="test" +PROPERTIES="test_network" + +DEPEND=" + app-arch/bzip2:= + app-arch/xz-utils:= + app-arch/zstd:= +" +RDEPEND=" + ${DEPEND} +" +# git/curl: https://bugs.gentoo.org/984287 +BDEPEND=" + virtual/pkgconfig + test? ( + dev-lang/python:3.9 + dev-lang/python:3.10 + dev-lang/python:3.11 + dev-lang/python:3.12 + dev-lang/python:3.13 + !!~dev-python/uv-0.5.0 + || ( + >=dev-vcs/git-2.57 + <net-misc/curl-8.23 + ) + ) +" + +QA_FLAGS_IGNORED="usr/bin/.*" + +check_space() { + local CHECKREQS_DISK_BUILD=3G + use debug && CHECKREQS_DISK_BUILD=9G + check-reqs_pkg_setup +} + +pkg_pretend() { + check_space +} + +pkg_setup() { + check_space + rust_pkg_setup +} + +src_prepare() { + default + + # force thin lto, makes build much faster and less memory hungry + # (i.e. makes it possible to actually build uv on 32-bit PPC) + sed -i -e '/lto/s:fat:thin:' Cargo.toml || die + + # enable system libraries where supported + export ZSTD_SYS_USE_PKG_CONFIG=1 + # TODO: unbundle tikv-jemalloc-sys? + + # remove unbundled sources, just in case + find "${ECARGO_VENDOR}"/zstd-sys-*/ -name '*.c' -delete || die +} + +src_configure() { + local myfeatures=( + test-git + test-pypi + test-python + ) + + cargo_src_configure --no-default-features + # otherwise, rustc/llvm runs out of address space + use arm && export RUSTFLAGS="${RUSTFLAGS} -Copt-level=0" +} + +src_compile() { + cd crates/uv || die + cargo_src_compile +} + +src_test() { + cd crates/uv || die + cargo_src_test --no-fail-fast +} + +src_install() { + dobin "$(cargo_target_dir)"/{uv,uvx} + + insinto /etc/xdg/uv + newins - uv.toml <<-EOF || die + # These defaults match Fedora, see: + # https://src.fedoraproject.org/rpms/uv/pull-request/18 + + # By default ("automatic"), uv downloads missing Python versions + # automatically and keeps them in the user's home directory. + # Disable that to make downloading opt-in, and especially + # to avoid unnecessarily fetching custom Python when the distro + # package would be preferable. Python builds can still be + # downloaded manually via "uv python install". + # + # https://docs.astral.sh/uv/reference/settings/#python-downloads + python-downloads = "manual" + + # By default ("managed"), uv always prefers self-installed + # Python versions over the system Python, independently + # of versions. Since we generally expect users to use that + # to install old Python versions not in ::gentoo anymore, + # this effectively means that uv would end up preferring very + # old Python versions over the newer ones that are provided + # by the system. Default to using the system versions to avoid + # this counter-intuitive behavior. + # + # https://docs.astral.sh/uv/reference/settings/#python-preference + python-preference = "system" + + # Always use system certificates. + # + # https://docs.astral.sh/uv/reference/settings/#system-certs + system-certs = true + EOF +} diff --git a/dev-python/vcsgraph/Manifest b/dev-python/vcsgraph/Manifest index be9baeb3ed50..fe0d7b7405c6 100644 --- a/dev-python/vcsgraph/Manifest +++ b/dev-python/vcsgraph/Manifest @@ -1,3 +1,6 @@ DIST vcsgraph-0.3.2-crates.tar.xz 1958984 BLAKE2B e315d3dca011e5ad422e4944a2972ca8698c3b8993e7c7f8be6c81a9dd04e6188a2ad41432ed6364713ecc7037f484c6c4812af5f783f15779b4ddaaead9bb0c SHA512 73e3a54e2a5aae51135c73c72316f49e0d52f4ccef7dc3a1540b1040d47a334d7a19ddaca841009dc01fa7aa36daea337df417cfd976c90b9fc0e57f0bb82d00 DIST vcsgraph-0.3.2.tar.gz 99202 BLAKE2B 670a41a48f10c24ff15a590db7393bcc2d5ff1ae8f8c816ed4e3a574b963f6b5b183435a4832998b316f7e3971626010410a8dd8a741a574ab4b2b283079a7db SHA512 2aa4802221d4a4cc4bc0f341ef521912bc493cbe1765a7880b4dedc1f598e7f398c7781d6a0a222a2cd4afcc17178fb50347c6c06bda1071d0b22c65885c654c DIST vcsgraph-0.3.2.tar.gz.provenance 9838 BLAKE2B 4724f8083025ca24eb3881aa8f964ae4b13d74384ea722d422587ffec345840963461cd34e62be92361754618a24c3107eb6f29e538d4f05bca1616f2be51568 SHA512 5553f43e89d8e236c5e7bb5f061f6f9e8b6bbf667b77e13defeb1c0c3de69734911d344c54772eb31e8a447371e2d322dad1fe9eb9bd647ad77939288a13deb4 +DIST vcsgraph-0.3.3-crates.tar.xz 2089584 BLAKE2B 4f300434ae93b427fe71815f99425d3f1d527c9c22084e396c91ea1d22b02497a90029de94f6b7d26e9eb0e2d731a4092ae8f6fc05361d37790101fe61f334ed SHA512 4ca79c24e87590e01a11d543cfb4c2c00d9f03d00c6b141a1b74d3cc86e6f80d4fe54c34b35f5c01e7d19ae778f47c472babff6ac75bc2ccccd51ada963a56d0 +DIST vcsgraph-0.3.3.tar.gz 99280 BLAKE2B d77f09a184afd7c30851fd59e0a97352ab3c51c56513ae5218efb189d2f1a56c115dc1b7fdbbc7c1b78c9c84a9a8a22c2dc39bcb7f9e8288060619b01baedabd SHA512 977a8f5dad82525192b51769ed6b745c200ea7d6a04570d85074403af054f973f548e89ab97d1e11b53ecc9545a26c7089c153a420f4e6746fcf51d56fa58aa3 +DIST vcsgraph-0.3.3.tar.gz.provenance 9810 BLAKE2B 8b928a7c95447db0e1b656ff07135e6cd61f1a200741ef72f5dcd5078b19d57ae891b94d6767c43096b383c8aaf32beed9a2811a22a82bf1dfa0aa1c523ebc91 SHA512 cefb068f2e2b392004cb715f1107712fcf30e9befa6ea881e178e285388bc7787e2a511e1900868ca9313b5fa45a38f47c8fdd2bd02de8925693786b55113343 diff --git a/dev-python/vcsgraph/vcsgraph-0.3.3.ebuild b/dev-python/vcsgraph/vcsgraph-0.3.3.ebuild new file mode 100644 index 000000000000..2a45f28a9d42 --- /dev/null +++ b/dev-python/vcsgraph/vcsgraph-0.3.3.ebuild @@ -0,0 +1,45 @@ +# Copyright 2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_EXT=1 +DISTUTILS_USE_PEP517=setuptools +PYPI_VERIFY_REPO=https://github.com/breezy-team/vcsgraph +PYTHON_COMPAT=( python3_{13..14} ) + +RUST_MIN_VER="1.83" +CRATES="" + +inherit cargo distutils-r1 pypi + +DESCRIPTION="Graph algorithms for version control systems" +HOMEPAGE=" + https://pypi.org/project/vcsgraph/ + https://github.com/breezy-team/vcsgraph/ +" +SRC_URI+=" + https://github.com/gentoo-crate-dist/${PN}/releases/download/v${PV}/${P}-crates.tar.xz +" + +LICENSE="GPL-2+" +SLOT="0" +KEYWORDS="~amd64 ~arm64 ~x86" + +BDEPEND=" + dev-python/setuptools-rust[${PYTHON_USEDEP}] +" + +distutils_enable_tests unittest + +QA_FLAGS_IGNORED="usr/lib.*/py.*/site-packages/vcsgraph/_graph_rs.*.so" + +src_unpack() { + pypi_src_unpack + cargo_gen_config +} + +python_test() { + cd "${BUILD_DIR}/install$(python_get_sitedir)" || die + eunittest +} diff --git a/dev-python/virtualenv/Manifest b/dev-python/virtualenv/Manifest index f621e97955e9..2b84f4a853bd 100644 --- a/dev-python/virtualenv/Manifest +++ b/dev-python/virtualenv/Manifest @@ -8,6 +8,8 @@ DIST virtualenv-21.14.10.tar.gz 5501185 BLAKE2B e7e0af87a1d923ca899db15ac92a1a87 DIST virtualenv-21.14.10.tar.gz.provenance 9622 BLAKE2B cefdd192e473a913d659fcd9f9da0dfd44a96f8b2c3638a94adced0246d0ec585c3f01893ee390a848d01f5340ea4b1324fe2c4acb9b3280af18eb789c09eacb SHA512 c856eb9343a104cf8f5388be5bfa0cfb6f5286d2cbdfb425d4613b32b38d0be0a530eb6bf9f89d2deef040dc009e6bb657b23334120c3cedb1e3a96cb316e8f3 DIST virtualenv-21.14.11.tar.gz 5503091 BLAKE2B 73e59fbf96059002e43819076f17912a2fb1a70cc6b188e10d62eb9d39badf3156dcbcdf5f7280b5bba793a42c0a6cdec734f90534eab59c5c67616f577961a2 SHA512 426cf1fa668754df3defb33f8fb92808b57c8093d573269005345206bbf4adab6fca1a8aaa669fd94678681c3fcd685cb545bf4ed1092b01608e32743d9fe65d DIST virtualenv-21.14.11.tar.gz.provenance 9939 BLAKE2B f1b9d5aacc24af5f6da163863444c58827b272312a03fd5986dd4425298b28d4948e66ccf61614adc76c3f341e084e7fccf6a0a8da20834cb798dcfc3b9d8ea6 SHA512 84f2025ce55b8285dd5fd82f8a5c7c8a6e1743ac775aa9fb3a502f9314dc72d0c83584e9534c907cf3572b7b2226786102614f178730f7a10d68feb60774bf88 +DIST virtualenv-21.14.12.tar.gz 5562800 BLAKE2B e74bae47559d4e42d62cb55b4eb454c89010e7bbe4c01e77353f1330d7d7b5b1d8674a44ab434f8348ae799c03630312a6fc1beccf833c19551a6bfab8e5efe5 SHA512 8ad3ba141e199b74861ca2480a940f022ce117d13c225973eedc96537b6ec99132255d7f34296199e6e6c3dede3db76d3f9c131f3504a805cb56b9d7bb8c8394 +DIST virtualenv-21.14.12.tar.gz.provenance 9575 BLAKE2B f92c135942161076edaf72fc7b458384804533d21d44e283f8be940a01d5bdf76502f4f70fa8ba88173ef2872b7253bfd1e47b36b229543e103ecac90eb27afc SHA512 4cdd9414ea7d623ddcae7d3ea52056843a196f2fe00415a2ed7b2835aaa093d48c595043b9a5fc0dd421627b2d0f32b123ceea4b141c327fafa0fd241af025bf DIST virtualenv-21.14.3.tar.gz 5464634 BLAKE2B e82707e1a51d55efef6b902fd8f5aa617003fa43d61558a504d7c26186fd64cbf5c559ccb17b0710b2bde0dea7130652d606cc71dfd82936ffe51c046bb74499 SHA512 6ed46584d704a2ea4b4241348a0c8a3dcc7ae0fb5096fe096f60cac1e589b7f634ef46c377b31f3db220b8b0d2cc2f4f1a46294171c662d8fbd8d2d4a569caec DIST virtualenv-21.14.3.tar.gz.provenance 9808 BLAKE2B 2bd41496285aa992dcebae332feda3bc0b6f6c06483ae09278ebb4e9835df510a66d29806033afda3cce87ea3899aeb4d5eee8f53ce1df2d2543ee32e13127fd SHA512 98c0db6aed32e36ce3b88ae8193a725e9abecff06cbe7e3821e172c82f9bfa03e2e247f50314740f700a0c2d828b254d6bbfb0e74a7919d10d5d33ebac54701b DIST virtualenv-21.14.5.tar.gz 5467127 BLAKE2B 029434d3c647a5898664b98ddd9fcb7577bc6cbe4e7e5cce308bcac83181e48d55556a9936b4928c26f9f5365bf3551047742d29a82b2233cb047df88336db3c SHA512 d58e9332fc6d79a04d534db9b597cbc7e4abebab11858223baa29cf0284bca72646a909c844c0d141c35e63f7b999879af1eed53d0c51e8e4213c3a494ee615f diff --git a/dev-python/virtualenv/virtualenv-21.14.12.ebuild b/dev-python/virtualenv/virtualenv-21.14.12.ebuild new file mode 100644 index 000000000000..f7348466af1b --- /dev/null +++ b/dev-python/virtualenv/virtualenv-21.14.12.ebuild @@ -0,0 +1,131 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=hatchling +PYPI_VERIFY_REPO=https://github.com/pypa/virtualenv +PYTHON_TESTED=( python3_{13..14} ) +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 pypi + +DESCRIPTION="Virtual Python Environment builder" +HOMEPAGE=" + https://virtualenv.pypa.io/en/stable/ + https://pypi.org/project/virtualenv/ + https://github.com/pypa/virtualenv/ +" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" +IUSE="test" +RESTRICT="!test? ( test )" + +RDEPEND=" + >=dev-python/distlib-0.3.7[${PYTHON_USEDEP}] + >=dev-python/filelock-3.24.2[${PYTHON_USEDEP}] + >=dev-python/packaging-23.1[${PYTHON_USEDEP}] + >=dev-python/platformdirs-3.9.1[${PYTHON_USEDEP}] + >=dev-python/python-discovery-1.6[${PYTHON_USEDEP}] + + dev-python/ensurepip-pip + >=dev-python/ensurepip-setuptools-70.1 + dev-python/ensurepip-wheel +" +# coverage is used somehow magically in virtualenv, maybe it actually +# tests something useful +BDEPEND=" + dev-python/hatch-vcs[${PYTHON_USEDEP}] + test? ( + ${RDEPEND} + $(python_gen_cond_dep ' + dev-python/coverage[${PYTHON_USEDEP}] + >=dev-python/hatchling-1.27[${PYTHON_USEDEP}] + >=dev-python/pip-22.2.1[${PYTHON_USEDEP}] + >=dev-python/pytest-mock-3.6.1[${PYTHON_USEDEP}] + dev-python/pytest-rerunfailures[${PYTHON_USEDEP}] + dev-python/pytest-timeout[${PYTHON_USEDEP}] + dev-python/pytest-xdist[${PYTHON_USEDEP}] + >=dev-python/setuptools-67.8[${PYTHON_USEDEP}] + dev-python/time-machine[${PYTHON_USEDEP}] + dev-python/wheel[${PYTHON_USEDEP}] + >=dev-python/packaging-20.0[${PYTHON_USEDEP}] + ' "${PYTHON_TESTED[@]}") + ) +" + +src_prepare() { + local PATCHES=( + # use wheels from ensurepip bundle + "${FILESDIR}/${PN}-21.5.1-ensurepip.patch" + ) + + distutils-r1_src_prepare + + # workaround test failures due to warnings from setuptools-scm, sigh + echo '[tool.setuptools_scm]' >> pyproject.toml || die + + # remove useless pins + sed -i -e 's:,<[=0-9.]*::' pyproject.toml || die + + # remove bundled wheels + rm src/virtualenv/seed/wheels/embed/*.whl || die +} + +python_test() { + if ! has "${EPYTHON}" "${PYTHON_TESTED[@]/_/.}"; then + einfo "Skipping testing on ${EPYTHON}" + return + fi + + local EPYTEST_DESELECT=( + tests/unit/seed/embed/test_bootstrap_link_via_app_data.py::test_seed_link_via_app_data + # tests for old wheels with py3.7 support + tests/unit/seed/embed/test_pip_invoke.py::test_base_bootstrap_via_pip_invoke + tests/unit/seed/wheels/test_wheels_util.py::test_wheel_not_support + # broken by different wheel versions in ensurepip + tests/unit/seed/wheels/test_acquire_find_wheel.py::test_find_latest_string + tests/unit/seed/wheels/test_acquire_find_wheel.py::test_find_exact + tests/unit/seed/wheels/test_acquire_find_wheel.py::test_find_latest_none + tests/unit/seed/wheels/test_acquire.py::test_download_wheel_bad_output + tests/unit/seed/wheels/test_wheels_util.py::test_embed_wheel_below_oldest_supported_is_missing + # hangs on a busy system, sigh + tests/unit/test_util.py::test_reentrant_file_lock_is_thread_safe + # TODO + tests/unit/create/via_global_ref/test_build_c_ext.py::test_can_build_c_extensions + # random resource leaks or xdist + tests/unit/test_file_limit.py::test_too_many_open_files + # Internet + tests/unit/create/test_creator.py::test_create_distutils_cfg + tests/unit/seed/embed/test_pip_invoke.py::test_pip_reseed_removes_previous_version_metadata + # we do not use bundled wheels + tests/unit/seed/wheels/test_bundle.py::test_every_wheel_on_disk_has_sha256 + # requires bundled packages in pip + tests/unit/test_sbom.py::test_sbom_spdx_declared_license + # require pre-commit = internet + tests/unit/test_wheel_age.py::test_generator_converges + tests/unit/test_wheel_age.py::test_generator_updates_license + # TODO + tests/unit/seed/embed/test_bootstrap_link_via_app_data.py::test_pip_launcher_record_matches_contents + tests/unit/seed/wheels/test_acquire.py::test_download_wheel_skips_local_pip_module + tests/unit/seed/wheels/test_acquire_find_wheel.py::test_find_latest + # some platformdirs nonsense (bundled packages?) + tests/unit/config/test___main__.py::test_module_entry_from_target_install + ) + + local -x TZ=UTC + local EPYTEST_PLUGINS=( pytest-{mock,rerunfailures} time-machine ) + local EPYTEST_RERUNS=5 + local EPYTEST_TIMEOUT=180 + local EPYTEST_XDIST=1 + epytest -o addopts= +} + +src_install() { + distutils-r1_src_install + + # remove bundled wheels, we're using ensurepip bundle instead + find "${ED}" -name '*.whl' -delete || die +} diff --git a/dev-python/virtualenvwrapper/virtualenvwrapper-6.1.1.ebuild b/dev-python/virtualenvwrapper/virtualenvwrapper-6.1.1.ebuild index 011d5747779c..6ef34457ce2d 100644 --- a/dev-python/virtualenvwrapper/virtualenvwrapper-6.1.1.ebuild +++ b/dev-python/virtualenvwrapper/virtualenvwrapper-6.1.1.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/dev-python/wcmatch/Manifest b/dev-python/wcmatch/Manifest index f2ac1761d057..3bb4e0ae20e9 100644 --- a/dev-python/wcmatch/Manifest +++ b/dev-python/wcmatch/Manifest @@ -1,2 +1,3 @@ DIST wcmatch-11.0.1.gh.tar.gz 150081 BLAKE2B c80a055c02d0fc671fae4ef7dd291c8e58bdf91a446394a22c845a6b5c0ad9842874631f0e446b7e407d34e61a938c9e18e6925b459205b85e93bed00d42fd9d SHA512 72ae74717212fd9d1cc8c726c6e07f6b2aa88fc6c508d03a8f616dc902fe5b08d356454b392fbfb4bc626df25c55153be8ec1cd42cc345a532a4cf489ea412c3 DIST wcmatch-11.1.gh.tar.gz 134230 BLAKE2B 8f87a7db63317b7e06cab2a0ef1192db4fec701ff0c3a759df0e7315dadded6575302ef66f1ec73202dc3a6d92cbac9186ede3286592101e3e36798ce5647667 SHA512 4729a2d1a70753e5bfe4183099a1fd052316b7249c1d69b55d776d87459d20fa01b4146d606e80afb52c3eb457d5d612409d1846cf2c56563306c7fc3530df0d +DIST wcmatch-11.2.gh.tar.gz 139990 BLAKE2B 4635312c36ee79ed13e076f9f262bd7cfbe1166c69645d9f80f534f50313460c72c1f690b793d48578c55fe72faceb0feec7cf8a703096ce3815dbd7e8f53ca3 SHA512 475c8b652d1cf876ad4b0d13e2adef4742f1bcad072de22f000f08f6b8924dfe41fe24149e246501a0dc23da2c2b3807f66c8f90fe71216f1ee4d529906d5c74 diff --git a/dev-python/wcmatch/wcmatch-11.2.ebuild b/dev-python/wcmatch/wcmatch-11.2.ebuild new file mode 100644 index 000000000000..7e487e22e2a9 --- /dev/null +++ b/dev-python/wcmatch/wcmatch-11.2.ebuild @@ -0,0 +1,50 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +PYTHON_COMPAT=( python3_{13..14} ) +DISTUTILS_USE_PEP517=hatchling + +inherit distutils-r1 + +DESCRIPTION="Wildcard/glob file name matcher" +HOMEPAGE=" + https://github.com/facelessuser/wcmatch/ + https://pypi.org/project/wcmatch/ +" +SRC_URI=" + https://github.com/facelessuser/wcmatch/archive/${PV}.tar.gz + -> ${P}.gh.tar.gz +" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64 ~arm ~arm64 ~ppc ~ppc64 ~riscv ~x86" + +RDEPEND=" + >=dev-python/bracex-3.2[${PYTHON_USEDEP}] +" + +BDEPEND=" + test? ( + dev-vcs/git + ) +" + +EPYTEST_PLUGINS=() +distutils_enable_tests pytest + +python_prepare_all() { + # tests require some files in homedir + > "${HOME}"/test1.txt || die + > "${HOME}"/test2.txt || die + + distutils-r1_python_prepare_all +} + +python_test() { + # https://github.com/python/cpython/issues/155823 + local -x PYTHON_JIT=0 + epytest +} diff --git a/dev-python/xlsxwriter/Manifest b/dev-python/xlsxwriter/Manifest index 0033b568b31e..e70c39c777c8 100644 --- a/dev-python/xlsxwriter/Manifest +++ b/dev-python/xlsxwriter/Manifest @@ -1 +1,2 @@ DIST XlsxWriter-RELEASE_3.2.9.gh.tar.gz 24923295 BLAKE2B 7a344b5670b6264c8e7bb8fd633566e88b77368788edabd67f7ff6cfb66d28cfc2791e97ad367a3bf8d5e91e9c3f238e6656c87076779a3b21594af96891f2e8 SHA512 7cfcc6e4123a998c369fd352f2ef725ee2781f001d867a7b2c1c1cb6ede5852f9b33e4e2f237dcbe583e0c1ee0573bb068d2a0d39b1fd8fd21ac7192b36ac695 +DIST XlsxWriter-RELEASE_3.3.0.gh.tar.gz 25040885 BLAKE2B 0b170561a92c690795f501d37a72503a852d96a6c8cef5a3368d29d56a2662fd8704a5e510b18b9aa786717a1e2c81c6a159da3eed7881b287f69bd9c148da6b SHA512 9b80288e20471bf793cb24a3a3ef34a56e17210fb5dfa5c7b8b4e5c69fe0b2e41cbfc00f487899cc051f631fab8dac30986c4b038cc05e8ab290236df93dc434 diff --git a/dev-python/xlsxwriter/xlsxwriter-3.3.0.ebuild b/dev-python/xlsxwriter/xlsxwriter-3.3.0.ebuild new file mode 100644 index 000000000000..fea7791b689e --- /dev/null +++ b/dev-python/xlsxwriter/xlsxwriter-3.3.0.ebuild @@ -0,0 +1,29 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=setuptools +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 + +TAG=RELEASE_${PV} +MY_P=XlsxWriter-${TAG} +DESCRIPTION="Python module for creating Excel XLSX files" +HOMEPAGE=" + https://github.com/jmcnamara/XlsxWriter/ + https://pypi.org/project/xlsxwriter/ +" +SRC_URI=" + https://github.com/jmcnamara/XlsxWriter/archive/${TAG}.tar.gz + -> ${MY_P}.gh.tar.gz +" +S=${WORKDIR}/${MY_P} + +LICENSE="BSD" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" + +EPYTEST_PLUGINS=() +distutils_enable_tests pytest diff --git a/dev-python/zipp/Manifest b/dev-python/zipp/Manifest index 6d8c477ad6f7..35a4899f97dd 100644 --- a/dev-python/zipp/Manifest +++ b/dev-python/zipp/Manifest @@ -1,2 +1,3 @@ DIST zipp-4.1.0.tar.gz 26214 BLAKE2B 8ef46c33c39adb1894599278307bfea4c1b9fbede22bff698585185fe3bc814a5460502896f8cdb38beb99d37a83e881cdded0db8686d0a25e4fccdb56bb4bbe SHA512 1648ee2581e958ff11472357784b34111f86fd2c2281bb35ca8122f238c721c5a1e54332ffe1fb03208dfee2706adb39ede5389b2d3612c9134f3662bef54ffa DIST zipp-4.1.1.tar.gz 27649 BLAKE2B af88a8dc9dfdbc94c08316ad25d07ca964ed47d9d69900e1dd380abfa81a4bfe755cc9909f5f027bc090522c62e4768dbd4cbc7c202bca8f252479b5048627ee SHA512 218a1c11f143bb581c396d4652f46680cce04098440ba202599a8953f2bc5ffbaecaba97048c62fd34d1ed55fe9490859c22ad091107e79cbebf23ac1a12729b +DIST zipp-4.1.2.tar.gz 27864 BLAKE2B a54f6737aaefd1f9e8651a3d051c00d301563e5f51610e6101c4061f18f4db8fa30c505918a90c4df9be51264807065ae741397be914b68c7ea126bd8949116b SHA512 8c4e21bd4547aef94d0a7fe3d85df6fb36b66a6ae162674565b7d209b563ce3eeb5bcd5fdeacb864faab91c49b39a7526d57bf68982c74a87b360eaea7d1b028 diff --git a/dev-python/zipp/zipp-4.1.2.ebuild b/dev-python/zipp/zipp-4.1.2.ebuild new file mode 100644 index 000000000000..de79dbca9cea --- /dev/null +++ b/dev-python/zipp/zipp-4.1.2.ebuild @@ -0,0 +1,54 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +# please keep this ebuild at EAPI 8 -- sys-apps/portage dep +EAPI=8 + +DISTUTILS_USE_PEP517=flit-core +PYTHON_COMPAT=( python3_{13..14} ) + +inherit distutils-r1 pypi + +DESCRIPTION="Backport of pathlib-compatible object wrapper for zip files" +HOMEPAGE=" + https://github.com/jaraco/zipp/ + https://pypi.org/project/zipp/ +" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-macos ~x64-solaris" + +# big_o is only used in test_complexity, that we ignore +BDEPEND=" + test? ( + dev-python/jaraco-functools[${PYTHON_USEDEP}] + dev-python/jaraco-itertools[${PYTHON_USEDEP}] + dev-python/jaraco-test[${PYTHON_USEDEP}] + dev-python/more-itertools[${PYTHON_USEDEP}] + ) +" + +EPYTEST_PLUGINS=() +distutils_enable_tests pytest + +EPYTEST_IGNORE=( + # performance tests are flaky by design + tests/test_complexity.py +) + +src_configure() { + grep -q 'build-backend = "setuptools' pyproject.toml || + die "Upstream changed build-backend, recheck" + # write a custom pyproject.toml to ease setuptools bootstrap + cat > pyproject.toml <<-EOF || die + [build-system] + requires = ["flit_core >=3.2,<4"] + build-backend = "flit_core.buildapi" + + [project] + name = "zipp" + version = "${PV}" + description = "Backport of pathlib-compatible object wrapper for zip files" + EOF +} diff --git a/dev-util/bpftool/Manifest b/dev-util/bpftool/Manifest index a639c1b28c1f..5b9a38d99385 100644 --- a/dev-util/bpftool/Manifest +++ b/dev-util/bpftool/Manifest @@ -1,2 +1,3 @@ DIST bpftool-libbpf-v7.6.0-sources.tar.gz 1527883 BLAKE2B 5918519009b1ee258c33506cb52d216b08fbcd6365083a71d74b11e2c788fd4c6f7cfd78c84bb85074eb463dfa8d087b8e35c79d00f3a0810979640af6b334e6 SHA512 d443601227acd9a0db15896da186b0a47d426c44f2b6eaa822391594aa23d617a94ee94f284abb841529d41ef7c585a1087dd5ab1708ce068cc02b3c08b0a51b DIST bpftool-libbpf-v7.7.0-sources.tar.gz 1553943 BLAKE2B db13d16f12af61d97312dd821088e81062d3bbc6d954e0d463026bb99e6acdca4a232db26a1b735d70bd91cd954fd628e0a699517d4b1b38838ad3ac5aa82721 SHA512 d74b3ace03981791e08ac50d6b977a27de67bfd5e088e0ecb424eaebb58c30b85ef306f51d0e9244666511b430606b1ed52382d360348da40787c0045b6c8f43 +DIST bpftool-libbpf-v7.8.1-sources.tar.gz 1594958 BLAKE2B 9717db9c8de3e4c4c9b8a5ab373e6bc14cb7710b045e900cce4d58862aec83e79ef3f9fd7efe575cfb694e33d100b0935c00767320c5322b59889a2b4cea99bc SHA512 8e13487a70ea5a7da7b697a95e4bb6b7d02954d19d37a7ce1d499ae75eca266557095ed5dabf27cbbe184771e451c7f707ba054b5ad383854fc999dc79c34fc9 diff --git a/dev-util/bpftool/bpftool-7.8.1.ebuild b/dev-util/bpftool/bpftool-7.8.1.ebuild new file mode 100644 index 000000000000..47eb44cb9907 --- /dev/null +++ b/dev-util/bpftool/bpftool-7.8.1.ebuild @@ -0,0 +1,166 @@ +# Copyright 2021-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +LLVM_COMPAT=( {17..23} ) +LLVM_OPTIONAL=1 +PYTHON_COMPAT=( python3_{13..14} ) + +inherit flag-o-matic linux-info llvm-r2 python-any-r1 shell-completion toolchain-funcs + +DESCRIPTION="Tool for inspection and simple manipulation of eBPF programs and maps" +HOMEPAGE="https://github.com/libbpf/bpftool" + +if [[ ${PV} == *9999* ]] ; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/libbpf/bpftool.git" + EGIT_SUBMODULES=(libbpf) +else + # bpftool typically vendors whatever libbpf is current at the time + # of a release, while libbpf publishes minor updates more frequently. + # Uncomment the following to bundle an updated libbpf e.g. in case of + # security or crasher bugs in libbpf and to keep the two synchronized. + # This allows us to quickly update the vendored lib with a revbump. + # Currently bpftool-x.y vendors libbpf-1.y; DO NOT mix different y versions. + # See the libbpf repo (https://github.com/libbpf/libbpf) for possible updates. + # LIBBPF_VERSION=1.7.0 + + if [[ ! -z ${LIBBPF_VERSION} ]] ; then + SRC_URI="https://github.com/libbpf/bpftool/archive/refs/tags/v${PV}.tar.gz -> bpftool-${PV}.tar.gz + https://github.com/libbpf/libbpf/archive/refs/tags/v${LIBBPF_VERSION}.tar.gz + -> libbpf-${LIBBPF_VERSION}.tar.gz" + else + # use tarball with bundled libbpf + SRC_URI="https://github.com/libbpf/bpftool/releases/download/v${PV}/bpftool-libbpf-v${PV}-sources.tar.gz" + S="${WORKDIR}/bpftool-libbpf-v${PV}-sources" + fi + + KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc ~ppc64 ~riscv ~x86" +fi + +LICENSE="|| ( GPL-2 BSD-2 )" +SLOT="0" +IUSE="caps +clang llvm" +REQUIRED_USE="llvm? ( ${LLVM_REQUIRED_USE} )" + +RDEPEND=" + caps? ( sys-libs/libcap:= ) + llvm? ( $(llvm_gen_dep 'llvm-core/llvm:${LLVM_SLOT}') ) + !llvm? ( sys-libs/binutils-libs:= ) + virtual/zlib:= + virtual/libelf:= +" +DEPEND=" + ${RDEPEND} + >=sys-kernel/linux-headers-5.8 +" +BDEPEND=" + ${PYTHON_DEPS} + app-arch/tar + dev-python/docutils + clang? ( $(llvm_gen_dep 'llvm-core/clang:${LLVM_SLOT}[llvm_targets_BPF]') ) + !clang? ( sys-devel/bpf-toolchain ) +" + +CONFIG_CHECK="~DEBUG_INFO_BTF" + +pkg_setup() { + linux-info_pkg_setup + python-any-r1_pkg_setup + use llvm && llvm-r2_pkg_setup +} + +src_prepare() { + default + + # prepare libbpf if necessary + if [[ ! -z ${LIBBPF_VERSION} ]] ; then + rm -rf libbpf || die + ln -s "${WORKDIR}/libbpf-${LIBBPF_VERSION}" libbpf || die + fi + + # remove -Werror from libbpf (bug 887981) + sed -i -e 's/\-Werror//g' libbpf/src/Makefile || die + + # remove -Werror from bpftool feature detection + sed -i -e 's/-Werror//g' src/Makefile.feature || die + + # remove hardcoded/unhelpful flags from bpftool + sed -e '/CFLAGS += -O2$/d' \ + -e '/CFLAGS += -W$/d' \ + -e '/CFLAGS += -Wextra$/d' \ + -i src/Makefile || die + + # always build bpf bits with std=gnu11 for kernel compatibility (bug 955156) + sed -i 's/-fno-stack-protector/& -std=gnu11/g' src/Makefile || die + + if ! use clang; then + # make people aware of what they are doing + ewarn "Using bpf-toolchain instead of clang due to USE=-clang." + ewarn "Please report any odd behaviours you observe, since using gcc for BPF" + ewarn "is still under development in both the Linux kernel and gcc itself." + + # prevent attribute warning about preserve_access_index + # since gcc does not support '#pragma clang attribute push': + # https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=675b4e2 + sed -i 's/std=gnu11/& -DBPF_NO_PRESERVE_ACCESS_INDEX/g' src/Makefile || die + + # remove bpf target & add assembly annotations to fix CO-RE feature detection + sed -i -e 's/-target bpf/-dA/' src/Makefile.feature || die + + # remove bpf target from skeleton build + sed -i -e 's/--target=bpf//g' src/Makefile || die + fi + + # Use rst2man or rst2man.py depending on which one exists (#930076) + type -P rst2man >/dev/null || sed -i -e 's/rst2man/rst2man.py/g' docs/Makefile || die +} + +src_configure() { + # filter LTO after discussion about UB in libbpf: + # https://lore.kernel.org/bpf/20260321024446.692008-1-irogers@google.com/ + filter-lto + + default +} + +bpftool_make() { + # which BPF compiler should we use? + if use clang; then + export CLANG="$(get_llvm_prefix -b)/bin/clang" + export LLVM_STRIP="$(get_llvm_prefix -b)/bin/llvm-strip" + else + # use bpf-toolchain + export CLANG="bpf-unknown-none-gcc" + export LLVM_STRIP="bpf-unknown-none-strip" + fi + + tc-export AR CC LD + + # add EXTRA_CFLAGS to be consistent with our libbpf ebuild + local extra_cflags="-fno-strict-aliasing" + + emake \ + ARCH="$(tc-arch-kernel)" \ + EXTRA_CFLAGS="${extra_cflags}" \ + HOSTAR="$(tc-getBUILD_AR)" \ + HOSTCC="$(tc-getBUILD_CC)" \ + HOSTLD="$(tc-getBUILD_LD)" \ + bash_compdir="$(get_bashcompdir)" \ + feature-libcap="$(usex caps 1 0)" \ + feature-llvm="$(usex llvm 1 0)" \ + prefix="${EPREFIX}"/usr \ + V=1 \ + "$@" +} + +src_compile() { + bpftool_make -C src + bpftool_make -C docs +} + +src_install() { + bpftool_make DESTDIR="${D}" -C src install + bpftool_make mandir="${ED}"/usr/share/man -C docs install +} diff --git a/dev-util/mesa_clc/Manifest b/dev-util/mesa_clc/Manifest index b05099f0ae47..19c572a7fa92 100644 --- a/dev-util/mesa_clc/Manifest +++ b/dev-util/mesa_clc/Manifest @@ -2,3 +2,4 @@ DIST mesa-26.1.8.tar.xz 77827888 BLAKE2B 173997a90bbb27d8f6b1e4c349430d063a9264e DIST mesa-26.2.2.tar.xz 68533264 BLAKE2B a9924a7cbac5dbdb4d93ba8499c49a6be51edd8ec5b258f2986a5690b04f6898012389dcc7e07097bad0defed52e9255d3a2c82bb6101a531b17fde82ef69afc SHA512 55b1234c8653dad9b85ba3de7b777a0d957909baf4628f0c00e21d4ba89c004cb687a1dcf8da46ed0a092231dd4f859027509f83ad8c558750845ba470902615 DIST mesa-26.2.3.tar.xz 68561540 BLAKE2B 53e451060bdc0df6ea8f7b5d3a23563c4bf56613c0871a91c5f0570649fcfe35e0dc1c0fc7834642621fc0772d0614b6a60acf196e82d4b77132629307c4a82e SHA512 ec621735ae4679762ea27b979a5314c7aecfdd4165afea19c8c55f933d79b40f01fa041a95874fc4347e732ac565786d9b616a9aa8856a422204d9ecc9a12ec4 DIST mesa-26.2.4.tar.xz 68613948 BLAKE2B 28506a9366e3c031acbb52924e43b0ec098f44a3ac3bb839ec2a86fa0faf83652347704b0e58bdc447aaebae5a2a254e5456e0b893755fd7721e57d6b614ecc4 SHA512 42e793561661a567e72c7324d071ab9f82d3a9f1e000827ed989b8d5fcc2cd16e50c02808d43465b3135c25a3cad77649ba0bf9e35d95b848f8fc8d8b9ce327c +DIST mesa-26.2.5.tar.xz 68652756 BLAKE2B cfcc4d72c5e6f54904c77b98c65df8159c41ebe9c4b8f82a18a38983bfe06e25c02ea20ba613753838f2455edb156a2d79938f28328313a8eb79fe67d2ea9624 SHA512 08c7d7ea426ddfb0f7bc51b06e18c363a4d31cf1a8b203828bb455eda0189e41d2215eb9f627585ed11e0d0f231a6a8c9f0c7176238760cd76b62e20c35ccd6a diff --git a/dev-util/mesa_clc/mesa_clc-26.2.5.ebuild b/dev-util/mesa_clc/mesa_clc-26.2.5.ebuild new file mode 100644 index 000000000000..4b4d408ec3b6 --- /dev/null +++ b/dev-util/mesa_clc/mesa_clc-26.2.5.ebuild @@ -0,0 +1,124 @@ +# Copyright 2023-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +LLVM_COMPAT=( {18..22} ) +PYTHON_COMPAT=( python3_{13..14} ) + +inherit llvm-r2 meson python-any-r1 + +MY_PV="${PV/_/-}" + +DESCRIPTION="mesa_clc tool used for building OpenCL C to SPIR-V" +HOMEPAGE="https://mesa3d.org/" + +if [[ ${PV} == 9999 ]]; then + S="${WORKDIR}/mesa_clc-${MY_PV}" + EGIT_REPO_URI="https://gitlab.freedesktop.org/mesa/mesa.git" + inherit git-r3 +else + S="${WORKDIR}/mesa-${MY_PV}" + SRC_URI="https://archive.mesa3d.org/mesa-${MY_PV}.tar.xz" + KEYWORDS="~amd64 ~arm ~arm64 ~riscv ~x86" +fi + +LICENSE="MIT" +SLOT="0" + +VIDEO_CARDS="asahi panfrost" +for card in ${VIDEO_CARDS}; do + IUSE_VIDEO_CARDS+=" video_cards_${card}" +done +IUSE="${IUSE_VIDEO_CARDS} debug" + +RDEPEND=" + dev-util/spirv-tools + $(llvm_gen_dep ' + dev-util/spirv-llvm-translator:${LLVM_SLOT} + llvm-core/clang:${LLVM_SLOT}= + llvm-core/llvm:${LLVM_SLOT}= + =llvm-runtimes/libclc-${LLVM_SLOT}* + ') +" +DEPEND="${RDEPEND} + dev-libs/expat + >=virtual/zlib-1.2.8:= + x11-libs/libdrm +" +BDEPEND=" + ${PYTHON_DEPS} + $(python_gen_any_dep " + >=dev-python/mako-0.8.0[\${PYTHON_USEDEP}] + dev-python/packaging[\${PYTHON_USEDEP}] + dev-python/pyyaml[\${PYTHON_USEDEP}] + ") + virtual/pkgconfig +" + +python_check_deps() { + python_has_version -b ">=dev-python/mako-0.8.0[${PYTHON_USEDEP}]" && + python_has_version -b "dev-python/packaging[${PYTHON_USEDEP}]" && + python_has_version -b "dev-python/pyyaml[${PYTHON_USEDEP}]" || return 1 +} + +pkg_setup() { + llvm-r2_pkg_setup + python-any-r1_pkg_setup +} + +src_configure() { + tools_enable video_cards_asahi asahi + tools_enable video_cards_panfrost panfrost + + tools_list() { + local tools="$(sort -u <<< "${1// /$'\n'}")" + echo "${tools//$'\n'/,}" + } + + PKG_CONFIG_PATH="$(get_llvm_prefix)/$(get_libdir)/pkgconfig" + + use debug && EMESON_BUILDTYPE=debug + + local emesonargs=( + -Dallow-broken-lto=true + -Dllvm=enabled + -Dshared-llvm=enabled + -Dmesa-clc=enabled + -Dinstall-mesa-clc=true + -Dprecomp-compiler=enabled + -Dinstall-precomp-compiler=true + -Dtools=$(tools_list "${TOOLS[*]}") + + -Dgallium-drivers='' + -Dvulkan-drivers='' + + # Set platforms empty to avoid the default "auto" setting. If + # platforms is empty meson.build will add surfaceless. + -Dplatforms='' + + -Dglx=disabled + -Dlibunwind=disabled + -Dzstd=disabled + + -Db_ndebug=$(usex debug false true) + ) + + meson_src_configure +} + +src_install() { + dobin "${BUILD_DIR}"/src/compiler/clc/mesa_clc + dobin "${BUILD_DIR}"/src/compiler/spirv/vtn_bindgen2 + use video_cards_asahi && dobin "${BUILD_DIR}"/src/asahi/clc/asahi_clc + use video_cards_panfrost && dobin "${BUILD_DIR}"/src/panfrost/clc/panfrost_compile +} + +# $1 - VIDEO_CARDS flag (check skipped for "--") +# other args - names of tools to enable +tools_enable() { + if [[ $1 == -- ]] || use $1; then + shift + TOOLS+=("$@") + fi +} diff --git a/dev-util/reuse/Manifest b/dev-util/reuse/Manifest index d9598e7002fe..4b827be78283 100644 --- a/dev-util/reuse/Manifest +++ b/dev-util/reuse/Manifest @@ -1,5 +1,2 @@ -DIST reuse-2.1.0.gh.tar.gz 260786 BLAKE2B 371c1167e10449ecdbaf0a93dbfa7d50c700daaf71394379a48d1baaae0b1be03728e79fd551da75a3743e06cae1a77e7445482222845d1356a6af3eb39bef46 SHA512 912d713b2c68029cc67a5fbebfca61d96165ae8ae9d927bbdd651cf2130104b297d5c0133f1a46442cba3ec84d00ba92c27414bff770d2250f5a2131c7ad4aef -DIST reuse-3.0.1.gh.tar.gz 305441 BLAKE2B e2f0cc4b4aea3e2b09e4ee395ec545a6cd683a083d05498b56401782439882853673bfb3d589ef6c4986cd86fcab64b9cc5ae40e5ddc7f36478582ca458fff81 SHA512 eb3aeb98b6fe127001702cf2a2c067e298fba071582206174a3647ccbf38263fa71dc6e56558926e4adce628c541035e4bce2afaa9fddfd89cbdb20cfe57aef5 -DIST reuse-3.0.2.gh.tar.gz 305529 BLAKE2B 96835aa6c47a951d7515236a632895e22ea93cad3602c4c37f45714ebe830b605315af479b7e5bbad764420ffc59997a163bbbe7a1b133bcd72f88af60b67c80 SHA512 cdf37980dddb116823fd0f0d47f6b6380e9da500cb3f33cf4fcfb75c367af7a7e3147848463f3d70c28dce72b8c0ad5d0b8284a69f7905050b0ba9ee7fff2f5c DIST reuse-5.0.2.gh.tar.gz 398518 BLAKE2B 11687ee3f396bde63248ce23311775ebddcc7424a0a3106a70d7cf4e00af391e65f0bf0d68310babb0ecc8522b8486a5c51f1b2953d54ed3863bd68a09ae23d5 SHA512 472048dcb16df330478727278de593e6e102fe224d7acd485666d3d2ceb49725426e008e1c116c41acea3c767de347cf5e4775c1471cb287c5d594860db75a5a DIST reuse-6.2.0.tar.gz 508748 BLAKE2B 74ea97df574c820f11fd85f2bac8375f180ce7d4d69740eb89661fbbb8881a88a251b6f044f6445cd6cfcb2c8cf7b0614413d35cddf7fd5da25a41e8907c386a SHA512 dc951c2a27b5737b30039e88a2a6427bec4a154245fece5769695e94561cc0f85523a7b24a995e9b3cd9f78153d8ae7feff893676ff3f0840b0eaa3c1cd052d9 diff --git a/dev-util/reuse/files/reuse-2.1.0_docs.patch b/dev-util/reuse/files/reuse-2.1.0_docs.patch deleted file mode 100644 index a9f19a8ea0a4..000000000000 --- a/dev-util/reuse/files/reuse-2.1.0_docs.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- a/docs/conf.py -+++ b/docs/conf.py -@@ -189,9 +189,9 @@ - """Copy the markdown files from the root of the project into the docs/ - directory. - """ -- copyfile("../README.md", "readme.md") -- copyfile("../CHANGELOG.md", "history.md") -- copyfile("../CONTRIBUTING.md", "contributing.md") -+ copyfile("README.md", "docs/readme.md") -+ copyfile("CHANGELOG.md", "docs/history.md") -+ copyfile("CONTRIBUTING.md", "docs/contributing.md") - - - def setup(app): diff --git a/dev-util/reuse/reuse-2.1.0.ebuild b/dev-util/reuse/reuse-2.1.0.ebuild deleted file mode 100644 index c6fd9ba6595c..000000000000 --- a/dev-util/reuse/reuse-2.1.0.ebuild +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2022-2024 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) -DISTUTILS_USE_PEP517=poetry - -inherit distutils-r1 - -DESCRIPTION="Manage license information according to the SPDX standard" -HOMEPAGE="https://reuse.software/ - https://github.com/fsfe/reuse-tool" -MY_PN="${PN}-tool" -SRC_URI="https://github.com/fsfe/${MY_PN}/archive/refs/tags/v${PV}.tar.gz -> ${P}.gh.tar.gz" -S="${WORKDIR}/${MY_PN}-${PV}" - -LICENSE="GPL-3+ CC-BY-SA-4.0 CC0-1.0 Apache-2.0" -SLOT="0" -KEYWORDS="amd64" - -RDEPEND=">=dev-python/binaryornot-0.4.4[${PYTHON_USEDEP}] - >=dev-python/boolean-py-3.8[${PYTHON_USEDEP}] - >=dev-python/jinja2-3.0.0[${PYTHON_USEDEP}] - >=dev-python/license-expression-1.0[${PYTHON_USEDEP}] - >=dev-python/python-debian-0.1.48[${PYTHON_USEDEP}]" - -BDEPEND="sys-devel/gettext - test? ( - dev-vcs/git - dev-vcs/mercurial - )" - -PATCHES=( "${FILESDIR}/reuse-2.1.0_docs.patch" ) - -DOCS=( AUTHORS.rst CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md README.md ) - -distutils_enable_tests pytest - -# dev-python/sphinx-autodoc-typehints will be dropped after 2.1.0 -distutils_enable_sphinx docs \ - ">=dev-python/furo-2023.3.27" \ - ">=dev-python/recommonmark-0.7.1" \ - ">=dev-python/sphinxcontrib-apidoc-0.3.0" \ - ">=dev-python/sphinx-autodoc-typehints-1.12.0" - -python_test() { - cd "${T}" || die - epytest "${S}"/tests -} diff --git a/dev-util/reuse/reuse-3.0.1.ebuild b/dev-util/reuse/reuse-3.0.1.ebuild deleted file mode 100644 index c6fd9ba6595c..000000000000 --- a/dev-util/reuse/reuse-3.0.1.ebuild +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2022-2024 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) -DISTUTILS_USE_PEP517=poetry - -inherit distutils-r1 - -DESCRIPTION="Manage license information according to the SPDX standard" -HOMEPAGE="https://reuse.software/ - https://github.com/fsfe/reuse-tool" -MY_PN="${PN}-tool" -SRC_URI="https://github.com/fsfe/${MY_PN}/archive/refs/tags/v${PV}.tar.gz -> ${P}.gh.tar.gz" -S="${WORKDIR}/${MY_PN}-${PV}" - -LICENSE="GPL-3+ CC-BY-SA-4.0 CC0-1.0 Apache-2.0" -SLOT="0" -KEYWORDS="amd64" - -RDEPEND=">=dev-python/binaryornot-0.4.4[${PYTHON_USEDEP}] - >=dev-python/boolean-py-3.8[${PYTHON_USEDEP}] - >=dev-python/jinja2-3.0.0[${PYTHON_USEDEP}] - >=dev-python/license-expression-1.0[${PYTHON_USEDEP}] - >=dev-python/python-debian-0.1.48[${PYTHON_USEDEP}]" - -BDEPEND="sys-devel/gettext - test? ( - dev-vcs/git - dev-vcs/mercurial - )" - -PATCHES=( "${FILESDIR}/reuse-2.1.0_docs.patch" ) - -DOCS=( AUTHORS.rst CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md README.md ) - -distutils_enable_tests pytest - -# dev-python/sphinx-autodoc-typehints will be dropped after 2.1.0 -distutils_enable_sphinx docs \ - ">=dev-python/furo-2023.3.27" \ - ">=dev-python/recommonmark-0.7.1" \ - ">=dev-python/sphinxcontrib-apidoc-0.3.0" \ - ">=dev-python/sphinx-autodoc-typehints-1.12.0" - -python_test() { - cd "${T}" || die - epytest "${S}"/tests -} diff --git a/dev-util/reuse/reuse-3.0.2.ebuild b/dev-util/reuse/reuse-3.0.2.ebuild deleted file mode 100644 index 7af15d3a41e5..000000000000 --- a/dev-util/reuse/reuse-3.0.2.ebuild +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2022-2024 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) -DISTUTILS_USE_PEP517=poetry - -inherit distutils-r1 - -DESCRIPTION="Manage license information according to the SPDX standard" -HOMEPAGE="https://reuse.software/ - https://github.com/fsfe/reuse-tool" -MY_PN="${PN}-tool" -SRC_URI="https://github.com/fsfe/${MY_PN}/archive/refs/tags/v${PV}.tar.gz -> ${P}.gh.tar.gz" -S="${WORKDIR}/${MY_PN}-${PV}" - -LICENSE="GPL-3+ CC-BY-SA-4.0 CC0-1.0 Apache-2.0" -SLOT="0" -KEYWORDS="~amd64" - -RDEPEND=">=dev-python/binaryornot-0.4.4[${PYTHON_USEDEP}] - >=dev-python/boolean-py-3.8[${PYTHON_USEDEP}] - >=dev-python/jinja2-3.0.0[${PYTHON_USEDEP}] - >=dev-python/license-expression-1.0[${PYTHON_USEDEP}] - >=dev-python/python-debian-0.1.48[${PYTHON_USEDEP}]" - -BDEPEND="sys-devel/gettext - test? ( - dev-vcs/git - dev-vcs/mercurial - )" - -PATCHES=( "${FILESDIR}/reuse-2.1.0_docs.patch" ) - -DOCS=( AUTHORS.rst CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md README.md ) - -distutils_enable_tests pytest - -# dev-python/sphinx-autodoc-typehints will be dropped after 2.1.0 -distutils_enable_sphinx docs \ - ">=dev-python/furo-2023.3.27" \ - ">=dev-python/recommonmark-0.7.1" \ - ">=dev-python/sphinxcontrib-apidoc-0.3.0" \ - ">=dev-python/sphinx-autodoc-typehints-1.12.0" - -python_test() { - cd "${T}" || die - epytest "${S}"/tests -} diff --git a/eclass/llvm.org.eclass b/eclass/llvm.org.eclass index 428d171fee00..5a6cf771d9b7 100644 --- a/eclass/llvm.org.eclass +++ b/eclass/llvm.org.eclass @@ -308,9 +308,6 @@ llvm.org_set_globals() { 22*) LLVM_MANPAGE_DIST="llvm-${LLVM_MAJOR}.1.0-r2-manpages.tar.xz" ;; - 23*) - LLVM_MANPAGE_DIST="llvm-${LLVM_MAJOR}.1.4-manpages.tar.xz" - ;; esac fi diff --git a/eclass/nginx.eclass b/eclass/nginx.eclass index d517c15ef727..80d3a5bc965f 100644 --- a/eclass/nginx.eclass +++ b/eclass/nginx.eclass @@ -436,6 +436,11 @@ _ngx_set_mod_depend() { [stream_geoip]="dev-libs/geoip" [stream_ssl]="dev-libs/openssl:=" ) + if ver_test -ge "1.31.7-r1" && ver_test -lt "1.31.100"; then + # Bug 984654. + COMMON_DEPEND[http_geoip]+=" dev-libs/libmaxminddb:=" + COMMON_DEPEND[stream_geoip]+=" dev-libs/libmaxminddb:=" + fi local COMMON_DEPEND_DEF # Bash does not have an easy way to copy an associative array, so its value # is obtained using the 'declare' builtin. diff --git a/gui-apps/ambient-background/ambient-background-1.0.0-r16.ebuild b/gui-apps/ambient-background/ambient-background-1.0.0-r16.ebuild new file mode 100644 index 000000000000..6d6ee09217e8 --- /dev/null +++ b/gui-apps/ambient-background/ambient-background-1.0.0-r16.ebuild @@ -0,0 +1,75 @@ +# Copyright 2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 +PYTHON_COMPAT=( python3_{13..14} ) +inherit toolchain-funcs python-single-r1 + +DESCRIPTION="Modular Wayfire live wallpapers, per-monitor idle fading and native session integration" +HOMEPAGE="" +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64" +IUSE="+mcp +pipewire" +REQUIRED_USE="${PYTHON_REQUIRED_USE}" + +BDEPEND="dev-libs/wayland dev-libs/wayland-protocols virtual/pkgconfig" +DEPEND=" + media-libs/glm + dev-libs/json-c:= + dev-libs/wayland + >=gui-wm/wayfire-0.11.0-r59:0= + media-libs/freetype:2 + media-libs/mesa[egl(+),gles2(+)] + pipewire? ( media-video/pipewire:= ) + sys-libs/pam + x11-libs/libxkbcommon +" +RDEPEND=" + ${DEPEND} + ${PYTHON_DEPS} + media-libs/fontconfig + !gui-apps/digitalrain + >=gui-wm/wayfire-0.11.0-r60:0= +" + +src_unpack() { + mkdir -p "${S}" || die + cp -a "${FILESDIR}/${PF}/." "${S}/" || die +} + +src_compile() { + emake PIPEWIRE="$(usex pipewire 1 0)" CC="$(tc-getCC)" CXX="$(tc-getCXX)" \ + CFLAGS="${CPPFLAGS} ${CFLAGS} -std=c11" \ + CXXFLAGS="${CPPFLAGS} ${CXXFLAGS} -std=c++17" LDFLAGS="${LDFLAGS}" \ + PREFIX="${EPREFIX}/usr" +} + +src_install() { + emake PIPEWIRE="$(usex pipewire 1 0)" CC="$(tc-getCC)" CXX="$(tc-getCXX)" \ + CFLAGS="${CPPFLAGS} ${CFLAGS} -std=c11" \ + CXXFLAGS="${CPPFLAGS} ${CXXFLAGS} -std=c++17" LDFLAGS="${LDFLAGS}" \ + PREFIX="${EPREFIX}/usr" DESTDIR="${D}" install + python_fix_shebang "${ED}/usr/bin/ambient-background" + if use mcp; then + python_fix_shebang "${ED}/usr/bin/ambient-background-mcp" + else + rm "${ED}/usr/bin/ambient-background-mcp" "${ED}/usr/bin/digitalrain-mcp" || die + fi + insinto /etc + newins effects/digital-rain/digitalrain.toml.default ambient-background.toml + dosym ambient-background.toml /etc/digitalrain.toml + dodoc docs/README.md effects/digital-rain/{configuration,interfaces-and-locking,alerts-and-audio,adaptive-rendering,hide-desktop,stitched-outputs,monitor-power,video-inhibition,live-config-alerts-audio-plan}.md + dodoc effects/digital-rain/tomlc17.{LICENSE,UPSTREAM} + dodoc effects/nebula/UPSTREAM effects/nebula/LICENSE + newdoc effects/digital-rain/digitalrain.toml.default ambient-background.toml.example +} + +pkg_postinst() { + elog "WCM → Desktop → Ambient Background owns live wallpaper and idle settings." + elog "Hyperspace, Nebula and Digital Rain have independent appearance and background controls." + elog "Background defaults to None for all effects." + elog "Idle/locking/audio configuration: ~/.config/ambient-background.toml or /etc/ambient-background.toml." + elog "Documentation: ${EROOT}/usr/share/doc/${PF}/" + elog "Legacy digitalrain commands remain compatibility aliases for existing scripts." +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/Makefile b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/Makefile new file mode 100644 index 000000000000..1612d495af16 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/Makefile @@ -0,0 +1,38 @@ +CXX ?= c++ +CXXFLAGS ?= -O2 -std=c++17 +CPPFLAGS ?= +LDFLAGS ?= +PREFIX ?= /usr +DESTDIR ?= +PIPEWIRE ?= 1 +NEBULA_FILES := effects/nebula/nebula-background.cpp effects/nebula/nebula-background.hpp effects/nebula/nebula-shaders.tpp effects/nebula/nebula-field.tpp +WF_CFLAGS := $(shell pkg-config --cflags wayfire wf-utils) +WF_LIBS := $(shell pkg-config --libs wayfire wf-utils) +WF_PLUGINDIR := $(shell pkg-config --variable=plugindir wayfire) +WF_METADATADIR := $(shell pkg-config --variable=metadatadir wayfire) + +.PHONY: all rain install +all: rain libambient-background.so +rain: + $(MAKE) -C effects/digital-rain PIPEWIRE=$(PIPEWIRE) all +libambient-background.so: $(NEBULA_FILES) core/ambient-background.cpp include/ambient-api.hpp include/effect-settings.inc effects/hyperspace/hyperspace-background.cpp effects/hyperspace/hyperspace-background.hpp effects/hyperspace/hyperspace-shaders.tpp effects/hyperspace/hyperspace-anomalies.tpp effects/hyperspace/hyperspace-asteroids.tpp effects/hyperspace/hyperspace-asteroid-shaders.tpp effects/hyperspace/hyperspace-galaxies.tpp effects/hyperspace/hyperspace-galaxy-shaders.tpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -fPIC -shared -DWAYFIRE_PLUGIN -DWLR_USE_UNSTABLE $(WF_CFLAGS) -Iinclude -Ieffects/hyperspace -Ieffects/nebula -o $@ core/ambient-background.cpp effects/hyperspace/hyperspace-background.cpp effects/nebula/nebula-background.cpp $(LDFLAGS) $(WF_LIBS) +install: all + $(MAKE) -C effects/digital-rain PIPEWIRE=$(PIPEWIRE) PREFIX=$(PREFIX) DESTDIR=$(DESTDIR) install + mv $(DESTDIR)$(PREFIX)/bin/digitalrain $(DESTDIR)$(PREFIX)/bin/ambient-idle + mv $(DESTDIR)$(PREFIX)/bin/digitalrain-lock $(DESTDIR)$(PREFIX)/bin/ambient-lock + mv $(DESTDIR)$(PREFIX)/bin/digitalrain-auth $(DESTDIR)$(PREFIX)/bin/ambient-auth + mv $(DESTDIR)$(PREFIX)/bin/digitalrain-mcp $(DESTDIR)$(PREFIX)/bin/ambient-background-mcp + ln -s ambient-idle $(DESTDIR)$(PREFIX)/bin/digitalrain + ln -s ambient-lock $(DESTDIR)$(PREFIX)/bin/digitalrain-lock + ln -s ambient-auth $(DESTDIR)$(PREFIX)/bin/digitalrain-auth + ln -s ambient-background-mcp $(DESTDIR)$(PREFIX)/bin/digitalrain-mcp +ifeq ($(PIPEWIRE),1) + mv $(DESTDIR)$(PREFIX)/bin/digitalrain-audio $(DESTDIR)$(PREFIX)/bin/ambient-audio + ln -s ambient-audio $(DESTDIR)$(PREFIX)/bin/digitalrain-audio +endif + install -Dm755 libambient-background.so $(DESTDIR)$(WF_PLUGINDIR)/libambient-background.so + install -Dm755 libambient-background.so $(DESTDIR)$(WF_PLUGINDIR)/ambient-background-r17/libambient-background.so + install -Dm755 libambient-background.so $(DESTDIR)$(WF_PLUGINDIR)/ambient-background-r18/libambient-background.so + install -Dm755 controller/ambient-background.py $(DESTDIR)$(PREFIX)/bin/ambient-background + install -Dm644 metadata/ambient-background.xml $(DESTDIR)$(WF_METADATADIR)/ambient-background.xml diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/controller/ambient-background.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/controller/ambient-background.py new file mode 100755 index 000000000000..a304f84eace9 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/controller/ambient-background.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python3 +"""Per-physical-monitor ambient controller; no credentials or desktop capture. + +The compositor owns rendering, bindings and reversible per-output composition. This +ordinary-user process reads Digital Rain's proven activity/inhibition clocks. +A connection-bound ownership lease leaves those clocks running and restores +legacy saver behavior automatically if this process disappears. +""" +import argparse +import fcntl +import json +import os +from pathlib import Path +import signal +import socket +import struct +import subprocess +import sys +import time +import tomllib + +MAX_MESSAGE = 1024 * 1024 +STOP = False + + +def peer(stream): + pid, uid, _ = struct.unpack('3i', stream.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, 12)) + if uid != os.geteuid(): + raise PermissionError('Ambient IPC requires the same desktop user') + return pid + + +def connect(path, kind=socket.SOCK_STREAM): + stream = socket.socket(socket.AF_UNIX, kind) + stream.settimeout(2) + try: + stream.connect(str(path)) + peer(stream) + return stream + except BaseException: + stream.close() + raise + + +def read(stream, count): + result = bytearray() + while len(result) < count: + part = stream.recv(count - len(result)) + if not part: + raise ConnectionError('Ambient IPC disconnected') + result.extend(part) + return result + + +def ipc(method, data=None): + with connect(os.environ['WAYFIRE_SOCKET']) as stream: + payload = json.dumps({'method': method, 'data': data or {}}).encode() + stream.sendall(struct.pack('=I', len(payload)) + payload) + count, = struct.unpack('=I', read(stream, 4)) + if not 0 < count <= MAX_MESSAGE: + raise ValueError('Oversized compositor response') + result = json.loads(read(stream, count)) + if isinstance(result, dict) and result.get('error'): + raise RuntimeError(result['error']) + return result + + +def dr_request(stream, method, **data): + stream.send(json.dumps({'version': 1, 'method': method, **data}).encode()) + payload, _, flags, _ = stream.recvmsg(MAX_MESSAGE) + if flags & socket.MSG_TRUNC or not payload: + raise ConnectionError('Invalid Digital Rain response') + result = json.loads(payload) + if result.get('error'): + raise RuntimeError(result['error']) + return result + + +def clamp(value): + return min(1., max(0., value)) + + +def phase(options, output, config): + """Pure timeline: UI fades, backdrop blackens, then the animation fades. + + Legacy black delays begin at idle activation, never at daemon startup. + Zero delays retain DR's meaning of a disabled stage. + """ + if output.get('application_inhibited') or output.get('timed_inhibited'): + return {'opacity': 1., 'black': 0., 'effect': 1.} + delay = options['focused_delay'] if output['focused'] else options['inactive_delay'] + override = config.get('output_timeouts', {}).get(output['name'], 0) + if not output['focused'] and override > 0: + delay = override + age = max(0., output['idle_seconds'] - delay) + fade = max(.05, options['fade_seconds']) + alpha = 1. - clamp(age / fade) * (1. - clamp(options['min_opacity'])) + matrix = config.get('matrix', {}) + transparent = True if 'matrix' in options else matrix.get('TRANSPARENT', True) + black = 0. if transparent else 1. + effect = 1. + if options['black_enabled'] and output['idle_seconds'] >= delay: + seconds = options.get('black_fade_duration', max(1, matrix.get('FADE_FRAMES', 20)) / 30.) + black_delay = options.get('background_black_delay', matrix.get('TRANSPARENT_TIMEOUT', 600)) + if black_delay > 0: + black = clamp((age - black_delay) / seconds) + rain_delay = options.get('effect_black_delay', matrix.get('BLACK_RAIN_TIMEOUT', 600)) + black_start = black_delay + seconds if transparent else 0 + if rain_delay > 0 and (not transparent or black_delay > 0): + effect = 1. - clamp((age - black_start - rain_delay) / seconds) + return {'opacity': alpha, 'black': black, 'effect': effect} + + +class Matrix: + def __init__(self, name, config, binary): + m = config.get('matrix', {}) + args = [binary, '--render', '--ambient', '--output', name, '--transparent', + '--transparent-timeout', '0', '--black-rain-timeout', '0', '--fade-frames', '1'] + for key in ('FONT_SIZE', 'DEPTH_LAYERS', 'MIN_SPEED', 'MAX_SPEED', 'AVG_SPEED', + 'MIN_TRAIL', 'MAX_TRAIL', 'MIN_DENSITY', 'MAX_DENSITY', 'HEAD_HOLD_FRAMES', + 'HEAD_MUTATION_CHANCE', 'HEAD_WHITE_MIN', 'HEAD_WHITE_MAX', + 'LAYER_NEAR_DENSITY', 'LAYER_FAR_DENSITY'): + if key in m: + args += ['--' + key.lower().replace('_', '-'), str(m[key])] + args += ['--color', config.get('output_colors', {}).get(name, m.get('COLOR', 'green'))] + self.channel, child = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.channel.setblocking(False) + args += ['--report-fd', str(child.fileno())] + try: + self.process = subprocess.Popen(args, pass_fds=(child.fileno(),), + stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except BaseException: + self.channel.close() + raise + finally: + child.close() + + def alive(self): + # Drain bounded renderer reports so their channel never blocks rendering. + for _ in range(8): + try: + if not self.channel.recv(8192): + break + except BlockingIOError: + break + return self.process.poll() is None + + def close(self): + self.channel.close() # EOF is the renderer's explicit lifetime boundary. + try: + self.process.wait(timeout=1) + except subprocess.TimeoutExpired: + self.process.terminate() + try: + self.process.wait(timeout=1) + except subprocess.TimeoutExpired: + self.process.kill() + self.process.wait(timeout=1) + + +class Controller: + def __init__(self, binary): + self.binary = binary + self.dr = None + self.matrix = {} + self.config = {} + self.config_key = None + self.fingerprint = None + self.generation = None + self.error = None + self.manager_process = None + self.last_manager_start = 0. + + def release(self): + for child in self.matrix.values(): + child.close() + self.matrix.clear() + if self.dr: + self.dr.close() + self.dr = None + + def tick(self, options): + if not options['enabled']: + self.release() + return + if not self.dr: + socket_path = Path(os.environ['XDG_RUNTIME_DIR']) / 'digitalrain/control.sock' + if not socket_path.exists() and time.monotonic() - self.last_manager_start > 10: + self.last_manager_start = time.monotonic() + self.manager_process = subprocess.Popen([self.binary], stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True) + self.dr = connect(socket_path, socket.SOCK_SEQPACKET) + dr_request(self.dr, 'ambient', enabled=True) + if options.get('suspended', False): + # Retain idle/lock management and the ambient ownership lease, so + # the legacy idle saver cannot restart GPU animation while paused. + for child in self.matrix.values(): + child.close() + self.matrix.clear() + return + generation = options['reset_generation'] + if self.generation is not None and generation != self.generation: + dr_request(self.dr, 'wake') + self.generation = generation + status = dr_request(self.dr, 'status') + path = status.get('config_file', '') + key = (path, status.get('settings_revision')) + if key != self.config_key: + # DR validates edits before changing this revision. Retain the last + # valid settings if an editor is midway through a replacement. + try: + with open(path, 'rb') as handle: + candidate = tomllib.load(handle) + # Scalars come from the manager's validated current settings, + # including runtime API changes and defaults absent from TOML. + for qualified, entry in dr_request(self.dr, 'schema').items(): + group, name = qualified.split('.', 1) + candidate.setdefault(group, {})[name] = entry['value'] + self.config = candidate + self.config_key = key + except (OSError, ValueError): + pass + targets = {item['name'] for item in options['outputs']} + wanted = targets if options['backend'] == 'matrix' and status['lock'] == 'unlocked' else set() + appearance = self.config.copy() + if 'matrix' in options: + appearance['matrix'] = options['matrix'].copy() + if not appearance['matrix'].get('MONITOR_COLOURS', True): + appearance['output_colors'] = {} + # Keep temporary cross-field edits valid while changing sliders. + m = appearance['matrix'] + for minimum, maximum in [('MIN_SPEED', 'MAX_SPEED'), ('MIN_TRAIL', 'MAX_TRAIL'), + ('MIN_DENSITY', 'MAX_DENSITY'), ('HEAD_WHITE_MIN', 'HEAD_WHITE_MAX')]: + m[maximum] = max(m[minimum], m[maximum]) + m['AVG_SPEED'] = min(m['MAX_SPEED'], max(m['MIN_SPEED'], m['AVG_SPEED'])) + # Backdrop controls are composited natively and do not restart glyphs. + for key in ('BACKGROUND', 'BACKGROUND_OPACITY', 'MONITOR_COLOURS'): + m.pop(key, None) + fingerprint = json.dumps([appearance.get('matrix', {}), appearance.get('output_colors', {})], sort_keys=True) + for name in list(self.matrix): + if name not in wanted or fingerprint != self.fingerprint or not self.matrix[name].alive(): + self.matrix.pop(name).close() + for name in sorted(wanted): + if name not in self.matrix: + self.matrix[name] = Matrix(name, appearance, self.binary) + self.fingerprint = fingerprint + activity = {item['name']: item for item in status['outputs']} + updates = [] + for name in sorted(targets): + output = activity.get(name) + state = phase(options, output, self.config) if output and status['lock'] == 'unlocked' else { + 'opacity': 1., 'black': 0., 'effect': 1.} + updates.append({'name': name, **state}) + ipc('ambient/sync', {'outputs': updates}) + self.error = None + + +def main(): + parser = argparse.ArgumentParser(description='Continuous ambient background and per-monitor desktop fading') + parser.add_argument('--daemon', action='store_true', help='run the compositor helper') + parser.add_argument('--toggle', action='store_true', help='toggle ambient-only mode on all monitors') + parser.add_argument('--toggle-background', action='store_true', help='stop or resume ambient GPU rendering on all monitors') + parser.add_argument('--restore', action='store_true', help='restore desktop elements and keyboard focus') + parser.add_argument('--status', action='store_true') + parser.add_argument('--renderer', '--digitalrain', dest='digitalrain', default='/usr/bin/ambient-idle', help=argparse.SUPPRESS) + args = parser.parse_args() + if args.toggle_background or args.toggle or args.restore or args.status: + print(json.dumps(ipc('ambient/toggle-background' if args.toggle_background else 'ambient/toggle' if args.toggle else 'ambient/restore' if args.restore else 'ambient/status'), indent=2)) + return + if not args.daemon: + parser.print_help() + return + if os.geteuid() == 0: + parser.error('Run the ambient controller as the desktop user') + # One daemon per compositor, including independent test sessions. + runtime = Path(os.environ['XDG_RUNTIME_DIR']) + with connect(os.environ['WAYFIRE_SOCKET']) as stream: + pid = peer(stream) + handle = open(runtime / f'wayfire-ambient-{pid}.lock', 'w') + try: + fcntl.flock(handle, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + return + def stop(*_): + global STOP + STOP = True + signal.signal(signal.SIGTERM, stop) + signal.signal(signal.SIGINT, stop) + controller = Controller(args.digitalrain) + try: + while not STOP: + try: + options = ipc('ambient/status') + except (OSError, RuntimeError, ValueError): + break # compositor exit or plugin unload + try: + controller.tick(options) + except (OSError, RuntimeError, ValueError) as error: + controller.release() + message = str(error) + if message != controller.error: + print('Ambient controller:', message, file=sys.stderr, flush=True) + controller.error = message + # Fail open immediately; the plugin also has an independent watchdog. + try: + ipc('ambient/sync', {'outputs': [{'name': z['name'], 'opacity': 1., 'black': 0., 'effect': 1.} + for z in options['outputs']]}) + except (OSError, RuntimeError, ValueError): + break + time.sleep(.25) + finally: + controller.release() + handle.close() + + +if __name__ == '__main__': + main() diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/core/ambient-background.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/core/ambient-background.cpp new file mode 100644 index 000000000000..2fb658d447d8 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/core/ambient-background.cpp @@ -0,0 +1,519 @@ +#include "ambient-api.hpp" +#include "hyperspace-background.hpp" +#include "nebula-background.hpp" +#include <wayfire/core.hpp> +#include <wayfire/config/config-manager.hpp> +#include <wayfire/scene-operations.hpp> +#include <wayfire/output.hpp> +#include <wayfire/output-layout.hpp> +#include <wayfire/output-stitch.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/render-manager.hpp> +#include <wayfire/scene.hpp> +#include <wayfire/scene-render.hpp> +#include <wayfire/seat.hpp> +#include <wayfire/signal-definitions.hpp> +#include <wayfire/view.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> +#include <algorithm> +#include <chrono> +#include <cmath> +#include <map> +#include <vector> +extern "C" { +#include <wlr/types/wlr_compositor.h> +#include <wlr/types/wlr_keyboard.h> +} + +namespace { +using clock_t = std::chrono::steady_clock; +double now() { return std::chrono::duration<double>(clock_t::now().time_since_epoch()).count(); } +struct zone_t { + std::string name; + wf::geometry_t box; + float opacity = 1, target = 1, black = 0, effect = 1; +}; +class ambient_node_t : public wf::scene::node_t { + wf::output_t *output; + std::function<void(const wf::render_target_t&, const wf::regionf_t&)> paint; + std::function<wf::regionf_t()> coverage; + public: + ambient_node_t(wf::output_t *o, decltype(paint) p, decltype(coverage) c = {}) : + node_t(false), output(o), paint(p), coverage(c) {} + wf::geometry_t get_bounding_box() override { return output->get_layout_geometry(); } + class instance_t : public wf::scene::render_instance_t { + ambient_node_t *self; + public: + explicit instance_t(ambient_node_t *s) : self(s) {} + void schedule_instructions(std::vector<wf::scene::render_instruction_t>& list, + const wf::render_target_t& target, wf::regionf_t& damage) override { + auto clipped = damage & self->get_bounding_box(); + if (self->coverage) clipped &= self->coverage(); + if (clipped.empty()) return; + list.push_back({.instance=this, .target=target, .damage=clipped}); + // A translucent output cover must retain the desktop damage below. + if (!self->coverage) damage ^= clipped; + } + void render(const wf::scene::render_instruction_t& data) override { + auto box = self->get_bounding_box(); + auto damage = data.damage; + damage += wf::pointf_t{-double(box.x), -double(box.y)}; + auto target = data.target.translated({-box.x, -box.y}); + data.pass->custom_gles_subpass([&] { self->paint(target, damage); }); + } + }; + void gen_render_instances(std::vector<wf::scene::render_instance_uptr>& list, + wf::scene::damage_callback, wf::output_t *shown_on) override { + if (shown_on == output) list.push_back(std::make_unique<instance_t>(this)); + } +}; +class shield_t : public wf::scene::node_t { + std::function<std::vector<wf::geometry_t>()> regions; + public: + explicit shield_t(decltype(regions) fn) : node_t(false), regions(fn) {} + std::optional<wf::scene::input_node_t> find_node_at(const wf::pointf_t& point) override { + for (auto b : regions()) + if (point.x >= b.x && point.y >= b.y && point.x < b.x+b.width && point.y < b.y+b.height) + return wf::scene::input_node_t{this, point}; + return {}; + } +}; +class ambient_plugin_t : public wf::plugin_interface_t { + wf::option_wrapper_t<bool> enabled, black_enabled; + wf::option_wrapper_t<std::string> backend, controller; + wf::option_wrapper_t<double> focused_delay, inactive_delay, fade_seconds, wake_seconds, + min_opacity, manual_seconds; + wf::option_wrapper_t<wf::activatorbinding_t> toggle_binding, restore_binding, background_binding; + wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; + struct output_state_t { + std::vector<zone_t> zones; + std::unique_ptr<ambient_hyperspace_background> hyperspace; + std::unique_ptr<ambient_nebula_background> nebula; + wf::scene::node_ptr node, fade_node; + wf::auxilliary_buffer_t fade_buffer; + }; + std::map<wf::output_t*, output_state_t> outputs; + wf::scene::node_ptr shield; + wf::wl_timer<true> timer; + bool manual = false, suspended = false, daemon_started = false, cursor_hidden = false; + unsigned reset_generation = 0; + double last_tick = now(), heartbeat = 0, restore_until = 0, last_launch = 0; + std::weak_ptr<wf::view_interface_t> previous_focus; + + bool active() const { return enabled && !suspended; } + + template<typename T> void register_setting(const char *name, T value) { + auto section = wf::get_core().config->get_section("ambient-background"); + if (!section) { + section = std::make_shared<wf::config::section_t>("ambient-background"); + wf::get_core().config->merge_section(section); + } + auto old = section->get_option_or(name); + if (!std::dynamic_pointer_cast<wf::config::option_t<T>>(old)) { + auto typed = std::make_shared<wf::config::option_t<T>>(name, value); + if (old) typed->set_value_str(old->get_value_str()); + section->register_new_option(typed); + } + } + template<typename T> void option(wf::option_wrapper_t<T>& wrapper, const char *name, T value) { + register_setting<T>(name, value); + wrapper.load_option(std::string("ambient-background/") + name); + } + template<typename T> T setting(const char *name) const { + auto option = wf::get_core().config->get_option(std::string("ambient-background/") + name); + return std::dynamic_pointer_cast<wf::config::option_t<T>>(option)->get_value(); + } + static void insert_before(wf::scene::node_ptr node, wf::scene::layer layer) { + auto root = wf::get_core().scene(); + auto children = root->get_children(); + auto anchor = root->layers[(int)layer]; + auto it = std::find(children.begin(), children.end(), anchor); + children.insert(it, node); + root->set_children_list(children); + } + bool locked() const { + for (auto& [o, s] : outputs) if (o->is_inhibited()) return true; + return false; + } + static std::vector<wf::output_stitch_zone_t> targets(wf::output_t *o) { + if (auto stitch = o->get_data<wf::output_stitch_layout_t>()) + if (!stitch->zones.empty()) return stitch->zones; + return {{o->to_string(), o->get_relative_geometry()}}; + } + void update_zones(wf::output_t *o, output_state_t& state) { + auto old = state.zones; + state.zones.clear(); + for (auto z : targets(o)) { + zone_t zone{z.name, z.geometry}; + for (auto& p : old) if (p.name == zone.name) { + zone.opacity=p.opacity; zone.target=p.target; zone.black=p.black; zone.effect=p.effect; + } + state.zones.push_back(zone); + } + } + void add_output(wf::output_t *o) { + if (outputs.count(o)) return; + auto& state = outputs[o]; + update_zones(o, state); + state.hyperspace = std::make_unique<ambient_hyperspace_background>([o] { o->render->damage_whole(); }); + state.nebula = std::make_unique<ambient_nebula_background>([o] { o->render->damage_whole(); }); + state.nebula->set_active(active() && !locked() && std::string(backend) == "nebula"); + state.hyperspace->set_active(active() && !locked() && std::string(backend) == "hyperspace"); + o->add_activator(toggle_binding, &toggle); + o->add_activator(restore_binding, &restore); + o->add_activator(background_binding, &toggle_background); + if (active()) attach(o, state); + } + void attach(wf::output_t *o, output_state_t& state) { + if (state.node) return; + state.node = std::make_shared<ambient_node_t>(o, [this,o] (auto& t,auto& d) { paint(o,t,d); }); + insert_before(state.node, wf::scene::layer::BOTTOM); + state.fade_node = std::make_shared<ambient_node_t>(o, + [this,o] (auto& t,auto& d) { paint_fade(o,t,d); }, + [this,o] { return fade_coverage(o); }); + // Blend above the completed desktop (including Cube and launchers), + // but below screensavers and the protected session-lock layer. + insert_before(state.fade_node, wf::scene::layer::OVERLAY); + o->render->damage_whole(); + } + static bool belongs(wayfire_view view, wf::output_t *output, bool wallpaper = false) { + if (!view->is_mapped()) return false; + using layer = wf::scene::layer; + auto layers = wallpaper ? std::vector<layer>{layer::BACKGROUND} : + std::vector<layer>{layer::BOTTOM,layer::WORKSPACE,layer::TOP,layer::UNMANAGED,layer::OVERLAY,layer::DWIDGET}; + for (wf::scene::node_t *n=view->get_root_node().get(); n; n=n->parent()) + for (auto l:layers) if (n == output->node_for_layer(l).get()) return true; + return false; + } + wf::regionf_t fade_coverage(wf::output_t *o) { + wf::regionf_t covered; + if (!active() || locked() || !outputs.count(o)) return covered; + auto offset = wf::origin(o->get_layout_geometry()); + for (const auto& z:outputs.at(o).zones) + if (z.opacity < 1.0f) covered |= z.box + offset; + return covered; + } + void paint_fade(wf::output_t *o, const wf::render_target_t& target, const wf::regionf_t& damage) { + if (!active() || locked() || !outputs.count(o)) return; + auto& state = outputs.at(o); + bool needed = false; + for (const auto& z:state.zones) needed |= z.opacity < 1.0f; + if (!needed) return; + auto box = o->get_relative_geometry(); + state.fade_buffer.allocate(wf::dimensions(box), target.scale); + if (!state.fade_buffer.get_texture()) return; + wf::render_target_t image{state.fade_buffer}; + image.geometry = box; image.scale = target.scale; + wf::gles::bind_render_buffer(image); + // Reuse the same wallpaper renderer as the desktop and Cube backdrop. + // Composite its complete opaque image once, avoiding separate star/cloud + // alpha passes that would darken the desktop nonlinearly. + paint(o, image, box); + wf::gles::bind_render_buffer(target); + GL_CALL(glDisable(GL_DEPTH_TEST)); GL_CALL(glDisable(GL_CULL_FACE)); + auto texture = wf::gles_texture_t::from_aux(state.fade_buffer); + for (const auto& z:state.zones) { + float alpha = std::clamp(1.0f-z.opacity, 0.0f, 1.0f); + if (alpha <= 0) continue; + wf::gles::for_each_scissor_rect(target, damage & z.box, [&] { + OpenGL::render_texture(texture, target, box, glm::vec4(1,1,1,alpha)); + }); + } + GL_CALL(glDisable(GL_BLEND)); + } + void update_shield() { + auto cursor=wf::get_core().get_cursor_position(); + bool hide=manual; + for (auto& [o,s]:outputs) { + auto layout=o->get_layout_geometry(); + for (auto& z:s.zones) { + auto b=z.box;b.x+=layout.x;b.y+=layout.y; + if ((b & cursor) && z.opacity<.02f) hide=true; + } + } + hide &= active() && !locked(); + if (hide != cursor_hidden) { + if (hide) wf::get_core().hide_cursor(); else wf::get_core().unhide_cursor(); + cursor_hidden=hide; + } + bool needed = manual; + for (auto& [o,s]:outputs) for (auto& z:s.zones) needed |= z.opacity < .02f; + needed &= active() && !locked(); + if (needed && !shield) { + shield=std::make_shared<shield_t>([this] { + std::vector<wf::geometry_t> result; + for (auto& [o,s]:outputs) { + auto layout=o->get_layout_geometry(); + for (auto& z:s.zones) if (manual || z.opacity < .02f) { + auto b=z.box; b.x+=layout.x; b.y+=layout.y; result.push_back(b); + } + } + return result; + }); + insert_before(shield,wf::scene::layer::SCREENSAVER); + } else if (!needed && shield) { + wf::scene::remove_child(shield); shield.reset(); + } + if (shield) wf::scene::update(shield,wf::scene::update_flag::INPUT_STATE); + } + void set_manual(bool state) { + if (state == manual) return; + if (state && (!active() || locked())) return; + manual=state; + if (state) { if (auto v=wf::get_core().seat->get_active_view()) previous_focus=v->weak_from_this(); } + else { + ++reset_generation; restore_until=now()+1; + for (auto& [o,s]:outputs) for (auto& z:s.zones) { z.target=1; z.black=0; z.effect=1; } + if (auto view=previous_focus.lock()) if (view->is_mapped()) wf::get_core().seat->focus_view(view->self()); + previous_focus.reset(); + } + update_shield(); + } + wf::activator_callback toggle = [this] (auto) { if (!active() || locked()) return false; set_manual(!manual); return true; }; + wf::activator_callback toggle_background = [this] (auto) { + if (!enabled || locked()) return false; + suspended = !suspended; + ++reset_generation; restore_until = now()+1; + changed(); + return true; + }; + wf::activator_callback restore = [this] (auto) { if (!manual) return false; set_manual(false); return true; }; + wf::signal::connection_t<wf::input_event_signal<wlr_keyboard_key_event>> on_key = [this] (auto ev) { + if (manual && !locked()) ev->mode=wf::input_event_processing_mode_t::NO_CLIENT; + }; + void paint(wf::output_t *o,const wf::render_target_t& target,const wf::regionf_t& damage) { + auto it=outputs.find(o); if (it==outputs.end() || !enabled || locked()) return; + auto& state=it->second; + GL_CALL(glDisable(GL_DEPTH_TEST)); GL_CALL(glDisable(GL_CULL_FACE)); + wf::gles::for_each_scissor_rect(target,damage,[&] { + OpenGL::render_rectangle(o->get_relative_geometry(),{0,0,0,1},wf::gles::render_target_orthographic_projection(target)); + }); + auto prefix = std::string(backend); + if (prefix != "hyperspace" && prefix != "nebula") prefix = "matrix"; + bool wallpaper = suspended || setting<std::string>((std::string(prefix) + "_background").c_str()) == "wallpaper"; + float wallpaper_alpha = suspended ? 1.0f : std::clamp(setting<double>((std::string(prefix) + "_background_opacity").c_str()), 0.0, 1.0); + auto draw_background_clients = [&](bool matrix) { + // Import only wallpaper or this module's Matrix GPU buffers. + for (auto view:wf::get_core().get_all_views()) { + if (!belongs(view,o,true) || !view->get_wlr_surface()) continue; + bool ambient=view->get_app_id() == "ambient-matrix"; + if (ambient != matrix) continue; + auto texture=wlr_surface_get_texture(view->get_wlr_surface()); + if (!texture) continue; + auto box=view->get_bounding_box(); + wf::gles::for_each_scissor_rect(target,damage & box,[&] { + OpenGL::render_texture(wf::gles_texture_t(texture),target,box, + glm::vec4(1,1,1,matrix ? 1.0f : wallpaper_alpha)); + }); + } + }; + if (wallpaper && wallpaper_alpha > 0) draw_background_clients(false); + // Cube still delegates its backdrop while paused. Draw only the normal + // wallpaper on demand, rather than activating Cube's animated fallback. + if (suspended) { GL_CALL(glDisable(GL_BLEND)); return; } + for (auto& z:state.zones) { + float black=manual ? 0 : z.black; + if (black>0) wf::gles::for_each_scissor_rect(target,damage & z.box,[&] { + OpenGL::render_rectangle(z.box,{0,0,0,black},wf::gles::render_target_orthographic_projection(target)); + }); + } + if (std::string(backend) == "hyperspace") + state.hyperspace->render(target,damage,wf::dimensions(o->get_relative_geometry())); + else if (std::string(backend) == "nebula") + state.nebula->render(target,damage,wf::dimensions(o->get_relative_geometry())); + else draw_background_clients(true); + for (auto& z:state.zones) { + auto d=damage & z.box; + float alpha=manual ? 0 : 1-z.effect; + if (alpha > 0) wf::gles::for_each_scissor_rect(target,d,[&] { + OpenGL::render_rectangle(z.box,{0,0,0,alpha},wf::gles::render_target_orthographic_projection(target)); + }); + } + GL_CALL(glDisable(GL_BLEND)); + } + bool tick() { + double t=now(), dt=std::clamp(t-last_tick,0.0,.1); last_tick=t; + if (locked() && manual) set_manual(false); + bool stale=(heartbeat ? t-heartbeat>3 : daemon_started && t-last_launch>3); + if (stale && manual) set_manual(false); + for (auto& [o,s]:outputs) { + update_zones(o,s); + s.hyperspace->set_active(active() && !locked() && std::string(backend)=="hyperspace"); + s.nebula->set_active(active() && !locked() && std::string(backend)=="nebula"); + for (auto& z:s.zones) { + float target = manual ? 0 : (stale || t<restore_until || locked() ? 1 : z.target); + double duration=manual ? double(manual_seconds) : double(wake_seconds); + z.opacity += std::clamp(target-z.opacity,float(-dt/std::max(.05,duration)),float(dt/std::max(.05,duration))); + z.opacity=std::clamp(z.opacity,0.f,1.f); + if (stale) { z.target=1;z.black=0;z.effect=1; } + } + // The locker owns animation while protected. Do not keep waking + // the desktop renderer behind a black or powered-off lock screen. + if (active() && !locked()) o->render->damage_whole(); + } + update_shield(); + if (enabled && stale && !std::string(controller).empty() && t-last_launch>10) { + last_launch=t;wf::get_core().run(std::string(controller)); + } + return true; + } + wf::json_t status() { + wf::json_t j=wf::ipc::json_ok(); + j["enabled"]=bool(enabled);j["backend"]=std::string(backend);j["manual"]=manual; + j["suspended"]=suspended; + j["fade_scope"]=std::string("output"); + j["reset_generation"]=int(reset_generation);j["black_enabled"]=bool(black_enabled); + j["focused_delay"]=double(focused_delay);j["inactive_delay"]=double(inactive_delay); + j["fade_seconds"]=double(fade_seconds);j["wake_seconds"]=double(wake_seconds); + j["min_opacity"]=double(min_opacity);j["manual_seconds"]=double(manual_seconds); + j["background_black_delay"]=setting<double>("background_black_delay"); + j["effect_black_delay"]=setting<double>("effect_black_delay"); + j["black_fade_duration"]=setting<double>("black_fade_duration"); + wf::json_t matrix, lock_screen, hyperspace, nebula; + auto section=wf::get_core().config->get_section("ambient-background"); + for (auto option:section->get_registered_options()) { + auto name=option->get_name(); + if (name.rfind("lock_",0)==0 || name.rfind("hyperspace_",0)==0 || name.rfind("nebula_",0)==0) { + auto& group=name.rfind("lock_",0)==0 ? lock_screen : name.rfind("nebula_",0)==0 ? nebula : hyperspace; + auto key=name.rfind("lock_",0)==0 ? name.substr(5) : name; + if (auto value=std::dynamic_pointer_cast<wf::config::option_t<double>>(option)) group[key]=value->get_value(); + else if (auto value=std::dynamic_pointer_cast<wf::config::option_t<int>>(option)) group[key]=value->get_value(); + else if (auto value=std::dynamic_pointer_cast<wf::config::option_t<bool>>(option)) group[key]=value->get_value(); + else group[key]=option->get_value_str(); + } + if (name.rfind("matrix_",0)!=0) continue; + name=name.substr(7); + std::transform(name.begin(),name.end(),name.begin(),[](unsigned char c){return std::toupper(c);}); + if (auto value=std::dynamic_pointer_cast<wf::config::option_t<double>>(option)) matrix[name]=value->get_value(); + else if (auto value=std::dynamic_pointer_cast<wf::config::option_t<int>>(option)) matrix[name]=value->get_value(); + else if (auto value=std::dynamic_pointer_cast<wf::config::option_t<bool>>(option)) matrix[name]=value->get_value(); + else matrix[name]=option->get_value_str(); + } + j["matrix"]=matrix; + j["lock_screen"]=lock_screen;j["hyperspace"]=hyperspace;j["nebula"]=nebula; + j["scenes"]=wf::json_t(); + for (auto& [o,s]:outputs) if (s.hyperspace->has_scene()) { + wf::json_t scene=wf::json_t::array(); + for (double v:s.hyperspace->scene()) scene.append(v); + j["scenes"][o->to_string()]=scene; + } + j["nebula_scenes"]=wf::json_t(); + for (auto& [o,s]:outputs) if (s.nebula->has_scene()) { + wf::json_t scene=wf::json_t::array(); + for (double v:s.nebula->scene()) scene.append(v); + j["nebula_scenes"][o->to_string()]=scene; + } + j["outputs"]=wf::json_t::array(); + for (auto& [o,s]:outputs) for (auto& z:s.zones) { + wf::json_t item;item["name"]=z.name;item["desktop"]=o->to_string(); + item["opacity"]=double(z.opacity);item["target"]=double(z.target); + j["outputs"].append(item); + } + return j; + } + void changed() { + if (!active()) set_manual(false); + for (auto& [o,s]:outputs) { + s.hyperspace->set_active(active() && !locked() && std::string(backend)=="hyperspace"); + s.nebula->set_active(active() && !locked() && std::string(backend)=="nebula"); + if (!active()) for (auto& z:s.zones) { z.opacity=1; z.target=1; z.black=0; z.effect=1; } + if (active()) attach(o,s); + else if (s.node) { + wf::scene::remove_child(s.node);s.node.reset(); + wf::scene::remove_child(s.fade_node);s.fade_node.reset(); + wf::gles::run_in_context([&] { s.fade_buffer.free(); }); + } + o->render->damage_whole(); + } + update_shield(); + wf::ambient_changed_signal signal;wf::get_core().emit(&signal); + if (enabled && !daemon_started && !std::string(controller).empty()) { + daemon_started=true;last_launch=now();wf::get_core().run(std::string(controller)); + } + } + wf::signal::connection_t<wf::ambient_query_signal> query = [this] (auto ev) { + ev->enabled=enabled && outputs.count(ev->output); + }; + wf::signal::connection_t<wf::ambient_render_signal> render = [this] (auto ev) { + if (enabled && outputs.count(ev->output)) { paint(ev->output,*ev->target,*ev->damage);ev->handled=true; } + }; + wf::signal::connection_t<wf::ambient_transition_signal> transition = [this] (auto ev) { + if (outputs.count(ev->output)) outputs.at(ev->output).hyperspace->set_transition(ev->moving); + }; + wf::signal::connection_t<wf::output_added_signal> added = [this] (auto ev) { add_output(ev->output); }; + wf::signal::connection_t<wf::output_pre_remove_signal> removed = [this] (auto ev) { + if (outputs.count(ev->output)) { + ev->output->rem_binding(&toggle);ev->output->rem_binding(&restore); + ev->output->rem_binding(&toggle_background); + auto& s=outputs.at(ev->output); + if(s.node)wf::scene::remove_child(s.node); + if(s.fade_node)wf::scene::remove_child(s.fade_node); + outputs.erase(ev->output); + } + }; + public: + void init() override { + #include "effect-settings.inc" + option(enabled,"enabled",false);option(black_enabled,"fade_to_black",true); + option(backend,"backend",std::string("hyperspace")); + option(controller,"controller",std::string("ambient-background --daemon")); + option(focused_delay,"focused_delay",240.0);option(inactive_delay,"inactive_delay",120.0); + option(fade_seconds,"fade_duration",20.0);option(wake_seconds,"restore_duration",.25); + option(min_opacity,"minimum_opacity",0.0);option(manual_seconds,"manual_duration",.7); + option(toggle_binding,"toggle",wf::activatorbinding_t{}); + option(background_binding,"toggle_background",wf::option_type::from_string<wf::activatorbinding_t>("<super> <alt> KEY_B").value()); + option(restore_binding,"restore",wf::option_type::from_string<wf::activatorbinding_t>("KEY_ESC").value()); + for (auto o:wf::get_core().output_layout->get_outputs()) add_output(o); + wf::get_core().output_layout->connect(&added);wf::get_core().output_layout->connect(&removed); + wf::get_core().connect(&query);wf::get_core().connect(&render);wf::get_core().connect(&transition); + wf::get_core().connect(&on_key); + enabled.set_callback([this]{suspended=false;changed();});backend.set_callback([this]{changed();}); + repo->register_method("ambient/status",[this](wf::json_t){return status();}); + repo->register_method("ambient/toggle",[this](wf::json_t){set_manual(!manual);return status();}); + repo->register_method("ambient/toggle-background",[this](wf::json_t){ + if (!toggle_background({})) return wf::ipc::json_error("Ambient background is disabled or the session is locked"); + return status(); + }); + repo->register_method("ambient/restore",[this](wf::json_t){set_manual(false);return status();}); + repo->register_method("ambient/sync",[this](wf::json_t data){ + if (!data.has_member("outputs") || !data["outputs"].is_array())return wf::ipc::json_error("outputs array required"); + for (size_t i=0;i<data["outputs"].size();i++) { + wf::json_t item=data["outputs"][i]; + if (!item.has_member("name") || !item["name"].is_string())return wf::ipc::json_error("name required"); + for (auto key:{"opacity","black","effect"}) { + if (!item.has_member(key) || (!item[key].is_int() && !item[key].is_double()) || + !std::isfinite(item[key].as_double()) || item[key].as_double()<0 || item[key].as_double()>1) + return wf::ipc::json_error("opacity, black and effect must be in [0,1]"); + } + } + for (size_t i=0;i<data["outputs"].size();i++) { + wf::json_t item=data["outputs"][i]; + for(auto& [o,s]:outputs)for(auto& z:s.zones)if(z.name==item["name"].as_string()) { + z.target=item["opacity"].as_double();z.black=item["black"].as_double();z.effect=item["effect"].as_double(); + } + } + heartbeat=now();return wf::ipc::json_ok(); + }); + changed();timer.set_timeout(33,[this]{return tick();}); + } + void fini() override { + timer.disconnect();enabled.set_callback(nullptr);backend.set_callback(nullptr); + set_manual(false); + if(cursor_hidden) { wf::get_core().unhide_cursor();cursor_hidden=false; } + if(shield) {wf::scene::remove_child(shield);shield.reset();} + query.disconnect();render.disconnect();transition.disconnect();on_key.disconnect();added.disconnect();removed.disconnect(); + for(auto& [o,s]:outputs) { + o->rem_binding(&toggle);o->rem_binding(&restore); + o->rem_binding(&toggle_background); + if(s.node)wf::scene::remove_child(s.node); + if(s.fade_node)wf::scene::remove_child(s.fade_node); + o->render->damage_whole(); + } + outputs.clear(); + for(auto m:{"ambient/status","ambient/toggle","ambient/toggle-background","ambient/restore","ambient/sync"})repo->unregister_method(m); + wf::ambient_changed_signal signal;wf::get_core().emit(&signal); + } +}; +} +DECLARE_WAYFIRE_PLUGIN(ambient_plugin_t); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/docs/README.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/docs/README.md new file mode 100644 index 000000000000..3136c36f8168 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/docs/README.md @@ -0,0 +1,81 @@ +# Ambient Background + +One Wayfire plugin and Gentoo package provide continuous Hyperspace and Digital Rain live wallpapers, per-physical-monitor desktop fading, manual ambient-only mode, and native idle/session integration. Effects have separate preferences and default to no desktop wallpaper behind them. + +WCM → Desktop → Ambient Background has Desktop and idle, Hyperspace, Digital Rain, Lock screen, and Integration tabs. Each effect has a background selector (None or Desktop wallpaper) and wallpaper opacity. None keeps the animated effect and removes the static wallpaper. Hyperspace owns its travel, stars, dust, nebula, pulsar, asteroid, galaxy and quality controls here; changing them does not change Cube's independent fallback effect. Digital Rain owns glyphs, depth, motion, trails, density and colour controls here. + +Hyperspace clouds are separate seeded dust volumes with torn edges, dark holes and varied depths. Stars span the full viewing frustum, with a middle-distance population joining nearby trails to the distant background. Their density follows Passing stars. Cloud shapes stay fixed as the camera approaches; banks expand and move outwards in perspective, fading before they recycle beyond the horizon. Cloud colour variation blends the chosen Hyperspace tint with seeded blue, violet, teal, rose and occasional amber banks; zero uses only the chosen tint, while one uses the full varied palette. Colours stay attached to their banks during travel. The glow control lights dust and stars. Pulsars retain their own irregular event schedule. Rare anomalies adds occasional distant binary pairs and broken glowing remnants on a separate schedule, with gradual fades and quiet intervals. Its intensity can be set to zero independently of pulsars. Speed zero freezes clouds, particles and all encounters. + +Distant galaxy density adds a sparse deep population of spiral, inclined disc and elliptical galaxies between the distant starfield and passing stars. Seeded cores, dusty arms, star clusters, inclinations, sizes and warm/cool stellar colours remain attached to each galaxy. Their fixed world positions expand slowly outwards in perspective. Distant star drift controls their unboosted motion; zero holds galaxies and distant stars still. Soft near/far fades hide recycling, including the bounded drift-clock wrap. Zero galaxy density removes this layer without resetting the journey or other populations. Small projected quads keep their GPU work local. Cube and the native lock use the same shared layer. + +Asteroid field density adds intermittent fields of solid procedural rocks. Each body has a seeded irregular mesh, broken planar faces, craters, fractured strata, chipped mineral patches and fine surface relief under matte lighting. Texture and lighting follow each body's slow tumble. Charcoal, stony grey and occasional iron-rich or pale bodies range from small fragments through boulders to rare huge asteroids; size varies independently of mesh and material. Half the encounters are fragments, with progressively fewer rocks and boulders; two percent are huge bodies. Sizes span a 500-fold range in world space. Independent flight clearance preserves visible size differences rather than making every body pass at the same apparent scale. A depth buffer resolves overlapping bodies and hides stars behind their silhouettes. Rocks approach near the central vanishing point, pass faster than the stars and sweep completely out of view beside the camera. Close bodies remain opaque and their final facets clip naturally at the viewport edges; there is no central-area disappearance fade. Only distant entry uses a fade. World-space paths keep joined-monitor approaches centred, and fast depth travel keeps edge crossings brief. Larger bodies gain extra velocity so their huge silhouettes do not linger around the edges. Field scheduling remains sparse, leaving long quiet stretches between encounters. Zero disables rocks without resetting travel or the other encounters. Quality changes mesh detail, and existing speed, brightness, pause, Cube delegation and lock controls apply. Meshes are cached per seed/quality; bounded travel keeps long sessions and lock entry continuous. Renderer internals have hidden visibility so a plugin reload cannot bind to an older resident mesh, material or trajectory implementation. + +The default monitor delays are 240 seconds focused and 120 seconds inactive. Existing per-connector overrides and application/presentation inhibitors remain effective, including physical members of joined outputs. The final desktop composition fades separately inside each physical monitor, after Cube projects all faces and rows. Windows, panels, launchers, docks and other workspace content on an idle monitor reveal the live wallpaper; active monitor pixels stay unchanged. Windows spanning monitors fade only on idle portions. Screensavers and session locks remain above the composition cover. Fade-to-black retains its two independent delays; zero preserves the disabled-stage behavior. Manual mode keeps the animation visible. Native session locking remains separate and above all ambient surfaces, with automatic locking disabled unless configured. + +No manual shortcut is assigned by default. Set Toggle ambient-only mode to an unused binding, or run `ambient-background --toggle`; repeat it or press Escape to restore the desktop and prior focus. Ordinary typing and mouse movement do not dismiss manual mode. A watchdog restores desktop opacity if the controller fails. + +Super+Alt+B stops ambient GPU rendering on every monitor and restores the normal desktop wallpaper. Press it again to resume. Change this shortcut with Desktop and idle → Toggle ambient background in WCM, or run `ambient-background --toggle-background`. Unlike ambient-only mode, this stops the effect, its continuous desktop redraws and Digital Rain child renderers. Idle monitoring and session locking continue. Settings and generated native scenes are preserved. The pause survives other appearance edits; changing Enable ambient background or restarting the session clears it. `ambient-background --status` reports `suspended` independently of the saved `enabled` preference. + +Persistent appearance and fading settings are in the [ambient-background] section of wayfire.ini. Edit them through WCM or the file; `wayfire/set-config-options` creates locked temporary overrides and is unsuitable for persistent settings. + +Idle, locking, monitor overrides, alerts and audio configuration is selected by AMBIENT_BACKGROUND_CONFIG, ~/.config/ambient-background.toml, then /etc/ambient-background.toml. DIGITALRAIN_CONFIG remains an explicit legacy override. The Integration tab opens the user's TOML and contains compositor integration switches. The related configuration, API, locking, inhibition, monitor and audio guides are packaged here alongside this document. `ambient-idle --check-config FILE` validates a TOML file. `ambient-idle --help` lists manager controls. Audio remains opt-in, and the optional MCP adapter is `ambient-background-mcp`. + +The plugin controller starts the idle manager when needed. Existing autostart scripts can run `ambient-idle`; existing digitalrain commands are compatibility aliases owned by this package. There is no dependency on the old gui-apps/digitalrain package. Configured lock commands and application inhibitor scripts continue to work through those aliases. + +Source modules: + +- core/: output composition, damage, fading, input protection and shared effect state. +- effects/hyperspace/: native GPU shaders and procedural particles, with their own Ambient settings. +- effects/digital-rain/: Matrix renderer and retained idle/session, audio, PAM and monitor support. +- controller/: ordinary-user activity controller and child-renderer ownership. +- include/ambient-api.hpp: the small signal interface through which Cube consumes the current live wallpaper. +- metadata/: the WCM controls for each effect. +- tests/: isolated headless GPU and WCM regressions. + +New backgrounds belong in effects/ and register their controls in metadata/. Cube consumes the same Ambient interface without learning about individual effects. It retains its original standalone background controls when Ambient is disabled. + +Tests use separate ordinary-user headless GPU compositors, synthetic wallpaper/panels/windows and private D-Bus/runtime directories. They cover no-background rendering, independent settings, opacity, Cube composition, partial damage, monitor fading, inhibitors, black transitions, manual mode, crashes, plugin unload and real WCM controls. They do not capture or inject input into the live desktop. + +## Lock-screen mode + +WCM → Desktop → Ambient Background → Lock screen configures the existing native +`ambient-lock` client. Run `ambient-idle --lock` or use the configured session-lock +shortcut. Ambient-only mode remains a reversible desktop presentation. + +The locked background can follow Ambient, select Hyperspace or Digital Rain +independently, or remain black. Speed and brightness are multipliers; changing +them does not change the live desktop. The optional colour override supplies a +base colour without modifying saved effect colours. Hyperspace uses the same +renderer, particles, cloud banks and encounters inside the lock client's own EGL +contexts. Its initial scene continues from the desktop's generated scene state. +No desktop buffers, windows or wallpapers are sampled while locked. Every lock +surface is opaque. Lock controls monitor chooses one physical monitor or All +monitors, including monitors sharing a stitched output. If a selected monitor is +disconnected, the controls move to an available monitor without changing the +saved choice. The animated background and session protection cover every output. + +By default the clock, date and unlock controls are invisible until keyboard or +mouse interaction. They fade in over 0.25 seconds and fade away after 15 seconds +without input, clearing any unfinished password. Both durations are adjustable; +a zero hide delay keeps the controls visible after input. Escape hides them. +Authentication in progress stays visible. Disabling Reveal lock controls on +interaction keeps the optional clock/date visible between interactions. + +Lock fade-to-black delay measures inactivity since lock entry or last input. Zero +keeps the effect visible. Lock fade duration controls both blackening and animation +restoration. Input restores opted-in powered-off monitors and shows the password +prompt; it never unlocks. Once fully black, the locker stops GPU animation, and +Ambient suspends desktop animation while the session is protected. Monitor power +policy remains in `ambient-background.toml`; automatic session locking remains +controlled by `[global] LOCK_TIMEOUT`. + +Existing PAM authentication, compositor lock confirmation, duplicate rejection, +crash recovery and output hot-plug protection remain in the native locker. The +appearance settings grant no unlock API. If Ambient is unavailable, the locker +keeps the last valid appearance or uses its existing Digital Rain configuration. + +## Nebula + +Nebula is an independent procedural live wallpaper adapted from okcomput3/wayfire-v-kube (MIT; upstream commit and attribution are bundled). It combines fractal light points and coloured volumetric clouds with continuous slow orbital drift. No image is embedded. Nebula has independent background, opacity, speed, brightness, clouds, stars, colours, seed, resolution and detail controls. Speed zero freezes it. Reduced render resolution limits GPU cost while leaving desktop surfaces full resolution. The periodic camera path is continuous at its bounded clock wrap. Cube and the native lock share this renderer; lock entry restores the generated scene and drift phase. + +Hyperspace → Fractal cloud detail optionally blends the same fractal fold into world-space cloud banks. It preserves the current cloud geometry, bank colours, perspective motion and every particle/encounter layer. Zero keeps the previous rendition. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/Makefile b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/Makefile new file mode 100644 index 000000000000..62fc28d2d810 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/Makefile @@ -0,0 +1,158 @@ +CC ?= gcc +CXX ?= c++ +CFLAGS ?= -Wall -Wextra -Wno-format-truncation -O2 -std=c11 +CXXFLAGS ?= -Wall -Wextra -O2 -std=c++17 +PIPEWIRE ?= 1 +AUDIO_BIN := $(if $(filter 1,$(PIPEWIRE)),digitalrain-audio) +LDFLAGS ?= +PREFIX ?= /usr +DESTDIR ?= + +# json-c via pkg-config +JSON_C_CFLAGS := $(shell pkg-config --cflags json-c) +JSON_C_LIBS := $(shell pkg-config --libs json-c) + +# Wayland + EGL + GLESv2 + FreeType +WL_CFLAGS := $(shell pkg-config --cflags wayland-client wayland-egl egl glesv2 freetype2) +WL_LIBS := $(shell pkg-config --libs wayland-client wayland-egl egl glesv2 freetype2) -lrt -lm + +# Wayfire plugin helper for compositor cursor control +WF_CFLAGS := $(shell pkg-config --cflags wayfire wf-utils) +WF_LIBS := $(shell pkg-config --libs wayfire wf-utils) +WF_PLUGINDIR := $(shell pkg-config --variable=plugindir wayfire) +WF_METADATADIR := $(shell pkg-config --variable=metadatadir wayfire) + +# Protocol XML sources +WLR_LAYER_SHELL_XML ?= wlr-layer-shell-unstable-v1.xml +XDG_OUTPUT_XML ?= $(shell pkg-config --variable=pkgdatadir wayland-protocols)/unstable/xdg-output/xdg-output-unstable-v1.xml +XDG_SHELL_XML ?= $(shell pkg-config --variable=pkgdatadir wayland-protocols)/stable/xdg-shell/xdg-shell.xml +EXT_IDLE_NOTIFY_XML ?= $(shell pkg-config --variable=pkgdatadir wayland-protocols)/staging/ext-idle-notify/ext-idle-notify-v1.xml + +# Generated protocol files +PROTO_HEADERS := wlr-layer-shell-unstable-v1-client-protocol.h \ + xdg-output-unstable-v1-client-protocol.h \ + xdg-shell-client-protocol.h \ + ext-idle-notify-v1-client-protocol.h +PROTO_SOURCES := wlr-layer-shell-unstable-v1-protocol.c \ + xdg-output-unstable-v1-protocol.c \ + xdg-shell-protocol.c \ + ext-idle-notify-v1-protocol.c + +.PHONY: all clean install install-conf + +IDLE_PLUGIN := $(if $(wildcard digitalrain-idle.cpp),libdigitalrain-idle.so) +SAMPLER_PLUGIN := $(if $(wildcard digitalrain-sampler.cpp),libdigitalrain-sampler.so) +DESKTOP_PLUGIN := $(if $(wildcard digitalrain-desktop.cpp),libdigitalrain-desktop.so) +POWER_PLUGIN := libdigitalrain-power.so + +all: digitalrain digitalrain-lock digitalrain-auth $(AUDIO_BIN) libdigitalrain-cursor.so $(IDLE_PLUGIN) $(SAMPLER_PLUGIN) $(DESKTOP_PLUGIN) $(POWER_PLUGIN) + +# ── Protocol generation ────────────────────────────────────────────── + +wlr-layer-shell-unstable-v1-client-protocol.h: $(WLR_LAYER_SHELL_XML) + wayland-scanner client-header $< $@ + +wlr-layer-shell-unstable-v1-protocol.c: $(WLR_LAYER_SHELL_XML) + wayland-scanner private-code $< $@ + +xdg-output-unstable-v1-client-protocol.h: $(XDG_OUTPUT_XML) + wayland-scanner client-header $< $@ + +xdg-output-unstable-v1-protocol.c: $(XDG_OUTPUT_XML) + wayland-scanner private-code $< $@ + +xdg-shell-client-protocol.h: $(XDG_SHELL_XML) + wayland-scanner client-header $< $@ + +xdg-shell-protocol.c: $(XDG_SHELL_XML) + wayland-scanner private-code $< $@ + +ext-idle-notify-v1-client-protocol.h: $(EXT_IDLE_NOTIFY_XML) + wayland-scanner client-header $< $@ + +ext-idle-notify-v1-protocol.c: $(EXT_IDLE_NOTIFY_XML) + wayland-scanner private-code $< $@ + +# ── digitalrain (combined manager + renderer) ────────────────────────── + +digitalrain: digitalrain.c digitalrain-api.h digitalrain-control.h digitalrain-config.h digitalrain-config-manager.h digitalrain-config-api.h digitalrain-config-tools.h digitalrain-live.h digitalrain-live-render.h digitalrain-live-manager.h digitalrain-audio-protocol.h tomlc17.c tomlc17.h $(PROTO_SOURCES) $(PROTO_HEADERS) + $(CC) $(CFLAGS) $(JSON_C_CFLAGS) $(WL_CFLAGS) -o $@ digitalrain.c tomlc17.c $(PROTO_SOURCES) $(LDFLAGS) $(JSON_C_LIBS) $(WL_LIBS) -lpthread + +ext-session-lock-v1-client-protocol.h: + wayland-scanner client-header $(shell pkg-config --variable=pkgdatadir wayland-protocols)/staging/ext-session-lock/ext-session-lock-v1.xml $@ + +ext-session-lock-v1-protocol.c: + wayland-scanner private-code $(shell pkg-config --variable=pkgdatadir wayland-protocols)/staging/ext-session-lock/ext-session-lock-v1.xml $@ + +lock-nebula.o: ../nebula/nebula-background.cpp ../nebula/nebula-background.hpp ../nebula/nebula-shaders.tpp ../nebula/nebula-field.tpp ../hyperspace/hyperspace-standalone.hpp + $(CXX) $(CXXFLAGS) $(JSON_C_CFLAGS) $(WL_CFLAGS) -DAMBIENT_LOCK_RENDERER -c $< -o $@ + +lock-hyperspace.o: ../hyperspace/hyperspace-background.cpp ../nebula/nebula-field.tpp ../hyperspace/hyperspace-background.hpp ../hyperspace/hyperspace-standalone.hpp ../hyperspace/hyperspace-shaders.tpp ../hyperspace/hyperspace-anomalies.tpp ../hyperspace/hyperspace-asteroids.tpp ../hyperspace/hyperspace-asteroid-shaders.tpp ../hyperspace/hyperspace-galaxies.tpp ../hyperspace/hyperspace-galaxy-shaders.tpp + $(CXX) $(CXXFLAGS) $(JSON_C_CFLAGS) $(WL_CFLAGS) -DAMBIENT_LOCK_RENDERER -c $< -o $@ + +lock-bridge.o: ../hyperspace/hyperspace-lock.cpp ../nebula/nebula-background.hpp ../hyperspace/hyperspace-lock.h ../hyperspace/hyperspace-background.hpp ../hyperspace/hyperspace-standalone.hpp + $(CXX) $(CXXFLAGS) $(JSON_C_CFLAGS) $(WL_CFLAGS) -DAMBIENT_LOCK_RENDERER -c $< -o $@ + +digitalrain-lock: lock-nebula.o lock-hyperspace.o lock-bridge.o digitalrain-lock.c digitalrain.c digitalrain-api.h digitalrain-control.h digitalrain-config.h digitalrain-config-manager.h digitalrain-config-api.h digitalrain-config-tools.h digitalrain-live.h digitalrain-live-render.h digitalrain-live-manager.h digitalrain-audio-protocol.h tomlc17.c tomlc17.h $(PROTO_SOURCES) $(PROTO_HEADERS) ext-session-lock-v1-client-protocol.h ext-session-lock-v1-protocol.c + $(CC) $(CFLAGS) $(JSON_C_CFLAGS) $(WL_CFLAGS) $(shell pkg-config --cflags xkbcommon) -o $@ digitalrain-lock.c tomlc17.c $(PROTO_SOURCES) ext-session-lock-v1-protocol.c lock-nebula.o lock-hyperspace.o lock-bridge.o $(LDFLAGS) $(JSON_C_LIBS) $(WL_LIBS) $(shell pkg-config --libs xkbcommon) -lpthread -lstdc++ + +digitalrain-audio: digitalrain-audio.c digitalrain-audio-protocol.h + $(CC) $(CFLAGS) $(shell pkg-config --cflags libpipewire-0.3 json-c) -o $@ $< $(LDFLAGS) $(shell pkg-config --libs libpipewire-0.3 json-c) -lm + +digitalrain-auth: digitalrain-auth.c + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -lpam + +libdigitalrain-cursor.so: digitalrain-cursor.cpp + $(CXX) $(CXXFLAGS) -fPIC -shared -DWAYFIRE_PLUGIN -DWLR_USE_UNSTABLE $(WF_CFLAGS) -o $@ $< $(LDFLAGS) $(WF_LIBS) + +libdigitalrain-idle.so: digitalrain-idle.cpp digitalrain-targets.hpp + $(CXX) $(CXXFLAGS) -fPIC -shared -DWAYFIRE_PLUGIN -DWLR_USE_UNSTABLE $(WF_CFLAGS) -o $@ $< $(LDFLAGS) $(WF_LIBS) + +libdigitalrain-sampler.so: digitalrain-sampler.cpp digitalrain-targets.hpp + $(CXX) $(CXXFLAGS) -fPIC -shared -DWAYFIRE_PLUGIN -DWLR_USE_UNSTABLE $(WF_CFLAGS) -o $@ $< $(LDFLAGS) $(WF_LIBS) + +libdigitalrain-desktop.so: digitalrain-desktop.cpp digitalrain-targets.hpp + $(CXX) $(CXXFLAGS) -fPIC -shared -DWAYFIRE_PLUGIN -DWLR_USE_UNSTABLE $(WF_CFLAGS) -o $@ $< $(LDFLAGS) $(WF_LIBS) + +libdigitalrain-power.so: digitalrain-power.cpp + $(CXX) $(CXXFLAGS) -fPIC -shared -DWAYFIRE_PLUGIN -DWLR_USE_UNSTABLE $(WF_CFLAGS) -o $@ $< $(LDFLAGS) $(WF_LIBS) + +# ── Install / Clean ───────────────────────────────────────────────── + +install: all +ifneq ($(AUDIO_BIN),) + install -Dm755 digitalrain-audio $(DESTDIR)$(PREFIX)/bin/digitalrain-audio +endif + install -Dm755 digitalrain $(DESTDIR)$(PREFIX)/bin/digitalrain + install -Dm755 digitalrain-lock $(DESTDIR)$(PREFIX)/bin/digitalrain-lock + install -Dm755 digitalrain-auth $(DESTDIR)$(PREFIX)/bin/digitalrain-auth + install -Dm755 digitalrain-mcp.py $(DESTDIR)$(PREFIX)/bin/digitalrain-mcp + install -Dm644 digitalrain.pam $(DESTDIR)/etc/pam.d/digitalrain + install -Dm755 libdigitalrain-cursor.so $(DESTDIR)$(WF_PLUGINDIR)/libdigitalrain-cursor.so + install -Dm644 digitalrain-cursor.xml $(DESTDIR)$(WF_METADATADIR)/digitalrain-cursor.xml + install -Dm755 libdigitalrain-power.so $(DESTDIR)$(WF_PLUGINDIR)/libdigitalrain-power.so + install -Dm755 libdigitalrain-power.so $(DESTDIR)$(WF_PLUGINDIR)/ambient-power-r1/libdigitalrain-power.so + install -Dm644 digitalrain-power.xml $(DESTDIR)$(WF_METADATADIR)/digitalrain-power.xml + +ifneq ($(IDLE_PLUGIN),) + install -Dm755 libdigitalrain-idle.so $(DESTDIR)$(WF_PLUGINDIR)/libdigitalrain-idle.so + install -Dm644 digitalrain-idle.xml $(DESTDIR)$(WF_METADATADIR)/digitalrain-idle.xml +endif + +ifneq ($(SAMPLER_PLUGIN),) + install -Dm755 libdigitalrain-sampler.so $(DESTDIR)$(WF_PLUGINDIR)/libdigitalrain-sampler.so + install -Dm644 digitalrain-sampler.xml $(DESTDIR)$(WF_METADATADIR)/digitalrain-sampler.xml +endif + +ifneq ($(DESKTOP_PLUGIN),) + install -Dm755 libdigitalrain-desktop.so $(DESTDIR)$(WF_PLUGINDIR)/libdigitalrain-desktop.so + install -Dm644 digitalrain-desktop.xml $(DESTDIR)$(WF_METADATADIR)/digitalrain-desktop.xml +endif + +install-conf: + install -Dm644 digitalrain.toml.default $(DESTDIR)/etc/digitalrain.toml + +clean: + rm -f lock-nebula.o lock-hyperspace.o lock-bridge.o + rm -f digitalrain-lock digitalrain-auth digitalrain-audio ext-session-lock-v1-client-protocol.h ext-session-lock-v1-protocol.c + rm -f digitalrain libdigitalrain-cursor.so libdigitalrain-idle.so libdigitalrain-sampler.so libdigitalrain-desktop.so $(POWER_PLUGIN) $(PROTO_HEADERS) $(PROTO_SOURCES) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/adaptive-rendering.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/adaptive-rendering.md new file mode 100644 index 000000000000..daecd939a6e0 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/adaptive-rendering.md @@ -0,0 +1,52 @@ +# Adaptive colors + +This integration is included in Ambient Background. See the configuration guide for current file paths and the WCM controls for continuous live wallpapers; helper and socket identifiers retain their compatible names. + +For all settings, units, defaults and reload behavior, see +[Configuration](configuration.md). Enable `ipc`, `ipc-rules`, and +`digitalrain-sampler` in Wayfire alongside the normal Digital Rain helpers. + +```toml +[matrix] +COLOR = "adaptive" +ADAPTIVE_SAMPLE_INTERVAL = 60 +ADAPTIVE_HUE_INTERVAL = 300 +``` + +Each physical monitor receives a random hue. `adaptive` selects a light or dark +shade according to the desktop below the screensaver. `adaptive-light` always +uses the light shade; `adaptive-dark` uses the dark shade over the desktop. +All adaptive modes use a light shade on a black background. Head glyphs use the +same adaptive palette, so `HEAD_WHITE_MIN` and `HEAD_WHITE_MAX` do not apply. + +Both intervals are **seconds**. Sampling has a five-second minimum; the default +is 60. Hue selection is timed separately, defaults to 900 seconds (15 minutes), +and is held at its initial value when `ADAPTIVE_HUE_INTERVAL=0`. Each physical +monitor is sampled independently, including members of a stitched desktop. +Changes to intervals can affect running adaptive renderers; changes to renderer +color modes update running rain through the live appearance channel. + +The sampler composes the scene below Wayfire's screensaver layer, including cube +content, downsamples on the GPU and reads a small luminance buffer. It does not +load wallpaper files, capture screenshots to disk or integrate with the bar. +Sampling failures retain the last palette. The sample precedes final output +postprocessing and software cursors, so it does not include output-wide filters, +zoom postprocessing or display calibration. + +Glyph bodies cover the desktop beneath them; trail age and depth affect color +intensity, while antialiasing and phase fades control coverage. The glow remains +translucent. Transitioning to black keeps the hue and blends toward its light +shade. + +## Diagnostics + +The `digitalrain/sample_desktop` IPC method accepts an empty data object and +returns protocol `version: 1` with an `outputs` map keyed by physical connector. +Successful entries contain `luminance` in 0–1 and `cached`; failed or locked +outputs contain `error`. Samples are computed on request; a one-second cache +coalesces requests. Adaptive palette state is published in +`$XDG_RUNTIME_DIR/digitalrain/palette.json`. + +The background sequence and optional power-off are described in +[Configuration](configuration.md#understand-the-timing) and +[Monitor power](monitor-power.md). diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/alerts-and-audio.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/alerts-and-audio.md new file mode 100644 index 000000000000..dbed43dbeae1 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/alerts-and-audio.md @@ -0,0 +1,137 @@ +# Automation alerts and audio response + +This integration is included in Ambient Background. See the configuration guide for current file paths and the WCM controls for continuous live wallpapers; helper and socket identifiers retain their compatible names. + +Digital Rain 1.2 can show temporary status colors and react to playback audio. +These affect existing rain. They do not start the saver, extend its idle/phase +clocks, wake a black screen, or change monitor/bar brightness. Configuration is +TOML; see [configuration.md](configuration.md) for migration and live reload. + +## Alerts from programs and agents + +Send an event when something changes: + +```sh +ambient-idle --request '{"version":1,"method":"alert","source":"backup","id":"nightly","severity":"error","seconds":60,"output":"DP-1"}' +ambient-idle --request '{"version":1,"method":"clear_alert","source":"backup","id":"nightly"}' +ambient-idle --list-alerts +``` + +Omit `output` to target all eligible physical monitors. A `(source, id)` pair is +updated in place and its lifetime renewed. Alerts remain after a client exits, +expire using monotonic time even while the screen is black, and are not persisted +across manager restart. Clearing an absent alert succeeds. A source is a label; +all clients use the existing same-user socket permissions. + +| Request | Fields and limits | +| --- | --- | +| `alert` | `source` and `id`: 1–63 bytes each; `severity`: info/success/warning/error; `seconds`: 1–86400; optional physical `output`; optional `message`: at most 127 bytes. | +| `clear_alert` | Required `source`, `id`. | +| `list_alerts` | Optional integer `offset` 0–64 and `limit` 1–16; defaults 0/16. Reply has `alerts`, `total`, and `next_offset` (-1 means complete). | + +There are at most 64 active alerts. An unknown named monitor is rejected at +submission. Disconnecting it retains the alert until expiry; only that connector +can receive it when it returns. A higher priority wins; the newest update breaks +ties. Clearing or expiring the winner reveals another eligible alert or the latest +base palette. Config edits underneath an alert are preserved. A preview suppresses +alerts and audio on its monitor while it runs. + +| TOML setting | Default | Meaning | +| --- | --- | --- | +| `[alerts] ENABLED` | `true` | Accept stored events but display their colors only when enabled. | +| `[alerts] SHOW_WHILE_LOCKED` | `false` | Display colors in native lock surfaces. Authentication is independent. | +| `[alerts.info] COLOR / PRIORITY` | `"#00bfff"` / `10` | Informational status. | +| `[alerts.success] COLOR / PRIORITY` | `"#40ff80"` / `20` | Successful completion. | +| `[alerts.warning] COLOR / PRIORITY` | `"#ffb000"` / `30` | Warning. | +| `[alerts.error] COLOR / PRIORITY` | `"#ff4040"` / `40` | Failure. | +| Each theme's `PULSE` | `0.0` | Optional gentle brightness reduction, 0–0.25, over a two-second cycle. | + +Theme colors accept fixed named or hex colors; priorities accept integers 0–100. +The `message` field is status metadata. Rendering text, frequency-band effects, +beat recognition and displaying alerts over a solid-black screen are future work. + +The optional MCP adapter exposes `alert`, `clear_alert`, `list_alerts`, and +`reload` alongside existing controls. `--watch` publishes state transitions, +alert revisions and selected alerts per output; it does not stream animation +frames or audio samples. Ordinary scripts can use the same CLI or socket without +an agent. The adapter only runs when an MCP client starts it. + +For a build or backup wrapper, publish success/failure after the command exits: + +```sh +if your-build-command; then + ambient-idle --request '{"version":1,"method":"alert","source":"build","id":"result","severity":"success","seconds":15}' +else + ambient-idle --request '{"version":1,"method":"alert","source":"build","id":"result","severity":"error","seconds":120}' +fi +``` + +A service-health program sends an error when health fails, renews it while the +failure persists, and clears it when service returns. No executable hooks run +inside Digital Rain. + +## Audio-reactive rain + +Build/install with Gentoo's `pipewire` USE flag and enable it in the selected TOML: + +```toml +[audio] +ENABLED = true +SOURCE = "default-output" +MODE = "energy" +``` + +`ambient-audio` monitors one playback sink through native PipeWire. The +`default-output` selection follows the session manager's default audio sink. +Alternatively, use the sink's PipeWire `node.name`; it reconnects that named sink +after unplug/replug. It never substitutes a microphone, records audio, changes +routing, or changes volume. Only playback routed to the selected sink contributes. + +| `[audio]` setting | Default | Range / behavior | +| --- | --- | --- | +| `ENABLED` | `false` | Start analysis only for eligible visible rain. | +| `SOURCE` | `"default-output"` | Default playback sink or explicit PipeWire node.name, at most 127 bytes. | +| `MODE` | `"energy"` | The implemented mode uses short-window RMS energy and peaks. | +| `SHOW_WHILE_LOCKED` | `false` | Allow effects in native lock surfaces. | +| `OUTPUTS` | `[]` | Up to eight physical connector names; empty means all eligible monitors. File-only setting. | +| `SPEED_AMOUNT` | `0.25` | Maximum proportional speed increase, 0–2; .25 means up to 25%. | +| `BRIGHTNESS_AMOUNT` | `0.10` | Maximum proportional glyph-brightness increase, 0–1. RGB output remains capped. | +| `ATTACK_MS` | `50` | Envelope rise time, 1–2000 ms. | +| `RELEASE_MS` | `250` | Envelope fall time, 1–5000 ms. | +| `GAIN` | `4` | Measurement gain, .01–100, with normalized output capped at 1. | +| `NOISE_GATE` | `0.005` | RMS below this level produces no effect, 0–1. | + +Energy changes speed; peaks change brightness. There is no tempo detection. +Alerts retain their hue while audio can modulate motion/brightness. Silence or +missing/stale samples smoothly returns to the current normal appearance. + +The helper is shared across participating monitors and stops when all eligible +surfaces are awake, black, or powered off. It restarts on later eligible rain; +server/helper failures use bounded retries. Application idle inhibitors keep their +normal behavior. Audio does not count as user input or prevent a later black phase. +A standalone locker without the manager renders ordinary rain without this helper. + +The selected sink's mute property suppresses visual modulation. Virtual-sink +verification covers mute/unmute; the PCM level available from a sink monitor can +differ from physical loudness with device-specific hardware volume. The feature +responds to the monitor stream and does not claim calibrated speaker loudness. + +Status reports `audio.state`: disabled, idle, unavailable, waiting_for_source, +or monitoring. A missing optional helper, absent PipeWire server, or missing sink +leaves normal rain operational. `SOURCE`, numeric controls and booleans can be +updated using the settings API; themes and the output list are edited in TOML. + +## Performance and verification + +Analysis runs in a separate helper. Its capture callback accumulates bounded +samples without allocations, blocking, or I/O. A timer emits at most 20 small +measurement packets per second; the manager forwards current values to eligible +renderers. Renderers smooth them at the existing 30 FPS. No raw audio or per-frame +JSON goes through the public API/MCP. With audio disabled, no helper runs. + +Tests use private headless compositor sessions and private PipeWire instances +with policy-only WirePlumber and virtual sinks. They cover live appearance without +renderer replacement, per-monitor alert priority/restoration, black-screen policy, +locked appearance without unlocking, playback energy, silence, mute, source +changes and unplug/reconnect. The source package’s `tests/README.live-config-alerts-audio.md` documents commands +and benchmark methodology. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/configuration.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/configuration.md new file mode 100644 index 000000000000..7c90b88c37dc --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/configuration.md @@ -0,0 +1,312 @@ +# Ambient Background idle and Digital Rain configuration + +Ambient Background runs one idle manager and continuously renders the selected live wallpaper on each physical monitor. Shared Wayfire workspaces and Cube do not combine monitor timers. + +WCM → Desktop → Ambient Background owns the live wallpaper selection, desktop fades, black delays, and effect appearance. The Hyperspace and Digital Rain tabs each have their own background selector and opacity. None removes the desktop wallpaper behind the effect. These preferences are saved in the [ambient-background] section of wayfire.ini. + +The TOML described below retains idle detection, per-monitor overrides, locking, inhibitors, adaptive sampling, alerts and audio policy. Its Matrix appearance and transition values are defaults for legacy idle/lock renderers; continuous wallpaper mode uses the independent WCM appearance and black-transition controls instead. Existing helper/API names remain compatible. + +## Choose the configuration file + +The idle manager uses TOML. It selects one file: an explicit `AMBIENT_BACKGROUND_CONFIG` (or the legacy `DIGITALRAIN_CONFIG` override), otherwise `$XDG_CONFIG_HOME/ambient-background.toml` (or `~/.config/ambient-background.toml` when XDG_CONFIG_HOME is unset), otherwise `/etc/ambient-background.toml`. It keeps that path until restarted. Files are not merged. +An explicitly selected missing or invalid file causes startup to fail clearly. + +Convert the old format once, preserving the original: + +```sh +ambient-idle --migrate-config ~/.config/digitalrain.conf ~/.config/ambient-background.toml +ambient-idle --check-config ~/.config/ambient-background.toml +``` + +The converter refuses to overwrite its destination and reports obsolete keys. +Use `schema_version = 1` at the top of a new file. Sections and keys are +case-sensitive. Quote strings, including `COLOR = "#00ff00"`; use `true`/`false` +for switches. TOML supports inline `#` comments. Unknown keys, duplicate keys, +invalid types, non-finite numbers and out-of-range values are rejected. +`~/` expands in the selected file path and `LOG_FILE`; shell expansion is absent. +Old `MOUSE_WARPING`, `CMD`, and `LOCK_CMD` options are removed by conversion. + +## Enable the Wayfire helpers + +Add the required names to your existing `[core] plugins` list in `wayfire.ini`: + +| Plugin | Purpose | +| --- | --- | +| `ipc`, `ipc-rules` | Manager communication, output discovery and focus information. | +| `digitalrain-cursor` | Pointer tracking and cursor visibility on sleeping monitors. | +| `digitalrain-idle` | Per-monitor application idle inhibition and compositor lock status/recovery. | +| `session-lock` | Required Wayfire protocol for the native password lock screen. | +| `digitalrain-sampler` | Desktop contrast samples for adaptive colors. | +| `digitalrain-desktop` | Optional wallpaper-only rain with `HIDE_DESKTOP=true`. | +| `digitalrain-power` | Optional monitor power-off with `POWER_OFF=true`. | + +Keep the installed helpers compatible with the installed Wayfire package. +Config edits do not require a compositor restart. Loading new plugins may need +metadata/plugin reload or a new session, depending on what Wayfire has loaded. + +With Wayfire's `autostart` plugin enabled, the manager can be started with: + +```ini +[autostart] +autostart_ambient_background = ambient-idle +``` + +Duplicate starts exit without disturbing the existing manager. + +## Legacy idle/lock renderer timing + +With `TRANSPARENT=true` and positive background/rain timers, each monitor follows: + +```text +Idle delay (TIMEOUT or FOCUSED_TIMEOUT) + → rain over the desktop (TRANSPARENT_TIMEOUT) + → rain over black (BLACK_RAIN_TIMEOUT) + → solid black (POWER_OFF=false) or monitor power-off (POWER_OFF=true) +``` + +Fades add transition time. `TRANSPARENT_TIMEOUT` starts when that monitor's +renderer starts, including its initial rain fade. `BLACK_RAIN_TIMEOUT` starts +only after the background has become fully black. With `TRANSPARENT=false`, there +is no transparent phase. `FADE_FRAMES` controls each fade stage at a target of +30 frames per second; slower rendering can lengthen a fade. + +Zero has different meanings for different options: + +| Setting | Meaning of zero | +| --- | --- | +| `TRANSPARENT_TIMEOUT` | Transparent rain never changes to black. | +| `BLACK_RAIN_TIMEOUT` | Rain on black never stops, so power-off is never reached. | +| `ADAPTIVE_HUE_INTERVAL` | Keep the initially selected hue. | +| Per-monitor `[output_timeouts]` value | Use the global unfocused `TIMEOUT`. | + +Use positive values for `TIMEOUT`, `FOCUSED_TIMEOUT` and `CHECK_INTERVAL`; zero +is not an idle-disable option. Times are approximate because manager checks and +input-idle notifications also contribute delay. + +Pointer crossings and view-focus changes identify the physical monitor you are +using. Focus changes refresh the arriving and departing monitors' idle timers. +Input resuming on the same monitor refreshes that monitor; others keep counting. +An active application inhibitor prevents its affected monitor from sleeping and +restarts its countdown until the inhibitor is released. + +## Apply configuration changes + +The manager watches saves, including atomic file replacement, with a 150 ms +save debounce and a periodic fallback. It parses a complete candidate before +applying anything. Invalid or missing replacements retain the last valid settings; +`--status` reports `config_error`. Removing an option restores its default. +`ambient-idle --reload` explicitly reloads the selected file and resets temporary +API settings even if the file contents are unchanged. + +Colors, speed, head behavior and phase durations reach running renderers. Fonts +and depth layers rebuild resources at a frame boundary while preserving renderer +PIDs and phase clocks; an allocation failure retains previous resources. Initial +density settings take effect at the next grid rebuild or activation. Changes to +`TRANSPARENT`, `HIDE_DESKTOP`, and `POWER_OFF` apply at the next saver/lock +activation. A black surface stays black after an appearance update. + +Idle thresholds use existing activity timestamps. A changed phase duration uses +its existing start time. `LOG_FILE` and the selected config path require a manager +restart. `--schema` describes application timing, and status reports desired versus +applied revisions per active renderer. `appearance_error` means a renderer retained +its prior appearance. Reloading does not restart the compositor or release a lock. + +## Option reference + +Defaults below describe a fresh manager with omitted options, rather than your +personal configuration. The annotated `ambient-background.toml.example` also ships in +the documentation directory; `/etc/ambient-background.toml` may contain local changes. + +### [global] Idle detection and logging + +| Option | Default | Meaning | +| --- | --- | --- | +| `TIMEOUT` | `300` | Seconds before an unfocused physical monitor starts rain. Use a positive integer. | +| `FOCUSED_TIMEOUT` | `30` | Seconds before the focused monitor starts rain while input is idle. Use a positive integer. This takes precedence over [output_timeouts]. | +| `CHECK_INTERVAL` | `5` | Seconds between normal manager checks; use a positive integer. Cursor tracking and wake checks continue during this wait. | +| `INHIBIT_VIDEO` | `true` | true honors visible applications' Wayland idle inhibitors on their monitors; false ignores them. Requires digitalrain-idle. This follows application requests, not video detection. | +| `LOG_FILE` | `~/.local/state/digitalrain/digitalrain.log` | Writable log path; ~/ expands to your home. Reopened and truncated on manager startup. Restart the manager after changing this path. | +| `LOCK_TIMEOUT` | `0` | Whole-seat inactivity seconds before native session locking. Zero disables automatic locking. Separate from per-monitor saver timers and unaffected by saver inhibitors. | + +If `LOG_FILE` is omitted, `$XDG_STATE_HOME/digitalrain/digitalrain.log` is +preferred when `XDG_STATE_HOME` is set, followed by the home-based path +shown above, then `/tmp/digitalrain.log` when no home is available. +The example explicitly sets the home-based path. Parent directories are +created when permitted; a failed file open reports to standard error. + +### [matrix] Background, transitions, and monitor power + +| Option | Default | Meaning | +| --- | --- | --- | +| `TRANSPARENT` | `false` | true starts rain over visible desktop content; false starts with a black background. | +| `TRANSPARENT_TIMEOUT` | `600` | Seconds from renderer startup until transparent rain starts fading to a black background. 0 keeps it transparent indefinitely. Used only with TRANSPARENT=true. | +| `BLACK_RAIN_TIMEOUT` | `0` | Additional seconds after the background is fully black before the rain fades away. 0 keeps rain running on black indefinitely; it does not immediately stop rain. | +| `POWER_OFF` | `false` | false retains a solid-black surface with the monitor powered for instant wake. true powers off after rain ends. Requires a positive BLACK_RAIN_TIMEOUT and digitalrain-power; unavailable power control falls back to black. | +| `HIDE_DESKTOP` | `false` | false leaves apps and bars visible under transparent rain; the black background covers them naturally. true optionally fades them away over the wallpaper. Requires TRANSPARENT=true and digitalrain-desktop. | +| `FADE_FRAMES` | `30` | Frames per fade stage, valid range 1–300. Target frame rate is 30 fps: 20 frames ≈ 0.67 seconds; 30 ≈ 1 second. A wake can contain both a rain fade and a background fade. | + +### [matrix] Color and adaptive sampling + +| Option | Default | Meaning | +| --- | --- | --- | +| `COLOR` | `green` | Named color, six-digit hex, random mode, or adaptive mode; see the color table in configuration.md. | +| `ADAPTIVE_SAMPLE_INTERVAL` | `60` | Seconds between desktop contrast samples in adaptive modes; minimum 5. Requires digitalrain-sampler. | +| `ADAPTIVE_HUE_INTERVAL` | `900` | Seconds between new random hues in adaptive modes. 0 holds the initial hue; negative values are rejected. Independent of contrast sampling. | + +### [matrix] Glyphs and depth layers + +| Option | Default | Meaning | +| --- | --- | --- | +| `FONT_SIZE` | `12` | Base glyph size in renderer pixels, valid range 6–72. Depth layers scale the glyph grid. | +| `DEPTH_LAYERS` | `4` | Number of rain layers, valid range 1–8. Far layers are smaller, dimmer and slower; near layers are larger, brighter and faster. | + +### [matrix] Motion and trail lifetime + +| Option | Default | Meaning | +| --- | --- | --- | +| `MIN_SPEED` | `0.4` | Minimum fall speed in cells per animation frame before depth scaling. Valid range 0.001–100. | +| `MAX_SPEED` | `2.0` | Maximum fall speed in cells per animation frame before depth scaling. Must be at least MIN_SPEED. | +| `AVG_SPEED` | `0.8` | Bias for the random fall-speed distribution. Keep it between MIN_SPEED and MAX_SPEED; this is not an exact mean. | +| `MIN_TRAIL` | `4` | Minimum glyph trail lifetime in animation frames; minimum 1. Visible trail length also depends on fall speed. | +| `MAX_TRAIL` | `28` | Maximum glyph trail lifetime in animation frames. Must be at least MIN_TRAIL. | + +### [matrix] Initial rain density + +| Option | Default | Meaning | +| --- | --- | --- | +| `MIN_DENSITY` | `0.25` | With DEPTH_LAYERS=1, the midpoint of MIN_DENSITY and MAX_DENSITY seeds active columns. Valid range 0–1. Ignored for multiple layers. | +| `MAX_DENSITY` | `0.45` | Upper density value for the single-layer midpoint. Valid between MIN_DENSITY and 1. | +| `LAYER_FAR_DENSITY` | `0.35` | With DEPTH_LAYERS>1, initial active-column probability in the far layer. Intermediate layers interpolate toward LAYER_NEAR_DENSITY. Normal range 0–1; values >=1 seed every eligible column. | +| `LAYER_NEAR_DENSITY` | `0.15` | With DEPTH_LAYERS>1, initial active-column probability in the near layer. Normal range 0–1. Ignored in single-layer mode. | + +Density currently affects initial column activation, not a continuously +enforced fraction of active columns as trails respawn. The multi-layer +values accept 0–2: values at or above 1 seed every eligible column; +zero seeds none. This does not make the animation permanently +full or empty. Use the normal 0–1 range when tuning. + +### [matrix] Leading glyph behavior + +| Option | Default | Meaning | +| --- | --- | --- | +| `HEAD_HOLD_FRAMES` | `5` | Minimum frames before a head glyph may change while it stays in one row. Valid range 0–120; entering a new row chooses a new glyph regardless. | +| `HEAD_MUTATION_CHANCE` | `0.15` | Probability per eligible frame of changing the head glyph, valid range 0–1. 0 disables these stationary changes. | +| `HEAD_WHITE_MIN` | `0.75` | Minimum white blend for heads in non-adaptive colors, valid range 0–1. 0 uses the trail color; 1 uses white. Adaptive modes use their palette instead. | +| `HEAD_WHITE_MAX` | `1.00` | Maximum white blend, valid range 0–1. If the minimum exceeds the maximum, the configuration is rejected. | + +## Color modes + +The same values work for `[matrix] COLOR` and `[output_colors]`: + +| Value | Behavior | +| --- | --- | +| `green`, `red`, `blue`, `white`, `yellow`, `cyan`, `magenta` | Fixed named color. | +| `#RRGGBB` or `RRGGBB` | Fixed six-digit hexadecimal color. | +| `random_named` (also `random` in manager config) | One random named color per monitor activation; different monitors may pick the same color. | +| `random_hex` | One random RGB color per monitor activation. | +| `random_trail_named` | Each new trail chooses a named color. | +| `random_trail_hex` | Each new trail chooses an RGB color. | +| `adaptive` | A per-monitor hue with a light or dark shade selected for desktop contrast. | +| `adaptive-light` | Always use the light shade of the per-monitor hue. | +| `adaptive-dark` | Use the dark shade over the desktop and the light shade over black. | + +Adaptive modes require `digitalrain-sampler`, use a light shade on black, and +ignore head white-blend settings. Sampling and hue rotation have separate +intervals. On a sampling failure, the last available palette is retained. +See [Adaptive colors](adaptive-rendering.md) for details. + +## Per-monitor overrides + +Use physical connector names such as `DP-1` or `HDMI-A-1`, including when Wayfire +combines them into `VIRTUAL-1`. Names are case-sensitive. Only the following +per-monitor sections are supported; there are no per-monitor speed, density, +focused-timeout or rain-duration sections. + +```toml +[output_timeouts] +HDMI-A-1 = 600 +DP-1 = 300 + +[output_colors] +HDMI-A-1 = "adaptive" +DP-1 = "random_trail_hex" + +[output_power_off] +HDMI-A-1 = false +DP-1 = true +``` + +`[output_timeouts]` overrides the **unfocused** `TIMEOUT`; the focused monitor +always uses `FOCUSED_TIMEOUT`. Positive overrides are used; zero values +fall back to `TIMEOUT`. `[output_colors]` overrides `COLOR`. +`[output_power_off]` overrides `POWER_OFF` with `false` or `true`. +Unlisted monitors use the global setting. An override only takes effect for a +currently discovered physical connector; output discovery reloads overrides. + +## Example: transparent rain, then instant-wake black + +These settings give an unfocused monitor two minutes before rain, or four minutes +on the focused monitor after input becomes idle. Rain stays transparent for ten +minutes, then runs on black for another ten minutes before stopping. Fades and +manager polling add small delays. + +```toml +[global] +TIMEOUT = 120 +FOCUSED_TIMEOUT = 240 +CHECK_INTERVAL = 3 +INHIBIT_VIDEO = true + +[matrix] +COLOR = "adaptive" +TRANSPARENT = true +TRANSPARENT_TIMEOUT = 600 +BLACK_RAIN_TIMEOUT = 600 +POWER_OFF = false +HIDE_DESKTOP = false +FADE_FRAMES = 20 +``` + +Apps and bars stay visible beneath transparent rain. The black surface covers +everything below the screensaver layer; session locks remain above it. No bar +integration is required. Solid black stops animation while retaining the display +connection, avoiding a monitor power-on delay. + +For an LCD, enable `digitalrain-power` and opt in with `POWER_OFF=true`, globally +or for one monitor under `[output_power_off]`. This replaces only the final +solid-black state. See [Monitor power](monitor-power.md). + +## Commands and diagnostics + +- `ambient-idle --reload`, `--check-config FILE`: reload or validate TOML. +- `ambient-idle --list-alerts`: inspect temporary visual alerts. +- `digitalrain`: start the idle manager, using the selected config. +- `ambient-idle --activate`: ask the running manager to start rain on all monitors. +- `ambient-idle --black [OUTPUT]`: make one monitor or all monitors black. +- `ambient-idle --wake [OUTPUT]`: wake the saver or show a lock prompt; never unlock. +- `ambient-idle --lock`: request native locking and wait for protocol confirmation. +- `ambient-idle --status`, `--explain`, `--lock-status`: inspect current state. +- `ambient-idle --schema`, `--watch`, `--request JSON`: local API controls. +- `ambient-idle --help`: show manager commands. +- `ambient-idle --render --help`: show renderer arguments. + +The native password screen authenticates through PAM. Ordinary saver activation +is still independent of locking. See [Interfaces and locking](interfaces-and-locking.md) +for required plugins, the shortcut, API examples, and optional MCP support. +`LOCK_CMD` no longer executes an external command. + +A manually launched `--render` process uses CLI arguments and renderer defaults; +it does not read the manager's config. Its timeout defaults are both zero. +Stop a standalone renderer with SIGTERM, or send SIGUSR1 for its normal wake fade. + +Normal session logs record the selected config, discovered monitors and active +renderers. Runtime files live under `$XDG_RUNTIME_DIR/digitalrain/`; the manager +PID file is `pid`. See [Video inhibition](video-inhibition.md), +[Wallpaper-only rain](hide-desktop.md), and [Stitched monitors](stitched-outputs.md) +for feature-specific behavior. + +## Alerts and audio + +The [alerts and audio guide](alerts-and-audio.md) documents every `[alerts]`, +`[alerts.info/success/warning/error]`, and `[audio]` setting, API examples and +performance behavior. The annotated example contains all their defaults. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-api.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-api.h new file mode 100644 index 000000000000..345863ba06fa --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-api.h @@ -0,0 +1,614 @@ +static void live_locker_reset(void); +static void live_locker_state(struct json_object *o); +static void live_status(struct json_object *o, bool event); +/* Manager-thread control API. Included after manager helpers; no renderer or + * password work is performed by socket clients. API version 1, JSON packets. */ +struct api_client { + int fd; + bool watch; +}; +struct api_inhibitor { + unsigned id; + double until; + char output[64], reason[128]; + pid_t owner; +}; +static struct { + int listener, locker, ambient_fd; + pid_t locker_pid; + char lock_state[32]; + double lock_started, last_activity, retry_lock_after; + unsigned lock_failures; + struct api_client clients[DR_CLIENTS]; + struct api_inhibitor inhibitors[32]; + unsigned next_inhibitor, revision; + char *last_event; +} A = {.listener = -1, .locker = -1, .ambient_fd = -1}; +static const char *api_phases[] = {"fading_to_black", "fading_in", "rain", + "fading_rain", "black", "fading_out", + "fading_to_desktop"}; +static bool api_locked(void) { + return !strcmp(A.lock_state, "locked") || !strcmp(A.lock_state, "locking") || + !strcmp(A.lock_state, "locked_unavailable"); +} +static bool api_inhibited(const char *output, double now) { + for (size_t i = 0; i < 32; i++) + if (A.inhibitors[i].until > now && + (!A.inhibitors[i].output[0] || !strcmp(output, A.inhibitors[i].output))) + return true; + return false; +} +static void api_probe_lock(void) { + struct json_object *o = mgr_ipc_request("digitalrain/lock_status", "{}"), *v; + const char *state = "unknown"; + if (o && json_object_object_get_ex(o, "protected", &v)) + state = json_object_get_boolean(v) ? "locked_unavailable" : "unlocked"; + snprintf(A.lock_state, sizeof(A.lock_state), "%s", state); + if (o) + json_object_put(o); +} +static void api_connect_locker(void) { + if (A.locker >= 0) + return; + int fd = dr_socket("lock.sock", false); + if (fd < 0) + return; + struct json_object *o = json_object_new_object(); + json_object_object_add(o, "method", json_object_new_string("watch")); + if (dr_send(fd, o)) { + A.locker = fd; live_locker_reset(); + } + else + close(fd); + json_object_put(o); +} +static struct json_object *api_lock(void) { + api_connect_locker(); + if (A.locker < 0 && !A.locker_pid) { + char path[PATH_MAX]; + if (dr_sibling(path, sizeof(path), "digitalrain-lock")) + return dr_error("locker path unavailable"); + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_addclosefrom_np(&actions, 3); + char *argv[] = {path, NULL}; + int rc = posix_spawn(&A.locker_pid, path, &actions, NULL, argv, environ); + posix_spawn_file_actions_destroy(&actions); + if (rc) { + A.locker_pid = 0; + return dr_error("unable to start native locker"); + } + snprintf(A.lock_state, sizeof(A.lock_state), "locking"); + A.lock_started = mgr_monotime(); + } + struct json_object *o = json_object_new_object(); + json_object_object_add(o, "state", json_object_new_string(A.lock_state)); + return o; +} + +#include "digitalrain-config-api.h" + +static struct json_object *api_snapshot(bool event) { + struct json_object *o = json_object_new_object(), *outputs = json_object_new_array(), + *inhibitors = json_object_new_array(); + json_object_object_add(o, "version", json_object_new_int(1)); + json_object_object_add(o, "ambient_active", json_object_new_boolean(A.ambient_fd >= 0)); + json_object_object_add(o, "lock", json_object_new_string(A.lock_state)); + json_object_object_add(o, "settings_revision", json_object_new_int(C.revision)); + double now = mgr_monotime(); + pthread_mutex_lock(&G.lock); + for (int i = 0; i < G.output_count; i++) { + mgr_output_info_t *p = &G.outputs[i]; + struct json_object *v = json_object_new_object(); + bool focused = !strcmp(G.focused_output, p->name); + json_object_object_add(v, "name", json_object_new_string(p->name)); + json_object_object_add(v, "desktop", json_object_new_string(p->target.desktop)); + json_object_object_add(v, "focused", json_object_new_boolean(focused)); + json_object_object_add(v, "saver", json_object_new_boolean(!strcmp(p->state, "off"))); + json_object_object_add( + v, "phase", + json_object_new_string(p->render_phase >= 0 && p->render_phase <= PHASE_FADE_TO_DESKTOP + ? api_phases[p->render_phase] + : !strcmp(p->state, "on") ? "awake" + : "starting")); + json_object_object_add(v, "desired_revision", json_object_new_int(C.revision)); + json_object_object_add(v, "applied_revision", json_object_new_int(p->applied_revision)); + json_object_object_add(v, "appearance_error", json_object_new_boolean(p->live_error)); + json_object_object_add(v, "font_size", json_object_new_int(p->applied_font)); + json_object_object_add(v, "depth_layers", json_object_new_int(p->applied_depth)); + json_object_object_add(v, "powered_off", json_object_new_boolean(p->powered_off)); + json_object_object_add(v, "application_inhibited", + json_object_new_boolean(p->video_inhibited)); + bool inhibited = api_inhibited(p->name, now); + json_object_object_add(v, "timed_inhibited", json_object_new_boolean(inhibited)); + json_object_object_add(v, "reason", + json_object_new_string(api_locked() ? "session lock" + : inhibited ? "timed inhibitor" + : p->video_inhibited ? "application inhibitor" + : !strcmp(p->state, "off") ? "saver active" + : focused && !G.seat_idle + ? "focused input" + : "idle countdown")); + if (!event) { + double timeout = focused ? G.config.focused_timeout : mgr_get_output_timeout(p); + json_object_object_add(v, "idle_seconds", + json_object_new_double(fmax(0, now - p->last_input))); + json_object_object_add(v, "timeout", json_object_new_double(timeout)); + json_object_object_add( + v, "remaining_seconds", + json_object_new_double(fmax(0, timeout - (now - p->last_input)))); + } + json_object_array_add(outputs, v); + } + pthread_mutex_unlock(&G.lock); + for (size_t i = 0; i < 32; i++) + if (A.inhibitors[i].until > now) { + struct api_inhibitor *p = &A.inhibitors[i]; + struct json_object *v = json_object_new_object(); + json_object_object_add(v, "id", json_object_new_int(p->id)); + json_object_object_add(v, "owner_pid", json_object_new_int(p->owner)); + json_object_object_add(v, "reason", json_object_new_string(p->reason)); + json_object_object_add(v, "output", json_object_new_string(p->output)); + if (!event) + json_object_object_add(v, "remaining_seconds", + json_object_new_double(p->until - now)); + json_object_array_add(inhibitors, v); + } + json_object_object_add(o, "outputs", outputs); + live_status(o, event); + json_object_object_add(o, "inhibitors", inhibitors); + json_object_object_add(o, "config_file", json_object_new_string(G.config_file)); + json_object_object_add(o, "config_error", json_object_new_string(C.error)); + json_object_object_add(o, "restart_required", json_object_new_boolean(strcmp(C.log_destination, G.config.log_file) != 0)); + return o; +} +static bool api_number(struct json_object *o, const char *key, double *out) { + struct json_object *v; + if (!json_object_object_get_ex(o, key, &v) || + (!json_object_is_type(v, json_type_int) && !json_object_is_type(v, json_type_double))) + return false; + *out = json_object_get_double(v); + return isfinite(*out); +} +#include "digitalrain-live-manager.h" +static struct json_object *api_request(struct api_client *client, struct json_object *req) { + struct json_object *v; + if (!json_object_object_get_ex(req, "version", &v) || !json_object_is_type(v, json_type_int) || + json_object_get_int(v) != 1) + return dr_error("unsupported API version (expected 1)"); + const char *method = dr_string(req, "method"), *output = dr_string(req, "output"); + if (!method) + return dr_error("method required"); + if (!strcmp(method, "alert") || !strcmp(method, "clear_alert") || !strcmp(method, "list_alerts")) return live_alert_request(method, req); + if (json_object_object_get_ex(req, "output", &v) && !output) + return dr_error("output must be a physical connector name"); + json_object_object_foreach(req, key, value) { + (void)value; + bool allowed = + !strcmp(key, "version") || !strcmp(key, "method") || + (!strcmp(key, "output") && + (!strcmp(method, "activate") || !strcmp(method, "black") || !strcmp(method, "wake") || + !strcmp(method, "preview") || !strcmp(method, "inhibit"))) || + (!strcmp(key, "settings") && + (!strcmp(method, "settings") || !strcmp(method, "preview"))) || + (!strcmp(key, "enabled") && !strcmp(method, "ambient")) || + (!strcmp(key, "save") && !strcmp(method, "settings")) || + (!strcmp(key, "seconds") && + (!strcmp(method, "preview") || !strcmp(method, "inhibit"))) || + (!strcmp(key, "reason") && !strcmp(method, "inhibit")) || + (!strcmp(key, "id") && !strcmp(method, "uninhibit")); + if (!allowed) + return dr_error("unexpected request field"); + } + if (output && !mgr_find_output(output)) + return dr_error("unknown physical output"); + if (!strcmp(method, "status") || !strcmp(method, "explain")) + return api_snapshot(false); + if (!strcmp(method, "ambient")) { + struct json_object *enabled; + if (!json_object_object_get_ex(req, "enabled", &enabled) || + !json_object_is_type(enabled, json_type_boolean)) + return dr_error("ambient requires a boolean enabled"); + if (A.ambient_fd >= 0 && A.ambient_fd != client->fd) + return dr_error("ambient is already owned by another connection"); + A.ambient_fd = json_object_get_boolean(enabled) ? client->fd : -1; + if (A.ambient_fd >= 0) { + /* Keep all activity clocks intact while removing any legacy overlay. */ + for (int i = 0; i < G.output_count; i++) mgr_unblank_output(G.outputs[i].name); + } + return api_snapshot(false); + } + if (!strcmp(method, "watch")) { + client->watch = true; + return api_snapshot(false); + } + if (!strcmp(method, "reload")) { C.force = true; if (!mgr_load_config()) return dr_error(C.error); return api_schema(); } + if (!strcmp(method, "schema")) + return api_schema(); + if (!strcmp(method, "lock")) + return api_lock(); + if (!strcmp(method, "lock_status")) { + struct json_object *o = json_object_new_object(); + json_object_object_add(o, "state", json_object_new_string(A.lock_state)); + return o; + } + if (!strcmp(method, "inhibit")) { + double seconds; + const char *reason = dr_string(req, "reason"); + if (!api_number(req, "seconds", &seconds) || seconds <= 0 || seconds > 86400 || !reason || + strlen(reason) > 127) + return dr_error("inhibit requires seconds in (0,86400] and a reason <=127 bytes"); + for (size_t i = 0; i < 32; i++) + if (A.inhibitors[i].until <= mgr_monotime()) { + struct api_inhibitor *p = &A.inhibitors[i]; + p->id = ++A.next_inhibitor; + p->until = mgr_monotime() + seconds; + snprintf(p->reason, sizeof(p->reason), "%s", reason); + snprintf(p->output, sizeof(p->output), "%s", output ? output : ""); + struct ucred cred; + socklen_t len = sizeof(cred); + p->owner = getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == 0 + ? cred.pid + : 0; + struct json_object *o = json_object_new_object(); + json_object_object_add(o, "id", json_object_new_int(p->id)); + return o; + } + return dr_error("too many timed inhibitors"); + } + if (!strcmp(method, "uninhibit")) { + double id; + if (!api_number(req, "id", &id)) + return dr_error("inhibitor id required"); + for (size_t i = 0; i < 32; i++) + if (A.inhibitors[i].id == id && A.inhibitors[i].until > 0) { + A.inhibitors[i].until = 0; + return json_object_new_object(); + } + return dr_error("unknown inhibitor"); + } + if (!strcmp(method, "settings") || !strcmp(method, "preview")) { + struct json_object *values = NULL; + json_object_object_get_ex(req, "settings", &values); + dr_config_t candidate = G.config; + const char *error = api_validate(values, &candidate); + if (error) + return dr_error(error); + if (!strcmp(method, "settings")) { + bool save = false; + if (json_object_object_get_ex(req, "save", &v)) { + if (!json_object_is_type(v, json_type_boolean)) + return dr_error("save must be boolean"); + save = json_object_get_boolean(v); + } + if (save && !api_save(values)) + return dr_error( + "cannot atomically save selected config (must be owned by this user)"); + api_copy_settings(&candidate); + ++C.revision; + if (save) { + G.config_mtime = 0; + mgr_load_config(); + } + return api_schema(); + } + if (api_locked()) + return dr_error("preview unavailable while session is locked"); + double seconds = 15; + if (json_object_object_get_ex(req, "seconds", &v) && + (!api_number(req, "seconds", &seconds) || seconds <= 0 || seconds > 600)) + return dr_error("preview seconds must be in (0,600]"); + dr_config_t before = G.config; + api_copy_settings(&candidate); + for (int i = 0; i < G.output_count; i++) + if (!output || !strcmp(output, G.outputs[i].name)) { + mgr_unblank_output(G.outputs[i].name); + mgr_blank_output(G.outputs[i].name); + G.outputs[i].preview_until = mgr_monotime() + seconds; + } + api_copy_settings(&before); + return api_snapshot(false); + } + if (!strcmp(method, "activate") || !strcmp(method, "black") || !strcmp(method, "wake")) { + bool wake = !strcmp(method, "wake"); + if (!wake && api_locked()) + return dr_error("session is locked; use wake to show its prompt"); + if (wake && A.locker >= 0) { + struct json_object *o = json_object_new_object(); + json_object_object_add(o, "method", json_object_new_string("wake")); + dr_send(A.locker, o); + json_object_put(o); + } + for (int i = 0; i < G.output_count; i++) + if (!output || !strcmp(output, G.outputs[i].name)) { + if (wake) { + mgr_unblank_output(G.outputs[i].name); + G.outputs[i].last_input = mgr_monotime(); + G.outputs[i].preview_until = 0; + } else { + G.manual_activation = 1; + G.seat_idle = 1; + mgr_blank_output(G.outputs[i].name); + if (!strcmp(method, "black")) + for (int j = 0; j < G.outputs[i].pid_count; j++) + kill(G.outputs[i].pids[j], SIGRTMIN + 1); + } + } + return api_snapshot(false); + } + return dr_error("unsupported method"); +} +static void api_events(void) { + bool watch = false; + for (int i = 0; i < DR_CLIENTS; i++) + watch |= A.clients[i].fd >= 0 && A.clients[i].watch; + if (!watch) + return; + struct json_object *o = api_snapshot(true); + const char *text = json_object_to_json_string_ext(o, JSON_C_TO_STRING_PLAIN); + if (!A.last_event || strcmp(text, A.last_event)) { + free(A.last_event); + A.last_event = strdup(text); + for (int i = 0; i < DR_CLIENTS; i++) + if (A.clients[i].fd >= 0 && A.clients[i].watch && !dr_send(A.clients[i].fd, o)) { + if (A.ambient_fd == A.clients[i].fd) A.ambient_fd = -1; + close(A.clients[i].fd); + A.clients[i].fd = -1; + } + } + json_object_put(o); +} +static void api_tick(double now) { + mgr_reap_fading(); + mgr_config_tick(now); + if (!strcmp(A.lock_state, "locked_unavailable") && now >= A.retry_lock_after) { + struct json_object *r = api_lock(); + json_object_put(r); + A.retry_lock_after = now + fmin(30, 1u << (A.lock_failures < 5 ? A.lock_failures++ : 5)); + } + if (!G.seat_idle) + A.last_activity = now; + if (G.config.lock_timeout > 0 && !api_locked() && now - A.last_activity >= G.config.lock_timeout) { + struct json_object *r = api_lock(); + json_object_put(r); + A.last_activity = now; + } + if (A.locker_pid && A.locker < 0) { + api_connect_locker(); + if (kill(A.locker_pid, 0) || now - A.lock_started > 8) { + A.locker_pid = 0; + if (A.locker < 0) + api_probe_lock(); + } + } + for (int i = 0; i < G.output_count; i++) + if (G.outputs[i].preview_until > 0 && now >= G.outputs[i].preview_until) { + G.outputs[i].preview_until = 0; + mgr_unblank_output(G.outputs[i].name); + G.outputs[i].last_input = now; + } +} +static void api_poll(int timeout) { + struct pollfd fds[4 + DR_CLIENTS + MAX_OUTPUTS] = {{.fd = A.listener, .events = POLLIN}, + {.fd = A.locker, .events = POLLIN}}; + for (int i = 0; i < DR_CLIENTS; i++) + fds[2 + i] = (struct pollfd){.fd = A.clients[i].fd, .events = POLLIN}; + for (int i = 0; i < MAX_OUTPUTS; i++) + fds[2 + DR_CLIENTS + i] = (struct pollfd){ + .fd = i < G.output_count ? G.outputs[i].report_fd : -1, .events = POLLIN}; + fds[2 + DR_CLIENTS + MAX_OUTPUTS] = (struct pollfd){.fd=C.fd, .events=POLLIN}; + fds[3 + DR_CLIENTS + MAX_OUTPUTS] = (struct pollfd){.fd=U.audio_fd, .events=POLLIN}; + int ready = poll(fds, 4 + DR_CLIENTS + MAX_OUTPUTS, timeout); + if (ready < 0 && errno != EINTR) + LOG_WARNING("control poll: %s", strerror(errno)); + if (fds[0].revents & POLLIN) { + int fd = dr_accept(A.listener), slot = -1; + for (int i = 0; i < DR_CLIENTS; i++) + if (A.clients[i].fd < 0) { + slot = i; + break; + } + if (fd >= 0) { + if (slot < 0) + close(fd); + else + A.clients[slot] = (struct api_client){.fd = fd}; + } + } + if (A.locker >= 0 && fds[1].revents) { + struct json_object *o = dr_receive(A.locker); + const char *state = dr_string(o, "state"); + if (state) { + live_locker_state(o); + snprintf(A.lock_state, sizeof(A.lock_state), "%s", state); + if (!strcmp(state, "locked")) { + G.activate_all = 0; + A.lock_failures = 0; + } + } + if (!o) { + close(A.locker); + A.locker = -1; + A.locker_pid = 0; + api_probe_lock(); + } + if (o) + json_object_put(o); + } + for (int i = 0; i < G.output_count; i++) + if (G.outputs[i].report_fd >= 0 && fds[2 + DR_CLIENTS + i].revents) { + dr_report_packet report; + ssize_t n; + int packets = 0; + while (packets++ < 16 && (n = recv(G.outputs[i].report_fd, &report, sizeof(report), MSG_DONTWAIT | MSG_TRUNC)) == sizeof(report)) { + if (report.magic != DR_LIVE_MAGIC || report.kind != DR_REPORT) continue; + if (report.phase >= 0 && report.phase <= PHASE_FADE_TO_DESKTOP) G.outputs[i].render_phase = report.phase; + G.outputs[i].powered_off = report.power != 0; + G.outputs[i].applied_revision = report.revision; + G.outputs[i].live_error = report.error; + G.outputs[i].applied_font = report.font_size; + G.outputs[i].applied_depth = report.depth_layers; + } + if (n == 0) { + close(G.outputs[i].report_fd); + G.outputs[i].report_fd = -1; + } + } + for (int i = 0; i < DR_CLIENTS; i++) + if (A.clients[i].fd >= 0 && fds[2 + i].revents) { + struct json_object *req = dr_receive(A.clients[i].fd); + if (!req) { + if (A.ambient_fd == A.clients[i].fd) A.ambient_fd = -1; + close(A.clients[i].fd); + A.clients[i].fd = -1; + continue; + } + struct json_object *reply = api_request(&A.clients[i], req); + if (!dr_send(A.clients[i].fd, reply)) { + if (A.ambient_fd == A.clients[i].fd) A.ambient_fd = -1; + close(A.clients[i].fd); + A.clients[i].fd = -1; + } + json_object_put(reply); + json_object_put(req); + } + api_tick(mgr_monotime()); + live_tick(mgr_monotime()); + api_events(); +} +static bool api_init(void) { + for (int i = 0; i < DR_CLIENTS; i++) + A.clients[i].fd = -1; + A.listener = dr_socket("control.sock", true); + if (A.listener < 0) { + LOG_ERROR("Cannot create private control socket: %s", strerror(errno)); + return false; + } + A.last_activity = mgr_monotime(); + api_probe_lock(); + api_connect_locker(); + return true; +} +static void api_close(void) { + live_audio_stop(); + if (U.audio_pid) { kill(U.audio_pid, SIGKILL); waitpid(U.audio_pid, NULL, 0); } + if (C.fd >= 0) close(C.fd); + for (int i = 0; i < DR_CLIENTS; i++) + if (A.clients[i].fd >= 0) { + if (A.ambient_fd == A.clients[i].fd) A.ambient_fd = -1; + close(A.clients[i].fd); + } + if (A.locker >= 0) + close(A.locker); /* Does not stop or unlock the independent locker. */ + close(A.listener); + char path[PATH_MAX]; + if (!dr_path(path, sizeof(path), "control.sock")) + unlink(path); + free(A.last_event); +} +#include "digitalrain-config-tools.h" +static int api_cli(int argc, char **argv) { + if (!strcmp(argv[1], "--check-config") || !strcmp(argv[1], "--migrate-config")) return cf_cli(argc, argv); + if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { + puts("digitalrain Run the idle manager\n" + "digitalrain --status|--explain|--schema|--watch|--lock-status\n" + "digitalrain --activate|--black|--wake [OUTPUT]\n" + "digitalrain --lock Request native session locking\n" + "digitalrain --reload|--list-alerts\n" + "digitalrain --check-config FILE\n" + "digitalrain --migrate-config OLD.conf NEW.toml\n" + "digitalrain --request JSON Send one API v1 request\n" + "digitalrain --render --help Renderer options"); + return 0; + } + struct json_object *req = NULL; + if (!strcmp(argv[1], "--request") && argc == 3) + req = json_tokener_parse(argv[2]); + else if (!strncmp(argv[1], "--", 2)) { + const char *method = argv[1] + 2; + if (!strcmp(method, "list-alerts")) method = "list_alerts"; + if (!strcmp(method, "lock-status")) + method = "lock_status"; + req = json_object_new_object(); + json_object_object_add(req, "version", json_object_new_int(1)); + json_object_object_add(req, "method", json_object_new_string(method)); + if (argc == 3) + json_object_object_add(req, "output", json_object_new_string(argv[2])); + } + if (!req || !json_object_is_type(req, json_type_object)) { + fprintf(stderr, "Invalid request; use --help\n"); + if (req) + json_object_put(req); + return 2; + } + int fd = dr_socket("control.sock", false); + if (fd < 0) { + fprintf(stderr, "Digital Rain manager unavailable\n"); + json_object_put(req); + return 1; + } + const char *method = dr_string(req, "method"); + bool watch = method && !strcmp(method, "watch"); + bool locking = method && !strcmp(method, "lock"); + if (!dr_send(fd, req)) { + close(fd); + json_object_put(req); + return 1; + } + json_object_put(req); + bool first = true; + for (;;) { + struct pollfd p = {.fd = fd, .events = POLLIN}; + int rc = poll(&p, 1, watch && !first ? -1 : 10000); + struct json_object *reply = rc > 0 ? dr_receive(fd) : NULL; + if (!reply) { + fprintf(stderr, "Control request timed out or disconnected\n"); + close(fd); + return 1; + } + struct json_object *error; + bool failed = json_object_object_get_ex(reply, "error", &error); + puts(json_object_to_json_string_ext(reply, JSON_C_TO_STRING_PLAIN)); + fflush(stdout); + json_object_put(reply); + if (failed) { + close(fd); + return 1; + } + if (!watch) + break; + first = false; + } + close(fd); + /* --lock is a confirmation boundary, not merely a process launch. */ + if (locking) { + double deadline = mgr_monotime() + 8; + do { + int locker = dr_socket("lock.sock", false); + if (locker >= 0) { + struct json_object *r = json_object_new_object(); + json_object_object_add(r, "method", json_object_new_string("status")); + dr_send(locker, r); + json_object_put(r); + struct pollfd p = {.fd = locker, .events = POLLIN}; + if (poll(&p, 1, 1000) > 0) { + r = dr_receive(locker); + const char *state = dr_string(r, "state"); + bool locked = state && !strcmp(state, "locked"); + if (r) + json_object_put(r); + close(locker); + if (locked) { + puts("{\"state\":\"locked\"}"); + return 0; + } + } else + close(locker); + } + usleep(50000); + } while (mgr_monotime() < deadline); + fprintf(stderr, "Native session lock was not confirmed\n"); + return 1; + } + return 0; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-audio-protocol.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-audio-protocol.h new file mode 100644 index 000000000000..d74b2b6b151b --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-audio-protocol.h @@ -0,0 +1,10 @@ +#ifndef DIGITALRAIN_AUDIO_PROTOCOL_H +#define DIGITALRAIN_AUDIO_PROTOCOL_H +#include <stdint.h> +#define DR_AUDIO_MAGIC UINT32_C(0x44524132) +typedef struct { + uint32_t magic, available; + double time; + float rms, peak; +} dr_audio_packet; +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-audio.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-audio.c new file mode 100644 index 000000000000..c6cfa2ab41fd --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-audio.c @@ -0,0 +1,297 @@ +/* Optional playback-monitor analyzer. No GL, saved audio, or public API. */ +#define _GNU_SOURCE +#include <pipewire/pipewire.h> +#include <pipewire/extensions/metadata.h> +#include <spa/param/audio/format-utils.h> +#include <spa/param/props.h> +#include <spa/pod/parser.h> +#include <json-c/json.h> +#include <math.h> +#include <signal.h> +#include <sys/prctl.h> +#include <sys/socket.h> +#include <time.h> +#include <unistd.h> +#include <errno.h> +#include "digitalrain-audio-protocol.h" + +static struct { + struct pw_main_loop *loop; + struct pw_context *context; + struct pw_core *core; + struct pw_registry *registry; + struct pw_metadata *metadata; + struct pw_stream *stream; + struct pw_node *node; + struct spa_hook registry_hook, metadata_hook, stream_hook, node_hook, core_hook; + struct spa_source *timer; + struct { + uint32_t id; + char name[128]; + } sinks[128]; + int count, fd; + uint32_t selected, metadata_id; + char requested[128], default_sink[128]; + double sum, last_samples; + uint64_t samples; + float peak; + bool muted, format_ok, available; +} D; +static double monotime(void) { + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return t.tv_sec + t.tv_nsec / 1e9; +} +static void quit(void *data, int signal) { + (void)data; + (void)signal; + pw_main_loop_quit(D.loop); +} +static void core_error(void *data, uint32_t id, int seq, int res, const char *message) { + (void)data; + (void)id; + (void)seq; + (void)message; + if (res == -EPIPE) + pw_main_loop_quit(D.loop); +} +static const struct pw_core_events core_events = {PW_VERSION_CORE_EVENTS, .error = core_error}; +static void process(void *data) { + (void)data; + struct pw_buffer *buffer = pw_stream_dequeue_buffer(D.stream); + if (!buffer) + return; + struct spa_buffer *b = buffer->buffer; + if (D.format_ok && b->n_datas && b->datas[0].data && b->datas[0].chunk) { + struct spa_data *d = &b->datas[0]; + uint32_t offset = d->chunk->offset; + uint32_t size = d->chunk->size; + if (offset <= d->maxsize && size <= d->maxsize - offset && offset % sizeof(float) == 0) { + const float *samples = (const float *)((char *)d->data + offset); + uint32_t n = size / sizeof(float); + if (n > 65536) + n = 65536; + for (uint32_t i = 0; i < n; i++) { + float value = samples[i]; + if (!isfinite(value)) + value = 0; + value = fminf(1, fabsf(value)); + D.sum += value * value; + if (value > D.peak) + D.peak = value; + } + D.samples += n; + D.last_samples = monotime(); + } + } + pw_stream_queue_buffer(D.stream, buffer); +} +static void stream_state(void *data, enum pw_stream_state old, enum pw_stream_state state, + const char *error) { + (void)data; + (void)old; + (void)error; + D.available = state == PW_STREAM_STATE_PAUSED || state == PW_STREAM_STATE_STREAMING; +} +static void stream_param(void *data, uint32_t id, const struct spa_pod *param) { + (void)data; + if (id != SPA_PARAM_Format || !param) + return; + struct spa_audio_info_raw info = {0}; + D.format_ok = + spa_format_audio_raw_parse(param, &info) >= 0 && info.format == SPA_AUDIO_FORMAT_F32; +} +static const struct pw_stream_events stream_events = { + PW_VERSION_STREAM_EVENTS, .state_changed = stream_state, .param_changed = stream_param, + .process = process}; +static void node_param(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) { + (void)data; + (void)seq; + (void)index; + (void)next; + if (id == SPA_PARAM_Props && param) { + const struct spa_pod_prop *p = spa_pod_find_prop(param, NULL, SPA_PROP_mute); + if (p) + spa_pod_get_bool(&p->value, &D.muted); + } +} +static const struct pw_node_events node_events = {PW_VERSION_NODE_EVENTS, .param = node_param}; +static void select_sink(void) { + const char *name = !strcmp(D.requested, "default-output") ? D.default_sink : D.requested; + uint32_t selected = SPA_ID_INVALID; + for (int i = 0; i < D.count; i++) + if (!strcmp(D.sinks[i].name, name)) + selected = D.sinks[i].id; + if (selected == D.selected) + return; + if (D.stream) { + pw_stream_destroy(D.stream); + D.stream = NULL; + } + if (D.node) { + spa_hook_remove(&D.node_hook); + pw_proxy_destroy((struct pw_proxy *)D.node); + D.node = NULL; + } + D.selected = selected; + D.available = false; + D.format_ok = false; + D.muted = false; + D.sum = 0; + D.samples = 0; + D.peak = 0; + if (selected == SPA_ID_INVALID) + return; + + D.node = pw_registry_bind(D.registry, selected, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0); + if (D.node) { + pw_node_add_listener(D.node, &D.node_hook, &node_events, NULL); + uint32_t ids[] = {SPA_PARAM_Props}; + pw_node_subscribe_params(D.node, ids, 1); + } + D.stream = pw_stream_new( + D.core, "Digital Rain audio energy", + pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Capture", + PW_KEY_MEDIA_ROLE, "DSP", PW_KEY_STREAM_CAPTURE_SINK, "true", + PW_KEY_STREAM_MONITOR, "true", PW_KEY_NODE_PASSIVE, "true", + PW_KEY_NODE_DONT_RECONNECT, "true", PW_KEY_TARGET_OBJECT, name, NULL)); + if (!D.stream) + return; + pw_stream_add_listener(D.stream, &D.stream_hook, &stream_events, NULL); + uint8_t buffer[1024]; + struct spa_pod_builder builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct spa_audio_info_raw info = {.format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 2, + .position = {SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR}}; + const struct spa_pod *params[] = { + spa_format_audio_raw_build(&builder, SPA_PARAM_EnumFormat, &info)}; + /* Main-loop callbacks: no RT_PROCESS. The callback only accumulates samples; + * the separate timer sends metrics outside that callback. */ + pw_stream_connect(D.stream, PW_DIRECTION_INPUT, PW_ID_ANY, + PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS, params, 1); +} +static int metadata_property(void *data, uint32_t subject, const char *key, const char *type, + const char *value) { + (void)data; + (void)type; + if (subject != PW_ID_CORE || !key || strcmp(key, "default.audio.sink")) + return 0; + struct json_object *obj = value ? json_tokener_parse(value) : NULL, *name; + D.default_sink[0] = 0; + if (obj && json_object_object_get_ex(obj, "name", &name) && + json_object_is_type(name, json_type_string)) + snprintf(D.default_sink, sizeof(D.default_sink), "%s", json_object_get_string(name)); + if (obj) + json_object_put(obj); + select_sink(); + return 0; +} +static const struct pw_metadata_events metadata_events = {PW_VERSION_METADATA_EVENTS, + .property = metadata_property}; +static void global(void *data, uint32_t id, uint32_t permissions, const char *type, + uint32_t version, const struct spa_dict *props) { + (void)data; + (void)permissions; + (void)version; + if (!props) + return; + if (!strcmp(type, PW_TYPE_INTERFACE_Metadata)) { + const char *name = spa_dict_lookup(props, PW_KEY_METADATA_NAME); + if (!D.metadata && name && !strcmp(name, "default")) { + D.metadata_id = id; + D.metadata = pw_registry_bind(D.registry, id, type, PW_VERSION_METADATA, 0); + if (D.metadata) + pw_metadata_add_listener(D.metadata, &D.metadata_hook, &metadata_events, NULL); + } + } else if (!strcmp(type, PW_TYPE_INTERFACE_Node)) { + const char *kind = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS), + *name = spa_dict_lookup(props, PW_KEY_NODE_NAME); + if (kind && name && !strcmp(kind, "Audio/Sink") && strlen(name) < 128 && D.count < 128) { + D.sinks[D.count].id = id; + snprintf(D.sinks[D.count++].name, 128, "%s", name); + select_sink(); + } + } +} +static void global_remove(void *data, uint32_t id) { + (void)data; + if (D.metadata && id == D.metadata_id) { + spa_hook_remove(&D.metadata_hook); + pw_proxy_destroy((struct pw_proxy *)D.metadata); + D.metadata = NULL; + D.default_sink[0] = 0; + select_sink(); + } + for (int i = 0; i < D.count; i++) + if (D.sinks[i].id == id) { + D.sinks[i] = D.sinks[--D.count]; + select_sink(); + break; + } +} +static const struct pw_registry_events registry_events = { + PW_VERSION_REGISTRY_EVENTS, .global = global, .global_remove = global_remove}; +static void report(void *data, uint64_t expirations) { + (void)data; + (void)expirations; + dr_audio_packet p = {.magic = DR_AUDIO_MAGIC, .available = D.available, .time = monotime()}; + if (D.samples && !D.muted && p.time - D.last_samples < .3) { + p.rms = sqrt(D.sum / D.samples); + p.peak = D.peak; + } + D.sum = 0; + D.samples = 0; + D.peak = 0; + if (send(D.fd, &p, sizeof(p), MSG_DONTWAIT | MSG_NOSIGNAL) < 0 && errno != EAGAIN && + errno != EINTR) + pw_main_loop_quit(D.loop); +} +int main(int argc, char **argv) { + if (argc != 3 || strlen(argv[2]) >= 128) { + fprintf(stderr, "digitalrain-audio PRIVATE_FD SINK_NAME\n"); + return 2; + } + D.fd = atoi(argv[1]); + if (D.fd < 3) + return 2; + pid_t parent = getppid(); + if (prctl(PR_SET_PDEATHSIG, SIGTERM) || getppid() != parent) + return 1; + snprintf(D.requested, sizeof(D.requested), "%s", argv[2]); + D.selected = SPA_ID_INVALID; + pw_init(NULL, NULL); + D.loop = pw_main_loop_new(NULL); + if (!D.loop) + return 1; + struct pw_loop *loop = pw_main_loop_get_loop(D.loop); + pw_loop_add_signal(loop, SIGTERM, quit, NULL); + pw_loop_add_signal(loop, SIGINT, quit, NULL); + D.context = pw_context_new(loop, NULL, 0); + D.core = D.context ? pw_context_connect(D.context, NULL, 0) : NULL; + if (!D.core) { + pw_main_loop_destroy(D.loop); + return 1; + } + pw_core_add_listener(D.core, &D.core_hook, &core_events, NULL); + D.registry = pw_core_get_registry(D.core, PW_VERSION_REGISTRY, 0); + pw_registry_add_listener(D.registry, &D.registry_hook, ®istry_events, NULL); + D.timer = pw_loop_add_timer(loop, report, NULL); + struct timespec first = {0, 50000000}, interval = {0, 50000000}; + pw_loop_update_timer(loop, D.timer, &first, &interval, false); + pw_main_loop_run(D.loop); + if (D.stream) + pw_stream_destroy(D.stream); + if (D.node) + pw_proxy_destroy((struct pw_proxy *)D.node); + if (D.metadata) + pw_proxy_destroy((struct pw_proxy *)D.metadata); + pw_proxy_destroy((struct pw_proxy *)D.registry); + pw_core_disconnect(D.core); + pw_context_destroy(D.context); + pw_main_loop_destroy(D.loop); + pw_deinit(); + close(D.fd); + return 0; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-auth.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-auth.c new file mode 100644 index 000000000000..ba34e1412177 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-auth.c @@ -0,0 +1,101 @@ +/* Unprivileged PAM worker. Passwords arrive only over the locker's private pipe. + * No CLI password, configurable username, or configurable production service. */ +#define _GNU_SOURCE +#include <security/pam_appl.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <pwd.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#ifndef DIGITALRAIN_PAM_SERVICE +#define DIGITALRAIN_PAM_SERVICE "digitalrain" +#endif +static char secret[1024]; +static const char *username; +static int converse(int n, const struct pam_message **msg, struct pam_response **out, void *data) { + (void)data; + if (n <= 0 || n > 32) + return PAM_CONV_ERR; + struct pam_response *r = calloc(n, sizeof(*r)); + if (!r) + return PAM_BUF_ERR; + for (int i = 0; i < n; ++i) { + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + r[i].resp = strdup(secret); + break; + case PAM_PROMPT_ECHO_ON: + r[i].resp = strdup(username); + break; + case PAM_TEXT_INFO: + case PAM_ERROR_MSG: + continue; + default: + goto fail; + } + if (!r[i].resp) + goto fail; + } + *out = r; + return PAM_SUCCESS; +fail: + for (int i = 0; i < n; ++i) + if (r[i].resp) { + explicit_bzero(r[i].resp, strlen(r[i].resp)); + free(r[i].resp); + } + free(r); + return PAM_CONV_ERR; +} +int main(int argc, char **argv) { + (void)argv; + if (argc != 1) + return 2; + struct rlimit core = {0, 0}; + setrlimit(RLIMIT_CORE, &core); + prctl(PR_SET_DUMPABLE, 0); + mlock(secret, sizeof(secret)); + /* A parent disappearing must not leave a password worker behind. */ + pid_t parent = getppid(); + prctl(PR_SET_PDEATHSIG, 9); + if (parent == 1 || getppid() != parent) + return 1; + struct passwd *pw = getpwuid(getuid()); + if (!pw || geteuid() != getuid()) + return 1; + username = pw->pw_name; + size_t used = 0; + while (used < sizeof(secret)) { + ssize_t n = read(STDIN_FILENO, secret + used, sizeof(secret) - used); + if (n < 0 && errno == EINTR) + continue; + if (n < 0) + goto done; + if (!n) + break; + used += n; + } + if (!used || used >= sizeof(secret) || memchr(secret, 0, used)) + goto done; + secret[used] = 0; + struct pam_conv conv = {.conv = converse}; + pam_handle_t *pam = NULL; + int result = pam_start(DIGITALRAIN_PAM_SERVICE, username, &conv, &pam); + if (result == PAM_SUCCESS) + result = pam_authenticate(pam, PAM_DISALLOW_NULL_AUTHTOK); + if (result == PAM_SUCCESS) + result = pam_acct_mgmt(pam, PAM_SILENT); + if (pam) + pam_end(pam, result); + explicit_bzero(secret, sizeof(secret)); + /* The parent requires both this byte and a successful worker exit. */ + if (result == PAM_SUCCESS && write(STDOUT_FILENO, "Y", 1) == 1) + return 0; +done: + explicit_bzero(secret, sizeof(secret)); + return 1; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config-api.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config-api.h new file mode 100644 index 000000000000..b6791dd2c7cc --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config-api.h @@ -0,0 +1,228 @@ +/* File updates use parser source coordinates, then validate the entire result. */ +static void api_copy_settings(const dr_config_t *candidate) { + pthread_mutex_lock(&G.lock); + G.config = *candidate; + pthread_mutex_unlock(&G.lock); +} +static const char *cf_application(const struct api_setting *s) { + if (!strcmp(s->name, "global.LOG_FILE")) + return "restart required"; + if (!strncmp(s->name, "global.", 7)) + return "next manager check"; + if (!strcmp(s->name, "matrix.TRANSPARENT") || !strcmp(s->name, "matrix.HIDE_DESKTOP") || + !strcmp(s->name, "matrix.POWER_OFF")) + return "next activation"; + if (strstr(s->name, "DENSITY")) + return "next grid rebuild or activation"; + if (!strcmp(s->name, "matrix.FONT_SIZE") || !strcmp(s->name, "matrix.DEPTH_LAYERS")) + return "frame boundary; resource rebuild"; + return "live"; +} +static struct json_object *api_schema(void) { + struct json_object *o = json_object_new_object(); + for (size_t i = 0; i < SETTING_COUNT; i++) { + const struct api_setting *s = &api_settings[i]; + struct json_object *v = json_object_new_object(); + json_object_object_add(v, "type", + json_object_new_string(s->type >= 2 ? "string" + : s->type == 1 ? "number" + : "integer")); + if (cf_boolean(s)) + json_object_object_add(v, "toml_type", json_object_new_string("boolean")); + json_object_object_add(v, "value", api_value(s, &G.config)); + json_object_object_add(v, "default", + s->type >= 2 ? json_object_new_string(s->text) + : json_object_new_double(s->initial)); + if (s->type < 2) { + json_object_object_add(v, "minimum", json_object_new_double(s->lo)); + json_object_object_add(v, "maximum", json_object_new_double(s->hi)); + } + json_object_object_add(v, "applies", json_object_new_string(cf_application(s))); + json_object_object_add(v, "file_only", + json_object_new_boolean(!strcmp(s->name, "global.LOG_FILE"))); + json_object_object_add(o, s->name, v); + } + return o; +} +static size_t cf_position(const char *text, int line, int col) { + size_t p = 0; + for (int n = 1; text[p] && n < line; p++) + if (text[p] == '\n') + n++; + return p + (col > 0 ? col - 1 : 0); +} +/* Parsed scalar tokens only: strings can contain delimiters and newlines. */ +static size_t cf_token_end(const char *text, size_t p) { + char quote = text[p]; + if (quote == '\'' || quote == '"') { + bool triple = text[p + 1] == quote && text[p + 2] == quote; + p += triple ? 3 : 1; + while (text[p]) { + if (quote == '"' && text[p] == '\\' && text[p + 1]) { + p += 2; + continue; + } + if (text[p] == quote && (!triple || (text[p + 1] == quote && text[p + 2] == quote))) { + p += triple ? 3 : 1; + if (triple) + while (text[p] == quote) + p++; + return p; + } + p++; + } + return p; + } + while (text[p] && !strchr(" \t\r\n#,}]", text[p])) + p++; + return p; +} +static char *cf_splice(char *text, size_t start, size_t end, const char *value) { + size_t len = strlen(text), size = start + strlen(value) + len - end; + if (start > end || end > len || size > CF_MAX_BYTES) + return NULL; + char *out = malloc(size + 1); + if (out) { + memcpy(out, text, start); + strcpy(out + start, value); + strcpy(out + start + strlen(value), text + end); + } + return out; +} +static bool api_save(struct json_object *values) { + struct stat st; + size_t len; + char *original = cf_read(G.config_file, &len, &st); + if (!original || st.st_uid != getuid()) { + free(original); + return false; + } + if (C.loaded && cf_hash(original, len) != C.hash) { + free(original); + return false; + } + char *text = strdup(original); + bool ok = text != NULL; + json_object_object_foreach(values, key, value) { + if (!ok) + break; + const struct api_setting *s = cf_setting(key); + toml_result_t t = toml_parse(text, strlen(text)); + if (!t.ok || !s) { + toml_free(t); + ok = false; + break; + } + toml_datum_t d = toml_seek(t.toptab, key); + const char *encoded = + cf_boolean(s) ? (json_object_get_boolean(value) ? "true" : "false") + : json_object_to_json_string_ext( + value, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); + size_t start, end; + char addition[2048]; + if (d.type != TOML_UNKNOWN) { + start = cf_position(text, d.lineno, d.colno); + if (d.type == TOML_STRING) { + if (start && text[start - 1] == '\n') { + start--; + if (start && text[start - 1] == '\r') + start--; + } + if (start >= 3 && (text[start - 1] == '\'' || text[start - 1] == '"') && + text[start - 2] == text[start - 1] && text[start - 3] == text[start - 1]) + start -= 3; + else if (start) + start--; + } + end = cf_token_end(text, start); + snprintf(addition, sizeof(addition), "%s", encoded); + } else { + char section[128]; + snprintf(section, sizeof(section), "%s", key); + char *dot = strrchr(section, '.'); + *dot++ = 0; + toml_datum_t parent = toml_seek(t.toptab, section); + if (parent.type == TOML_UNKNOWN) { + start = end = strlen(text); + snprintf(addition, sizeof(addition), "\n[%s]\n%s = %s\n", section, dot, encoded); + } else if (parent.flag & TOML_FLAG_INLINED) { + start = end = cf_position(text, parent.lineno, parent.colno) + 1; + snprintf(addition, sizeof(addition), "%s = %s%s", dot, encoded, + parent.u.tab.size ? ", " : ""); + } else if (parent.flag & TOML_FLAG_STDEXPR) { + start = cf_position(text, parent.lineno, 1); + while (text[start] && text[start] != '\n') + start++; + if (text[start]) + start++; + end = start; + snprintf(addition, sizeof(addition), "%s%s = %s\n", + start && text[start - 1] != '\n' ? "\n" : "", dot, encoded); + } else { + start = end = 0; + snprintf(addition, sizeof(addition), "%s = %s\n", key, encoded); + } + } + char *next = cf_splice(text, start, end, addition); + toml_free(t); + free(text); + text = next; + ok = text != NULL; + } + dr_config_t candidate; + char error[512]; + if (ok) + ok = cf_parse(text, strlen(text), &candidate, error); + char tmp[MAX_PATH_LEN + 32]; + snprintf(tmp, sizeof(tmp), "%s.tmp.XXXXXX", G.config_file); + int fd = ok ? mkstemp(tmp) : -1; + if (fd < 0) + ok = false; + if (ok) { + size_t size = strlen(text), done = 0; + while (done < size) { + ssize_t n = write(fd, text + done, size - done); + if (n < 0 && errno == EINTR) + continue; + if (n <= 0) { + ok = false; + break; + } + done += n; + } + if (fchmod(fd, st.st_mode & 0777) || fsync(fd)) + ok = false; + struct stat current; + size_t current_len; + char *current_text = cf_read(G.config_file, ¤t_len, ¤t); + if (!current_text || current.st_dev != st.st_dev || current.st_ino != st.st_ino || + current_len != len || memcmp(current_text, original, len)) + ok = false; + free(current_text); + } + if (fd >= 0 && close(fd)) + ok = false; + if (ok && rename(tmp, G.config_file)) + ok = false; + if (ok) { + char dir[MAX_PATH_LEN]; + snprintf(dir, sizeof(dir), "%s", G.config_file); + char *slash = strrchr(dir, '/'); + if (slash) { + if (slash == dir) + slash[1] = 0; + else + *slash = 0; + } else + strcpy(dir, "."); + int dfd = open(dir, O_DIRECTORY | O_RDONLY | O_CLOEXEC); + if (dfd >= 0) { + fsync(dfd); + close(dfd); + } + } else if (fd >= 0) + unlink(tmp); + free(original); + free(text); + return ok; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config-manager.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config-manager.h new file mode 100644 index 000000000000..8f4e594f7223 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config-manager.h @@ -0,0 +1,133 @@ +/* Config state belongs to the manager thread. Watch the parent for atomic saves. */ +static struct { + int fd, watch; + unsigned revision; + uint64_t hash, attempted_hash; + bool loaded, dirty, force; + double deadline, checked; + char error[512], log_destination[512]; +} C = {.fd = -1, .watch = -1}; +static void mgr_set_defaults(void) { cf_defaults(&G.config); } +static void mgr_apply_output_config(void) { + for (int i = 0; i < G.output_count; i++) { + mgr_output_info_t *o = &G.outputs[i]; + o->timeout_override = o->power_off_override = -1; + o->color[0] = 0; + o->min_speed = o->max_speed = o->min_density = o->max_density = -1; + o->min_trail = o->max_trail = -1; + for (int j = 0; j < G.config.output_count; j++) + if (!strcmp(o->name, G.config.outputs[j].name)) { + o->timeout_override = G.config.outputs[j].timeout; + o->power_off_override = G.config.outputs[j].power_off; + snprintf(o->color, sizeof(o->color), "%s", G.config.outputs[j].color); + } + } +} +static void mgr_config_watch(void) { + if (C.fd < 0) + C.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (C.fd < 0 || C.watch >= 0) + return; + char dir[MAX_PATH_LEN]; + snprintf(dir, sizeof(dir), "%s", G.config_file); + char *slash = strrchr(dir, '/'); + if (slash) { + if (slash == dir) + slash[1] = 0; + else + *slash = 0; + } else + strcpy(dir, "."); + C.watch = inotify_add_watch(C.fd, dir, + IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE | IN_DELETE | + IN_MOVE_SELF | IN_DELETE_SELF); +} +static bool mgr_load_config(void) { + if (!G.config_file[0]) { + const char *explicit = getenv("AMBIENT_BACKGROUND_CONFIG") ?: getenv("DIGITALRAIN_CONFIG"), *xdg = getenv("XDG_CONFIG_HOME"), + *home = getenv("HOME"); + if (explicit && *explicit) + mgr_expand_tilde(explicit, G.config_file, sizeof(G.config_file)); + else { + char path[MAX_PATH_LEN]; + if (xdg && *xdg) + snprintf(path, sizeof(path), "%s/ambient-background.toml", xdg); + else + snprintf(path, sizeof(path), "%s/.config/ambient-background.toml", home ? home : ""); + snprintf(G.config_file, sizeof(G.config_file), "%s", + access(path, R_OK) == 0 ? path : "/etc/ambient-background.toml"); + } + } + mgr_config_watch(); + bool force = C.force; + C.force = false; + size_t len; + struct stat st; + char *text = cf_read(G.config_file, &len, &st); + if (!text) { + snprintf(C.error, sizeof(C.error), "%s: %s", G.config_file, strerror(errno)); + return false; + } + uint64_t hash = cf_hash(text, len); + if (C.loaded && !force && hash == C.hash) { + free(text); + C.error[0] = 0; + mgr_apply_output_config(); + return true; + } + dr_config_t candidate; + char error[512]; + bool ok = cf_parse(text, len, &candidate, error); + free(text); + if (!ok) { + snprintf(C.error, sizeof(C.error), "%s: %s", G.config_file, error); + if (hash != C.attempted_hash) + LOG_WARNING("Config retained: %s", C.error); + C.attempted_hash = hash; + return false; + } + char expanded[512]; + mgr_expand_tilde(candidate.log_file, expanded, sizeof(expanded)); + snprintf(candidate.log_file, sizeof(candidate.log_file), "%s", expanded); + pthread_mutex_lock(&G.lock); + G.config = candidate; + mgr_apply_output_config(); + pthread_mutex_unlock(&G.lock); + G.last_adaptive_sample = 0; + G.config_mtime = st.st_mtim.tv_sec + st.st_mtim.tv_nsec / 1e9; + C.hash = hash; + C.loaded = true; + C.revision++; + C.error[0] = 0; + LOG_INFO("Config revision %u loaded from %s", C.revision, G.config_file); + return true; +} +static void mgr_config_tick(double now) { + if (C.fd >= 0) { + char buffer[8192] __attribute__((aligned(__alignof__(struct inotify_event)))); + ssize_t n; + while ((n = read(C.fd, buffer, sizeof(buffer))) > 0) { + for (char *p = buffer; p < buffer + n;) { + struct inotify_event *e = (void *)p; + const char *base = strrchr(G.config_file, '/'); + base = base ? base + 1 : G.config_file; + if (e->mask & (IN_IGNORED | IN_MOVE_SELF | IN_DELETE_SELF)) { + if (C.watch >= 0) + inotify_rm_watch(C.fd, C.watch); + C.watch = -1; + } + if (!e->len || !strcmp(e->name, base)) { + C.dirty = true; + C.deadline = now + .15; + } + p += sizeof(*e) + e->len; + } + } + } + if ((C.dirty && now >= C.deadline) || now - C.checked >= 10) { + C.dirty = false; + C.checked = now; + mgr_config_watch(); + mgr_load_config(); + } +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config-tools.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config-tools.h new file mode 100644 index 000000000000..3a885547d5f2 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config-tools.h @@ -0,0 +1,122 @@ +/* Offline validation/conversion. Conversion never overwrites the source. */ +static int cf_cli(int argc, char **argv) { + bool migrate = !strcmp(argv[1], "--migrate-config"); + if ((!migrate && argc != 3) || (migrate && argc != 4)) { + fprintf(stderr, "Use --check-config FILE or --migrate-config OLD.conf NEW.toml\n"); + return 2; + } + struct stat st; + size_t len; + char *text = cf_read(argv[2], &len, &st); + if (!text) { + perror(argv[2]); + return 1; + } + char *converted = NULL; + size_t size = 0; + if (migrate) { + FILE *out = open_memstream(&converted, &size); + if (!out) { + free(text); + return 1; + } + fputs("schema_version = 1\n", out); + char section[128] = "", *line, *save; + bool bad = false; + for (line = strtok_r(text, "\n", &save); line; line = strtok_r(NULL, "\n", &save)) { + if (strlen(line) >= 2048) { + fprintf(stderr, "Legacy config line is too long\n"); + bad = true; + break; + } + char copy[2048]; + snprintf(copy, sizeof(copy), "%s", line); + mgr_trim(copy); + if (!copy[0] || copy[0] == '#' || copy[0] == ';') { + fprintf(out, "%s%s\n", copy[0] == ';' ? "#" : "", copy[0] == ';' ? copy + 1 : line); + continue; + } + if (copy[0] == '[') { + char *end = strchr(copy, ']'); + if (!end) { + bad = true; + break; + } + *end = 0; + snprintf(section, sizeof(section), "%s", copy + 1); + fprintf(out, "[%s]\n", section); + continue; + } + char *eq = strchr(copy, '='); + if (!eq) { + bad = true; + break; + } + *eq++ = 0; + mgr_trim(copy); + mgr_trim(eq); + if (!strcmp(copy, "CMD") || !strcmp(copy, "LOCK_CMD") || + !strcmp(copy, "MOUSE_WARPING")) { + fprintf(stderr, "Removed obsolete %s.%s\n", section, copy); + continue; + } + char key[256]; + snprintf(key, sizeof(key), "%s.%s", section, copy); + const struct api_setting *s = cf_setting(key); + bool string = s ? s->type >= 2 : !strcmp(section, "output_colors"); + bool boolean = s ? cf_boolean(s) : !strcmp(section, "output_power_off"); + if (!s && strcmp(section, "output_colors") && strcmp(section, "output_timeouts") && + strcmp(section, "output_power_off")) { + fprintf(stderr, "Unknown setting %s\n", key); + bad = true; + break; + } + struct json_object *v = json_object_new_string(eq); + struct json_object *k = json_object_new_string(copy); + fprintf(out, "%s = %s\n", + json_object_to_json_string_ext(k, JSON_C_TO_STRING_PLAIN | + JSON_C_TO_STRING_NOSLASHESCAPE), + string ? json_object_to_json_string_ext(v, JSON_C_TO_STRING_PLAIN | + JSON_C_TO_STRING_NOSLASHESCAPE) + : boolean ? (!strcmp(eq, "1") ? "true" + : !strcmp(eq, "0") ? "false" + : "INVALID_BOOLEAN") + : eq); + json_object_put(v); + json_object_put(k); + } + fclose(out); + free(text); + text = converted; + len = size; + if (bad) { + free(text); + return 1; + } + } + dr_config_t c; + char error[512]; + if (!cf_parse(text, len, &c, error)) { + fprintf(stderr, "%s: %s\n", argv[2], error); + free(text); + return 1; + } + if (migrate) { + int fd = open(argv[3], O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600); + if (fd < 0) { + perror(argv[3]); + free(text); + return 1; + } + bool ok = write(fd, text, len) == (ssize_t)len && fsync(fd) == 0; + close(fd); + if (!ok) { + unlink(argv[3]); + free(text); + return 1; + } + } + puts(migrate ? "Converted and validated; original preserved." : "Configuration valid."); + free(text); + return 0; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config.h new file mode 100644 index 000000000000..c5d0c5b73aeb --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-config.h @@ -0,0 +1,406 @@ +/* Shared configuration schema. Included after rain constants and color presets. */ +#ifndef DIGITALRAIN_CONFIG_H +#define DIGITALRAIN_CONFIG_H +#include "tomlc17.h" +#include <sys/inotify.h> +#include <ctype.h> +typedef struct { + int global_timeout; + int focused_timeout; + int check_interval; + int inhibit_video; + int lock_timeout; + int matrix_transparent; + int matrix_hide_desktop; + int matrix_power_off; + int matrix_transparent_timeout; + int matrix_black_rain_timeout; + int matrix_fade_frames; + int matrix_font_size; + int matrix_depth_layers; + int matrix_min_trail; + int matrix_max_trail; + int matrix_head_hold_frames; + int adaptive_sample_interval; + int adaptive_hue_interval; + double matrix_min_speed; + double matrix_max_speed; + double matrix_avg_speed; + double matrix_min_density; + double matrix_max_density; + double matrix_layer_near_density; + double matrix_layer_far_density; + double matrix_head_mutation_chance; + double matrix_head_white_min; + double matrix_head_white_max; + char matrix_color[32]; + char log_file[512]; + int alerts_enabled, alerts_locked, audio_enabled, audio_locked; + double audio_speed, audio_brightness, audio_attack, audio_release, audio_gain, audio_gate; + char audio_source[128], audio_mode[32]; + char audio_outputs[8][64]; + int audio_output_count; + struct { + char color[32]; + int priority; + double pulse; + } themes[4]; + struct { + char name[64], color[32]; + int timeout, power_off; + } outputs[64]; + int output_count; +} dr_config_t; +struct api_setting { + const char *name; + size_t offset; + int type; + double lo, hi, initial; + size_t width; + const char *text; +}; +#define INT_SETTING(section, key, field, min, max, def) \ + {section "." key, offsetof(dr_config_t, field), 0, min, max, def, 0, NULL} +#define NUM_SETTING(section, key, field, min, max, def) \ + {section "." key, offsetof(dr_config_t, field), 1, min, max, def, 0, NULL} +static const struct api_setting api_settings[] = { + INT_SETTING("global", "TIMEOUT", global_timeout, 1, 86400, 300), + INT_SETTING("global", "FOCUSED_TIMEOUT", focused_timeout, 1, 86400, 30), + INT_SETTING("global", "CHECK_INTERVAL", check_interval, 1, 60, 5), + INT_SETTING("global", "INHIBIT_VIDEO", inhibit_video, 0, 1, 1), + INT_SETTING("global", "LOCK_TIMEOUT", lock_timeout, 0, 86400, 0), + INT_SETTING("matrix", "TRANSPARENT", matrix_transparent, 0, 1, 0), + INT_SETTING("matrix", "HIDE_DESKTOP", matrix_hide_desktop, 0, 1, 0), + INT_SETTING("matrix", "POWER_OFF", matrix_power_off, 0, 1, 0), + INT_SETTING("matrix", "TRANSPARENT_TIMEOUT", matrix_transparent_timeout, 0, 86400, 600), + INT_SETTING("matrix", "BLACK_RAIN_TIMEOUT", matrix_black_rain_timeout, 0, 86400, 0), + INT_SETTING("matrix", "FADE_FRAMES", matrix_fade_frames, 1, 300, 30), + INT_SETTING("matrix", "FONT_SIZE", matrix_font_size, 6, 72, 12), + INT_SETTING("matrix", "DEPTH_LAYERS", matrix_depth_layers, 1, 8, 4), + INT_SETTING("matrix", "MIN_TRAIL", matrix_min_trail, 1, 1000, 4), + INT_SETTING("matrix", "MAX_TRAIL", matrix_max_trail, 1, 1000, 28), + INT_SETTING("matrix", "HEAD_HOLD_FRAMES", matrix_head_hold_frames, 0, 120, 5), + INT_SETTING("matrix", "ADAPTIVE_SAMPLE_INTERVAL", adaptive_sample_interval, 5, 86400, 60), + INT_SETTING("matrix", "ADAPTIVE_HUE_INTERVAL", adaptive_hue_interval, 0, 86400, 900), + NUM_SETTING("matrix", "MIN_SPEED", matrix_min_speed, .001, 100, .4), + NUM_SETTING("matrix", "MAX_SPEED", matrix_max_speed, .001, 100, 2), + NUM_SETTING("matrix", "AVG_SPEED", matrix_avg_speed, .001, 100, .8), + NUM_SETTING("matrix", "MIN_DENSITY", matrix_min_density, 0, 1, .25), + NUM_SETTING("matrix", "MAX_DENSITY", matrix_max_density, 0, 1, .45), + NUM_SETTING("matrix", "LAYER_NEAR_DENSITY", matrix_layer_near_density, 0, 2, .15), + NUM_SETTING("matrix", "LAYER_FAR_DENSITY", matrix_layer_far_density, 0, 2, .35), + NUM_SETTING("matrix", "HEAD_MUTATION_CHANCE", matrix_head_mutation_chance, 0, 1, .15), + NUM_SETTING("matrix", "HEAD_WHITE_MIN", matrix_head_white_min, 0, 1, .75), + NUM_SETTING("matrix", "HEAD_WHITE_MAX", matrix_head_white_max, 0, 1, 1), + {"matrix.COLOR", offsetof(dr_config_t, matrix_color), 2, 0, 0, 0, 32, "green"}, + {"global.LOG_FILE", offsetof(dr_config_t, log_file), 3, 0, 0, 0, 512, ""}, + INT_SETTING("alerts", "ENABLED", alerts_enabled, 0, 1, 1), + INT_SETTING("alerts", "SHOW_WHILE_LOCKED", alerts_locked, 0, 1, 0), + INT_SETTING("audio", "ENABLED", audio_enabled, 0, 1, 0), + INT_SETTING("audio", "SHOW_WHILE_LOCKED", audio_locked, 0, 1, 0), + NUM_SETTING("audio", "SPEED_AMOUNT", audio_speed, 0, 2, .25), + NUM_SETTING("audio", "BRIGHTNESS_AMOUNT", audio_brightness, 0, 1, .10), + NUM_SETTING("audio", "ATTACK_MS", audio_attack, 1, 2000, 50), + NUM_SETTING("audio", "RELEASE_MS", audio_release, 1, 5000, 250), + NUM_SETTING("audio", "GAIN", audio_gain, .01, 100, 4), + NUM_SETTING("audio", "NOISE_GATE", audio_gate, 0, 1, .005), + {"audio.SOURCE", offsetof(dr_config_t, audio_source), 3, 0, 0, 0, 128, "default-output"}, + {"audio.MODE", offsetof(dr_config_t, audio_mode), 3, 0, 0, 0, 32, "energy"}, +}; +#define SETTING_COUNT (sizeof(api_settings) / sizeof(api_settings[0])) +static bool api_color_valid(const char *v) { + if (!v) + return false; + for (size_t i = 0; i < RENDER_NUM_COLORS; i++) + if (!strcmp(v, RENDER_COLOR_TABLE[i].name)) + return true; + const char *names[] = { + "adaptive", "adaptive-light", "adaptive-dark", "random", + "random_named", "random_hex", "random_trail_named", "random_trail_hex"}; + for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); i++) + if (!strcmp(v, names[i])) + return true; + if (*v == '#') + v++; + return strlen(v) == 6 && strspn(v, "0123456789abcdefABCDEF") == 6; +} + +static bool cf_boolean(const struct api_setting *s) { + return s->type == 0 && s->lo == 0 && s->hi == 1; +} +static const struct api_setting *cf_setting(const char *name) { + for (size_t i = 0; i < SETTING_COUNT; ++i) + if (!strcmp(name, api_settings[i].name)) + return &api_settings[i]; + return NULL; +} +static void cf_defaults(dr_config_t *c) { + memset(c, 0, sizeof(*c)); + for (size_t i = 0; i < SETTING_COUNT; i++) { + const struct api_setting *s = &api_settings[i]; + char *p = (char *)c + s->offset; + if (s->type >= 2) + snprintf(p, s->width, "%s", s->text); + else if (s->type == 1) + *(double *)p = s->initial; + else + *(int *)p = s->initial; + } + const char *state = getenv("XDG_STATE_HOME"), *home = getenv("HOME"); + if (state && *state) + snprintf(c->log_file, sizeof(c->log_file), "%s/digitalrain/digitalrain.log", state); + else if (home && *home) + snprintf(c->log_file, sizeof(c->log_file), "%s/.local/state/digitalrain/digitalrain.log", + home); + else + snprintf(c->log_file, sizeof(c->log_file), "/tmp/digitalrain.log"); + const char *colors[] = {"#00bfff", "#40ff80", "#ffb000", "#ff4040"}; + for (int i = 0; i < 4; i++) { + snprintf(c->themes[i].color, 32, "%s", colors[i]); + c->themes[i].priority = (i + 1) * 10; + } +} +static struct json_object *api_value(const struct api_setting *s, const dr_config_t *c) { + const char *p = (const char *)c + s->offset; + if (s->type >= 2) + return json_object_new_string(p); + if (s->type == 1) + return json_object_new_double(*(const double *)p); + return json_object_new_int(*(const int *)p); +} +static const char *cf_set(dr_config_t *c, const struct api_setting *s, struct json_object *v, + bool toml) { + char *p = (char *)c + s->offset; + if (s->type >= 2) { + if (!json_object_is_type(v, json_type_string) || + json_object_get_string_len(v) != (int)strlen(json_object_get_string(v)) || + strlen(json_object_get_string(v)) >= s->width) + return "invalid string"; + const char *str = json_object_get_string(v); + if (s->type == 2 && !api_color_valid(str)) + return "invalid color"; + if (!strcmp(s->name, "audio.MODE") && strcmp(str, "energy")) + return "audio.MODE must be energy"; + if (!strcmp(s->name, "audio.SOURCE") && (!*str || strpbrk(str, "\r\n") != NULL)) + return "invalid audio source"; + snprintf(p, s->width, "%s", str); + return NULL; + } + bool boolean = cf_boolean(s); + if (boolean && toml && !json_object_is_type(v, json_type_boolean)) + return "expected TOML boolean"; + if (!(boolean && json_object_is_type(v, json_type_boolean)) && + !json_object_is_type(v, json_type_int) && + !(s->type == 1 && json_object_is_type(v, json_type_double))) + return "incorrect type"; + double n = json_object_is_type(v, json_type_boolean) ? json_object_get_boolean(v) + : json_object_get_double(v); + if (!isfinite(n) || n < s->lo || n > s->hi) + return "outside allowed range"; + if (s->type == 1) + *(double *)p = n; + else + *(int *)p = n; + return NULL; +} +static const char *cf_relationships(const dr_config_t *c) { + if (c->matrix_min_speed > c->matrix_max_speed || c->matrix_min_trail > c->matrix_max_trail || + c->matrix_min_density > c->matrix_max_density || + c->matrix_head_white_min > c->matrix_head_white_max) + return "minimum exceeds maximum"; + return NULL; +} +static const char *api_validate(struct json_object *values, dr_config_t *c) { + if (!json_object_is_type(values, json_type_object)) + return "settings must be an object"; + json_object_object_foreach(values, key, val) { + const struct api_setting *s = cf_setting(key); + if (!s || !strcmp(key, "global.LOG_FILE")) + return "unknown or file-only setting (use schema)"; + const char *e = cf_set(c, s, val, false); + if (e) + return e; + } + return cf_relationships(c); +} +static struct json_object *cf_json(toml_datum_t d) { + switch (d.type) { + case TOML_STRING: + return json_object_new_string_len(d.u.str.ptr, d.u.str.len); + case TOML_BOOLEAN: + return json_object_new_boolean(d.u.boolean); + case TOML_INT64: + return json_object_new_int64(d.u.int64); + case TOML_FP64: + return json_object_new_double(d.u.fp64); + default: + return NULL; + } +} +static bool cf_walk(toml_datum_t table, const char *prefix, dr_config_t *c, char error[512], + int depth) { + if (depth > 4) { + snprintf(error, 512, "config nesting too deep"); + return false; + } + for (int i = 0; i < table.u.tab.size; i++) { + const char *key = table.u.tab.key[i]; + toml_datum_t d = table.u.tab.value[i]; + char full[256]; + if ((int)strlen(key) != table.u.tab.len[i] || + snprintf(full, sizeof(full), "%s%s%s", prefix, *prefix ? "." : "", key) >= + (int)sizeof(full)) { + snprintf(error, 512, "invalid/long key at line %d", d.lineno); + return false; + } + const char *e = NULL; + const struct api_setting *s = cf_setting(full); + if (!strcmp(full, "schema_version")) { + if (d.type != TOML_INT64 || d.u.int64 != 1) + e = "expected schema_version = 1"; + } else if (s) { + struct json_object *v = cf_json(d); + e = cf_set(c, s, v, true); + if (v) + json_object_put(v); + } else if (!strcmp(full, "audio.OUTPUTS")) { + if (d.type != TOML_ARRAY || d.u.arr.size > 8) + e = "expected up to 8 physical connector names"; + else + for (int j = 0; j < d.u.arr.size; j++) { + toml_datum_t a = d.u.arr.elem[j]; + if (a.type != TOML_STRING || a.u.str.len < 1 || a.u.str.len >= 64 || + a.u.str.len != (int)strlen(a.u.s)) { + e = "invalid physical connector name"; + break; + } + snprintf(c->audio_outputs[c->audio_output_count++], 64, "%s", a.u.s); + } + } else if (!strcmp(prefix, "output_timeouts") || !strcmp(prefix, "output_colors") || + !strcmp(prefix, "output_power_off")) { + int j; + for (j = 0; j < c->output_count; j++) + if (!strcmp(c->outputs[j].name, key)) + break; + if (strlen(key) >= 64 || j >= 64) + e = "too many/long output names"; + else { + if (j == c->output_count) { + c->output_count++; + snprintf(c->outputs[j].name, 64, "%s", key); + c->outputs[j].timeout = c->outputs[j].power_off = -1; + } + if (!strcmp(prefix, "output_timeouts")) { + if (d.type != TOML_INT64 || d.u.int64 < 0 || d.u.int64 > 86400) + e = "expected timeout 0..86400"; + else + c->outputs[j].timeout = d.u.int64; + } else if (!strcmp(prefix, "output_power_off")) { + if (d.type != TOML_BOOLEAN) + e = "expected boolean"; + else + c->outputs[j].power_off = d.u.boolean; + } else if (d.type != TOML_STRING || !api_color_valid(d.u.s) || d.u.str.len >= 32 || d.u.str.len != (int)strlen(d.u.s)) + e = "invalid output color"; + else + snprintf(c->outputs[j].color, 32, "%s", d.u.s); + } + } else { + const char *themes[] = {"alerts.info", "alerts.success", "alerts.warning", + "alerts.error"}; + int theme = -1; + for (int j = 0; j < 4; j++) + if (!strcmp(prefix, themes[j])) + theme = j; + if (theme >= 0) { + if (!strcmp(key, "COLOR") && d.type == TOML_STRING && d.u.str.len < 32 && d.u.str.len == (int)strlen(d.u.s) && + api_color_valid(d.u.s) && strncmp(d.u.s, "adaptive", 8) && + strncmp(d.u.s, "random", 6)) + snprintf(c->themes[theme].color, 32, "%s", d.u.s); + else if (!strcmp(key, "PRIORITY") && d.type == TOML_INT64 && d.u.int64 >= 0 && + d.u.int64 <= 100) + c->themes[theme].priority = d.u.int64; + else if (!strcmp(key, "PULSE") && (d.type == TOML_FP64 || d.type == TOML_INT64)) { + double n = d.type == TOML_FP64 ? d.u.fp64 : (double)d.u.int64; + if (!isfinite(n) || n < 0 || n > .25) + e = "PULSE must be 0..0.25"; + else + c->themes[theme].pulse = n; + } else + e = "unknown/invalid alert theme setting"; + } else if (d.type == TOML_TABLE && + (!strcmp(full, "global") || !strcmp(full, "matrix") || + !strcmp(full, "audio") || !strcmp(full, "alerts") || + !strcmp(full, "output_timeouts") || !strcmp(full, "output_power_off") || + !strcmp(full, "output_colors") || !strcmp(full, "alerts.info") || + !strcmp(full, "alerts.success") || !strcmp(full, "alerts.warning") || + !strcmp(full, "alerts.error"))) { + if (!cf_walk(d, full, c, error, depth + 1)) + return false; + } else + e = "unknown setting/table"; + } + if (e) { + snprintf(error, 512, "line %d, %s: %s", d.lineno, full, e); + return false; + } + } + return true; +} +static bool cf_parse(const char *text, size_t len, dr_config_t *c, char error[512]) { + toml_result_t t = toml_parse(text, len); + if (!t.ok) { + snprintf(error, 512, "%s", t.errmsg); + toml_free(t); + return false; + } + cf_defaults(c); + bool ok = cf_walk(t.toptab, "", c, error, 0); + const char *e = cf_relationships(c); + if (ok && e) { + snprintf(error, 512, "%s", e); + ok = false; + } + toml_free(t); + return ok; +} +#define CF_MAX_BYTES (1024 * 1024) +static char *cf_read(const char *path, size_t *len, struct stat *st) { + int fd = open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK); + if (fd < 0) + return NULL; + if (fstat(fd, st) || !S_ISREG(st->st_mode) || st->st_size > CF_MAX_BYTES) { + close(fd); + errno = EINVAL; + return NULL; + } + char *text = malloc(CF_MAX_BYTES + 1); + if (!text) { + close(fd); + return NULL; + } + size_t n = 0; + for (;;) { + ssize_t count = read(fd, text + n, CF_MAX_BYTES + 1 - n); + if (count < 0 && errno == EINTR) + continue; + if (count < 0 || (n += count) > CF_MAX_BYTES) { + free(text); + close(fd); + return NULL; + } + if (!count) + break; + } + close(fd); + text[n] = 0; + *len = n; + return text; +} +static uint64_t cf_hash(const char *text, size_t n) { + uint64_t h = UINT64_C(1469598103934665603); + for (size_t i = 0; i < n; i++) + h = (h ^ (unsigned char)text[i]) * UINT64_C(1099511628211); + return h; +} +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-control.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-control.h new file mode 100644 index 000000000000..290a668a6a59 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-control.h @@ -0,0 +1,123 @@ +/* Local, bounded SOCK_SEQPACKET transport shared by manager and locker. */ +#ifndef DIGITALRAIN_CONTROL_H +#define DIGITALRAIN_CONTROL_H +#include <pwd.h> +#include <spawn.h> +#include <limits.h> +#define DR_PACKET 16384 +#define DR_CLIENTS 16 +extern char **environ; + +static int dr_path(char *path, size_t size, const char *name) { + const char *base = getenv("XDG_RUNTIME_DIR"); + struct stat st; + if (!base || base[0] != '/' || lstat(base, &st) || !S_ISDIR(st.st_mode) || + st.st_uid != getuid() || (st.st_mode & 0077)) { + errno = EACCES; + return -1; + } + char dir[PATH_MAX]; + if (snprintf(dir, sizeof(dir), "%s/digitalrain", base) >= (int)sizeof(dir)) + return -1; + if (mkdir(dir, 0700) && errno != EEXIST) + return -1; + int fd = open(dir, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (fd < 0) + return -1; + int ok = fstat(fd, &st) == 0 && st.st_uid == getuid(); + if (ok && (st.st_mode & 0077)) + ok = fchmod(fd, 0700) == 0; + close(fd); + if (!ok) + return -1; + return snprintf(path, size, "%s/%s", dir, name) < (int)size ? 0 : -1; +} + +static int dr_socket(const char *name, bool server) { + struct sockaddr_un addr = {.sun_family = AF_UNIX}; + if (dr_path(addr.sun_path, sizeof(addr.sun_path), name)) + return -1; + int fd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (fd < 0) + return -1; + if (server) { + /* Caller must own its instance lock before removing a stale socket. */ + if (unlink(addr.sun_path) && errno != ENOENT) + goto fail; + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) || chmod(addr.sun_path, 0600) || + listen(fd, DR_CLIENTS)) + goto fail; + } else if (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) + goto fail; + return fd; +fail: + close(fd); + return -1; +} + +static int dr_accept(int listener) { + int fd = accept4(listener, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); + if (fd < 0) + return -1; + struct ucred cred; + socklen_t len = sizeof(cred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) || cred.uid != getuid()) { + close(fd); + return -1; + } + return fd; +} + +static struct json_object *dr_receive(int fd) { + char buf[DR_PACKET + 1]; + ssize_t n = recv(fd, buf, DR_PACKET, MSG_DONTWAIT | MSG_TRUNC); + if (n <= 0 || n > DR_PACKET) + return NULL; + buf[n] = 0; + struct json_tokener *tok = json_tokener_new(); + json_tokener_set_flags(tok, JSON_TOKENER_STRICT); + struct json_object *obj = json_tokener_parse_ex(tok, buf, n + 1); + if (json_tokener_get_error(tok) != json_tokener_success || + !json_object_is_type(obj, json_type_object)) { + if (obj) + json_object_put(obj); + obj = NULL; + } + json_tokener_free(tok); + return obj; +} + +static bool dr_send(int fd, struct json_object *obj) { + const char *s = json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN); + size_t n = strlen(s); + return n <= DR_PACKET && send(fd, s, n, MSG_DONTWAIT | MSG_NOSIGNAL) == (ssize_t)n; +} + +static const char *dr_string(struct json_object *obj, const char *key) { + struct json_object *v; + return obj && json_object_object_get_ex(obj, key, &v) && + json_object_is_type(v, json_type_string) + ? json_object_get_string(v) + : NULL; +} + +static struct json_object *dr_error(const char *message) { + struct json_object *o = json_object_new_object(); + json_object_object_add(o, "error", json_object_new_string(message)); + return o; +} + +static int dr_sibling(char *path, size_t size, const char *name) { + ssize_t n = readlink("/proc/self/exe", path, size - 1); + if (n <= 0 || (size_t)n >= size - 1) + return -1; + path[n] = 0; + char *slash = strrchr(path, '/'); + if (!slash) + return -1; + return snprintf(slash + 1, size - (slash + 1 - path), "%s", name) < + (int)(size - (slash + 1 - path)) + ? 0 + : -1; +} +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-cursor.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-cursor.cpp new file mode 100644 index 000000000000..834e6634d78a --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-cursor.cpp @@ -0,0 +1,104 @@ +#include <cmath> +#include <string> + +#include <wayfire/core.hpp> +#include <wayfire/output-layout.hpp> +#include <wayfire/output.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> + +namespace wf +{ +class digitalrain_cursor_plugin_t : public wf::plugin_interface_t +{ + public: + void init() override + { + repo->register_method("digitalrain/cursor_status", + [=] (wf::json_t data) { return this->cursor_status(data); }); + repo->register_method("digitalrain/hide_cursor", + [=] (wf::json_t data) { return this->hide_cursor(data); }); + repo->register_method("digitalrain/unhide_cursor", + [=] (wf::json_t data) { return this->unhide_cursor(data); }); + } + + void fini() override + { + repo->unregister_method("digitalrain/cursor_status"); + repo->unregister_method("digitalrain/hide_cursor"); + repo->unregister_method("digitalrain/unhide_cursor"); + set_cursor_hidden(false); + } + + bool is_unloadable() override + { + return false; + } + + private: + wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; + bool cursor_hidden = false; + + wf::output_t *get_output_at(wf::pointf_t position) + { + for (auto output : wf::get_core().output_layout->get_outputs()) + { + if (output->get_layout_geometry() & position) + { + return output; + } + } + + return nullptr; + } + + void set_cursor_hidden(bool hidden) + { + if (hidden == cursor_hidden) + { + return; + } + + if (hidden) + { + wf::get_core().hide_cursor(); + } else + { + wf::get_core().unhide_cursor(); + } + + cursor_hidden = hidden; + } + + wf::json_t cursor_status(wf::json_t) + { + wf::json_t response; + auto pos = wf::get_core().get_cursor_position(); + bool valid = std::isfinite(pos.x) && std::isfinite(pos.y); + + response["valid"] = valid; + response["hidden"] = cursor_hidden; + response["x"] = valid ? pos.x : 0.0; + response["y"] = valid ? pos.y : 0.0; + + auto output = valid ? get_output_at(pos) : nullptr; + response["output"] = (output && output->handle) ? output->handle->name : ""; + return response; + } + + wf::json_t hide_cursor(wf::json_t) + { + set_cursor_hidden(true); + return wf::ipc::json_ok(); + } + + wf::json_t unhide_cursor(wf::json_t) + { + set_cursor_hidden(false); + return wf::ipc::json_ok(); + } +}; +} + +DECLARE_WAYFIRE_PLUGIN(wf::digitalrain_cursor_plugin_t); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-cursor.xml b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-cursor.xml new file mode 100644 index 000000000000..2f9b64289c67 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-cursor.xml @@ -0,0 +1,8 @@ +<?xml version='1.0' encoding='utf-8'?> +<wayfire> + <plugin name="digitalrain-cursor"> + <_short>Monitor activity tracking</_short> + <_long>Track physical-monitor input activity for ambient idle policies.</_long> + <category>Utility</category> + </plugin> +</wayfire> diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-desktop.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-desktop.cpp new file mode 100644 index 000000000000..e664a7f5e19d --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-desktop.cpp @@ -0,0 +1,278 @@ +#include "digitalrain-targets.hpp" +#include <algorithm> +#include <optional> +#include <chrono> +#include <map> +#include <vector> +#include <wayfire/core.hpp> +#include <wayfire/output-layout.hpp> +#include <wayfire/output.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/render-manager.hpp> +#include <wayfire/scene.hpp> +#include <wayfire/util.hpp> +#include <wayfire/view.hpp> +#include <wayfire/view-transform.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> + +namespace +{ +/* Fade only the pixels within a monitor, including windows spanning a seam. */ +class monitor_fade_t : public wf::scene::view_2d_transformer_t +{ + public: + using view_2d_transformer_t::view_2d_transformer_t; + std::optional<wf::geometry_t> region; + class instance_t : public wf::scene::transformer_render_instance_t<monitor_fade_t> + { + public: + using transformer_render_instance_t::transformer_render_instance_t; + void render(const wf::scene::render_instruction_t& data) override + { + auto faded = self->region ? data.damage & *self->region : data.damage; + auto normal = data.damage ^ faded; + if (normal.empty() && (faded.empty() || self->alpha <= 0)) return; + auto texture = get_texture(data.target.scale); + if (!texture) return; + auto box = self->get_bounding_box(); + if (!normal.empty()) data.pass->add_texture(texture, data.target, box, normal); + if (!faded.empty() && self->alpha > 0) + data.pass->add_texture(texture, data.target, box, faded, self->alpha); + } + }; + void gen_render_instances(std::vector<wf::scene::render_instance_uptr>& instances, + wf::scene::damage_callback damage, wf::output_t *output) override + { + instances.push_back(std::make_unique<instance_t>(this, damage, output)); + } +}; + +/* Hide desktop content only while the renderer holds its IPC connection. + * Disconnect (including SIGKILL), plugin unload and output removal all restore + * our own scene enable references without changing any window's opacity/state. */ +class digitalrain_desktop_plugin_t : public wf::plugin_interface_t +{ + using client_t = wf::ipc::client_interface_t; + struct hidden_output_t + { + wf::output_t *output; + std::vector<wf::scene::node_ptr> nodes; + std::vector<std::shared_ptr<monitor_fade_t>> fades; + std::chrono::steady_clock::time_point started; + std::string name; + int fade_ms; + bool disabled = false; + bool revealing = false; + float alpha = 1.0f; + float start_alpha = 0.0f; + std::optional<wf::geometry_t> region = std::nullopt; + }; + wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; + std::map<client_t*, hidden_output_t> hidden; + wf::wl_timer<true> fade_timer; + + static bool belongs_to(wayfire_view view, const hidden_output_t& state) + { + if (!view->is_mapped()) return false; + for (wf::scene::node_t *node = view->get_root_node().get(); node; node = node->parent()) + for (auto& layer : state.nodes) + if (node == layer.get()) return true; + return false; + } + + static void clear_fades(hidden_output_t& state) + { + for (auto& fade : state.fades) + if (auto view = fade->view.lock()) + view->get_transformed_node()->rem_transformer(fade); + state.fades.clear(); + } + + static bool update_fades(hidden_output_t& state) + { + bool changed = false; + // Remove our fade if a window moved to another output/layer. + for (auto it = state.fades.begin(); it != state.fades.end();) + { + auto view = (*it)->view.lock(); + if (!view || !belongs_to(view->self(), state)) + { + if (view) view->get_transformed_node()->rem_transformer(*it); + it = state.fades.erase(it); + changed = true; + } else ++it; + } + for (auto view : wf::get_core().get_all_views()) + { + if (!belongs_to(view, state)) continue; + auto manager = view->get_transformed_node(); + auto fade = manager->get_transformer<monitor_fade_t>(state.name); + if (!fade) + { + fade = std::make_shared<monitor_fade_t>(view); + manager->add_transformer(fade, wf::TRANSFORMER_BLUR + 1, state.name); + state.fades.push_back(fade); + changed = true; + } + fade->alpha = state.alpha; + fade->region = state.region; + } + return changed; + } + + bool animate() + { + bool running = false; + for (auto it = hidden.begin(); it != hidden.end();) + { + auto& [client, state] = *it++; + if (state.disabled) { + if (state.region) { + running = true; + if (update_fades(state)) state.output->render->damage_whole(); + } + continue; + } + auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - state.started).count(); + if (elapsed >= state.fade_ms) + { + if (state.revealing) + { + restore(client); + continue; + } + state.disabled = true; + state.alpha = 0.0f; + if (state.region) { + update_fades(state); + running = true; + } else { + for (auto& node : state.nodes) wf::scene::set_node_enabled(node, false); + clear_fades(state); + } + } else + { + running = true; + float progress = float(elapsed) / state.fade_ms; + state.alpha = state.revealing ? + state.start_alpha + (1.0f - state.start_alpha) * progress : 1.0f - progress; + update_fades(state); + } + state.output->render->damage_whole(); + } + return running; + } + + void restore(client_t *client) + { + auto it = hidden.find(client); + if (it == hidden.end()) return; + clear_fades(it->second); + if (it->second.disabled && !it->second.region) + for (auto& node : it->second.nodes) + wf::scene::set_node_enabled(node, true); + it->second.output->render->damage_whole(); + hidden.erase(it); + // The timer stops itself on its next tick. Do not disconnect it from + // inside animate(), where restoring the final client also comes here. + } + + wf::json_t reveal(client_t *client) + { + auto it = hidden.find(client); + if (it == hidden.end() || it->second.revealing) return wf::ipc::json_ok(); + animate(); // Reverse from the actual opacity, including a partial fade-out. + auto& state = it->second; + state.revealing = true; + state.start_alpha = state.alpha; + state.started = std::chrono::steady_clock::now(); + if (state.disabled) + { + // Install zero-opacity transforms before making the layers visible. + update_fades(state); + if (!state.region) + for (auto& node : state.nodes) wf::scene::set_node_enabled(node, true); + state.disabled = false; + } + if (animate() && !fade_timer.is_connected()) + fade_timer.set_timeout(16, [this] { return animate(); }); + return wf::ipc::json_ok(); + } + + wf::signal::connection_t<wf::ipc::client_disconnected_signal> on_disconnect = + [this] (auto ev) { restore(ev->client); }; + wf::signal::connection_t<wf::output_pre_remove_signal> on_output_removed = + [this] (auto ev) + { + for (auto it = hidden.begin(); it != hidden.end();) + { + auto current = it++; + if (current->second.output == ev->output) restore(current->first); + } + }; + + public: + void init() override + { + repo->connect(&on_disconnect); + wf::get_core().output_layout->connect(&on_output_removed); + repo->register_method("digitalrain/show_desktop", [this] (wf::json_t, client_t *client) + { + return reveal(client); + }); + repo->register_method("digitalrain/hide_desktop", [this] (wf::json_t data, client_t *client) + { + if (!client || !data.has_member("output") || !data["output"].is_string()) + return wf::ipc::json_error("An IPC connection and output name are required"); + auto name = data["output"].as_string(); + auto output = wf::get_core().output_layout->find_output(name); + std::optional<wf::geometry_t> region; + if (!output) { + for (auto candidate : wf::get_core().output_layout->get_outputs()) + for (const auto& target : digitalrain_targets(candidate)) + if (target.name == name) { output = candidate; region = target.geometry; } + } + if (!output) return wf::ipc::json_error("Output not found"); + if (hidden.count(client) && hidden.at(client).output == output && hidden.at(client).region == region) + return wf::ipc::json_ok(); + restore(client); + int fade_ms = 0; + if (data.has_member("fade_ms")) + { + if (!data["fade_ms"].is_int()) return wf::ipc::json_error("fade_ms must be an integer"); + fade_ms = std::clamp(data["fade_ms"].as_int(), 0, 10000); + } + hidden_output_t state{output, {}, {}, std::chrono::steady_clock::now(), + "digitalrain-desktop-" + std::to_string(reinterpret_cast<uintptr_t>(client)), fade_ms}; + state.region = region; + using layer = wf::scene::layer; + // Keep wallpaper, screensaver and session-lock layers visible. + for (auto desktop_layer : {layer::BOTTOM, layer::WORKSPACE, layer::TOP, + layer::UNMANAGED, layer::OVERLAY, layer::DWIDGET}) + { + auto node = output->node_for_layer(desktop_layer); + if (!node) continue; + state.nodes.push_back(node); + } + hidden.emplace(client, std::move(state)); + if (animate() && !fade_timer.is_connected()) + fade_timer.set_timeout(16, [this] { return animate(); }); + return wf::ipc::json_ok(); + }); + } + + void fini() override + { + repo->unregister_method("digitalrain/hide_desktop"); + repo->unregister_method("digitalrain/show_desktop"); + fade_timer.disconnect(); + on_disconnect.disconnect(); + on_output_removed.disconnect(); + while (!hidden.empty()) restore(hidden.begin()->first); + } +}; +} + +DECLARE_WAYFIRE_PLUGIN(digitalrain_desktop_plugin_t); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-desktop.xml b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-desktop.xml new file mode 100644 index 000000000000..5f244dce7d1f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-desktop.xml @@ -0,0 +1,8 @@ +<?xml version='1.0' encoding='utf-8'?> +<wayfire> + <plugin name="digitalrain-desktop"> + <_short>Legacy desktop fading</_short> + <_long>Desktop fading support for compatible legacy saver clients.</_long> + <category>Utility</category> + </plugin> +</wayfire> diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-idle.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-idle.cpp new file mode 100644 index 000000000000..eb099b25b8be --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-idle.cpp @@ -0,0 +1,181 @@ +#include "digitalrain-targets.hpp" +#include <set> +#include <map> +#include <algorithm> +#include <string> +#include <wayfire/core.hpp> +#include <wayfire/output.hpp> +#include <wayfire/output-layout.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/view.hpp> +#include <wayfire/seat.hpp> +#include <wayfire/toplevel-view.hpp> +#include <wayfire/signal-definitions.hpp> +#include <wayfire/nonstd/wlroots-full.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> +#include <wayfire/plugins/ipc/ipc-helpers.hpp> +extern "C" { +#include <wlr/types/wlr_xdg_shell.h> +} + +/* Expose per-surface keep-awake requests without turning them into a global + * inhibition. This is separate from the non-unloadable cursor plugin so it + * can be enabled on an existing desktop without restarting Wayfire. */ +namespace { +class digitalrain_idle_plugin_t : public wf::plugin_interface_t +{ + wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; + using client_t = wf::ipc::client_interface_t; + // A portal request holds one IPC connection and one exact window lifetime. + // Never turn an application id or a fullscreen state into a global lease. + std::map<client_t*, std::weak_ptr<wf::view_interface_t>> portal; + + static pid_t view_pid(wayfire_view view) + { + pid_t pid = -1; + if (view && view->get_client()) + wl_client_get_credentials(view->get_client(), &pid, nullptr, nullptr); + return pid; + } + + static void add_outputs(wayfire_view view, std::set<std::string>& outputs) + { + if (!view || !view->is_mapped() || !view->get_output()) return; + if (auto top = wf::toplevel_cast(view); top && top->minimized) return; + for (wf::scene::node_t *node = view->get_root_node().get(); node; node = node->parent()) + if (!node->is_enabled()) return; + auto output = view->get_output(); + auto box = view->get_bounding_box(); + auto screen = output->get_relative_geometry(); + if (wf::geometry_intersection(box, screen).width <= 0 || + wf::geometry_intersection(box, screen).height <= 0) return; + for (const auto& target : digitalrain_targets(output)) + { + auto overlap = wf::geometry_intersection(box, target.geometry); + if (overlap.width > 0 && overlap.height > 0) outputs.insert(target.name); + } + } + + wf::json_t portal_inhibit(wf::json_t data, client_t *client) + { + if (!client || portal.count(client) || portal.size() >= 128) + return wf::ipc::json_error("One bounded portal lease per IPC connection is required"); + auto pid = wf::ipc::json_get_int64(data, "pid"); + auto window = wf::ipc::json_get_string(data, "window"); + if (pid <= 0 || pid > INT32_MAX || window.size() > 512) + return wf::ipc::json_error("Invalid portal window identity"); + wayfire_view chosen; + if (window.rfind("wayland:", 0) == 0) + { + auto exported = wlr_xdg_foreign_registry_find_by_handle( + wf::get_core().protocols.foreign_registry, window.c_str() + 8); + if (exported) + chosen = wf::wl_surface_to_wayfire_view(exported->toplevel->base->surface->resource); + if (!chosen || view_pid(chosen) != pid) + return wf::ipc::json_error("Portal parent does not belong to its caller"); + } else + { + // Firefox sends g_get_prgname() here, not a native parent handle. + // A bare placeholder is no identity: select only by the original + // caller PID, just as for an omitted parent. Unsupported prefixed + // handles are rejected; invalid wayland: handles never fall back. + if (window.find(':') != std::string::npos) + return wf::ipc::json_error("Only native Wayland parent handles are supported"); + // Bind the focused root, or sole mapped root. Ambiguous unfocused + // multi-window apps must provide a native parent instead. + std::vector<wayfire_view> candidates; + for (auto view : wf::get_core().get_all_views()) + if (auto top = wf::toplevel_cast(view); top && !top->parent && + top->is_mapped() && !top->minimized && view_pid(view) == pid) + candidates.push_back(view); + auto active = wf::get_core().seat->get_active_view(); + while (auto top = wf::toplevel_cast(active)) + { + if (!top->parent) break; + active = top->parent; + } + if (std::find(candidates.begin(), candidates.end(), active) != candidates.end()) + chosen = active; + else if (candidates.size() == 1) chosen = candidates.front(); + } + if (!chosen || !chosen->is_mapped() || !wf::toplevel_cast(chosen)) + return wf::ipc::json_error("No unambiguous mapped portal window"); + portal[client] = chosen->weak_from_this(); + auto result = wf::ipc::json_ok(); + result["view-id"] = chosen->get_id(); + return result; + } + + wf::signal::connection_t<wf::ipc::client_disconnected_signal> disconnected = [this] (auto ev) + { portal.erase(ev->client); }; + wf::signal::connection_t<wf::view_pre_unmap_signal> unmapped = [this] (auto ev) + { + for (auto it = portal.begin(); it != portal.end();) + if (auto view = it->second.lock(); !view || view.get() == ev->view.get()) + it = portal.erase(it); + else ++it; + }; + + wf::json_t status() + { + std::set<std::string> outputs; + for (const auto& [_, weak] : portal) + if (auto view = weak.lock()) add_outputs(view->self(), outputs); + auto manager = wf::get_core().protocols.idle_inhibit; + if (manager) + { + wlr_idle_inhibitor_v1 *inhibitor; + wl_list_for_each(inhibitor, &manager->inhibitors, link) + { + auto surface = wlr_surface_get_root_surface(inhibitor->surface); + auto view = wf::wl_surface_to_wayfire_view(surface->resource); + // No occlusion test: a saver overlay must not hide its + // application's request from the manager below it. + add_outputs(view, outputs); + } + } + + wf::json_t response; + response["outputs"] = wf::json_t::array(); + for (const auto& output : outputs) + response["outputs"].append(output); + response["portal-requests"] = (int)portal.size(); + return response; + } + + public: + void init() override + { + repo->connect(&disconnected); + wf::get_core().connect(&unmapped); + repo->register_method("digitalrain/portal_inhibit", [this] (wf::json_t data, client_t *client) + { return portal_inhibit(data, client); }); + repo->register_method("digitalrain/idle_inhibitors", + [this] (wf::json_t) { return status(); }); + repo->register_method("digitalrain/lock_status", [] (wf::json_t) + { + bool inhibited = false; + for (auto output : wf::get_core().output_layout->get_outputs()) + inhibited |= output->is_inhibited(); + // Wayfire session-lock owns output input inhibition, including its + // crashed-client fallback. This method cannot release inhibition. + wf::json_t response; + response["protected"] = inhibited; + return response; + }); + } + + void fini() override + { + disconnected.disconnect(); + unmapped.disconnect(); + portal.clear(); + repo->unregister_method("digitalrain/portal_inhibit"); + repo->unregister_method("digitalrain/idle_inhibitors"); + repo->unregister_method("digitalrain/lock_status"); + } +}; + +} // Keep implementation symbols local when an old helper is still resident. +DECLARE_WAYFIRE_PLUGIN(digitalrain_idle_plugin_t); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-idle.xml b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-idle.xml new file mode 100644 index 000000000000..1bb955ed4786 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-idle.xml @@ -0,0 +1,8 @@ +<?xml version='1.0' encoding='utf-8'?> +<wayfire> + <plugin name="digitalrain-idle"> + <_short>Presentation inhibitors</_short> + <_long>Honor application and browser idle inhibitors on their physical monitors.</_long> + <category>Utility</category> + </plugin> +</wayfire> diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-live-manager.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-live-manager.h new file mode 100644 index 000000000000..412d257c2c13 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-live-manager.h @@ -0,0 +1,386 @@ +#include "digitalrain-audio-protocol.h" +struct dr_alert { + char source[64], id[64], output[64], message[128]; + int severity; + double until; + uint64_t sequence; +}; +static struct { + struct dr_alert alerts[64]; + uint64_t sequence; + int audio_fd; + pid_t audio_pid; + double retry, stopping, started; + unsigned failures, locker_revision; + char source[128]; + dr_audio_packet sample; + dr_effect_packet locker_effects; + bool locker_black, audio_failed; +} U = {.audio_fd = -1}; +static void live_locker_reset(void) { + U.locker_revision = 0; + U.locker_black = false; + memset(&U.locker_effects, 0, sizeof(U.locker_effects)); +} +static void live_locker_state(struct json_object *o) { + const char *phase = dr_string(o, "phase"); + U.locker_black = phase && !strcmp(phase, "black"); +} +static const char *alert_names[] = {"info", "success", "warning", "error"}; +static const struct dr_alert *live_winner(const char *output, double now, bool locked) { + if (!G.config.alerts_enabled || (locked && !G.config.alerts_locked)) + return NULL; + const struct dr_alert *winner = NULL; + for (int i = 0; i < 64; i++) { + const struct dr_alert *a = &U.alerts[i]; + if (a->until <= now || (a->output[0] && strcmp(a->output, output))) + continue; + int priority = G.config.themes[a->severity].priority; + if (!winner || priority > G.config.themes[winner->severity].priority || + (priority == G.config.themes[winner->severity].priority && + a->sequence > winner->sequence)) + winner = a; + } + return winner; +} +static bool live_audio_output(const char *output) { + if (!G.config.audio_output_count) + return true; + for (int i = 0; i < G.config.audio_output_count; i++) + if (!strcmp(output, G.config.audio_outputs[i])) + return true; + return false; +} +static bool live_audio_needed(void) { + if (!G.config.audio_enabled) + return false; + bool locked = api_locked(); + if (locked && (!G.config.audio_locked || U.locker_black || strcmp(A.lock_state, "locked"))) + return false; + for (int i = 0; i < G.output_count; i++) { + mgr_output_info_t *o = &G.outputs[i]; + if (live_audio_output(o->name) && + (locked || + (!strcmp(o->state, "off") && o->render_phase >= 0 && o->render_phase != PHASE_BLACK && + !o->powered_off && o->preview_until <= mgr_monotime()))) + return true; + } + return false; +} +static void live_audio_stop(void) { + if (U.audio_fd >= 0) { + close(U.audio_fd); + U.audio_fd = -1; + } + memset(&U.sample, 0, sizeof(U.sample)); + if (U.audio_pid && !U.stopping) { + kill(U.audio_pid, SIGTERM); + U.stopping = mgr_monotime(); + } +} +static void live_audio_tick(double now) { + if (U.audio_pid) { + int status; + pid_t result = waitpid(U.audio_pid, &status, WNOHANG); + if (result == U.audio_pid || (result < 0 && errno == ECHILD)) { + bool intentional = U.stopping > 0 && !U.audio_failed; + if (U.audio_fd >= 0) + close(U.audio_fd); + U.audio_fd = -1; + U.audio_pid = 0; + U.stopping = 0; + U.audio_failed = false; + memset(&U.sample, 0, sizeof(U.sample)); + if (!intentional) + U.retry = now + fmin(30, 1u << (U.failures < 5 ? U.failures++ : 5)); + } else if (U.stopping && now - U.stopping > 2) + kill(U.audio_pid, SIGKILL); + } + if (!live_audio_needed() || (U.audio_pid && strcmp(U.source, G.config.audio_source))) { + live_audio_stop(); + return; + } + if (U.audio_pid && !U.stopping && now - U.started > 5 && now - U.sample.time > 5) { + U.audio_failed = true; + live_audio_stop(); + return; + } + if (!U.audio_pid && now >= U.retry) { + int pair[2]; + char path[PATH_MAX]; + if (dr_sibling(path, sizeof(path), "digitalrain-audio") || + socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, pair)) { + U.retry = now + 30; + return; + } + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_adddup2(&actions, pair[1], 3); + posix_spawn_file_actions_addclosefrom_np(&actions, 4); + char *argv[] = {path, "3", G.config.audio_source, NULL}; + int rc = posix_spawn(&U.audio_pid, path, &actions, NULL, argv, environ); + posix_spawn_file_actions_destroy(&actions); + close(pair[1]); + if (rc) { + close(pair[0]); + U.audio_pid = 0; + U.retry = now + 30; + } else { + U.audio_failed = false; + U.audio_fd = pair[0]; + U.started = now; + snprintf(U.source, sizeof(U.source), "%s", G.config.audio_source); + } + } + if (U.audio_fd >= 0) { + dr_audio_packet p; + ssize_t n; + for (int i = 0; i < 8; i++) { + n = recv(U.audio_fd, &p, sizeof(p), MSG_DONTWAIT | MSG_TRUNC); + if (n <= 0) { + if (n == 0 || (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)) { + U.audio_failed = true; + live_audio_stop(); + } + break; + } + if (n == sizeof(p) && p.magic == DR_AUDIO_MAGIC && p.available <= 1 && + isfinite(p.time) && fabs(now - p.time) < 2 && isfinite(p.rms) && p.rms >= 0 && + p.rms <= 1 && isfinite(p.peak) && p.peak >= 0 && p.peak <= 1) { + U.sample = p; + if (p.available) + U.failures = 0; + } + } + } +} +static void live_effect(dr_effect_t *e, const mgr_output_info_t *o, bool locked, double now) { + memset(e, 0, sizeof(*e)); + snprintf(e->output, sizeof(e->output), "%s", o->name); + if (locked) { + e->x = o->target.x; + e->y = o->target.y; + e->width = o->target.width; + e->height = o->target.height; + } + const struct dr_alert *a = live_winner(o->name, now, locked); + if (a) { + rgb_t color = render_lookup_color(G.config.themes[a->severity].color); + e->color[0] = color.r; + e->color[1] = color.g; + e->color[2] = color.b; + e->until = a->until; + e->pulse = G.config.themes[a->severity].pulse; + } + if (G.config.audio_enabled && (!locked || G.config.audio_locked) && + live_audio_output(o->name)) { + e->speed = G.config.audio_speed; + e->brightness = G.config.audio_brightness; + if (U.sample.available && now - U.sample.time < .3) { + e->audio_time = U.sample.time; + e->energy = U.sample.rms >= G.config.audio_gate + ? fmin(1, U.sample.rms * G.config.audio_gain) + : 0; + e->peak = U.sample.rms >= G.config.audio_gate + ? fmin(1, U.sample.peak * G.config.audio_gain) + : 0; + } + } +} +static void live_tick(double now) { + live_audio_tick(now); + for (int i = 0; i < G.output_count; i++) { + mgr_output_info_t *o = &G.outputs[i]; + if (o->report_fd < 0 || o->preview_until > now) + continue; + if (o->sent_revision != C.revision) { + dr_config_packet p = {.magic = DR_LIVE_MAGIC, + .kind = DR_CONFIG, + .revision = C.revision, + .config = G.config}; + if (o->color[0]) + snprintf(p.config.matrix_color, sizeof(p.config.matrix_color), "%s", o->color); + if (send(o->report_fd, &p, sizeof(p), MSG_DONTWAIT | MSG_NOSIGNAL) == sizeof(p)) + o->sent_revision = C.revision; + else + continue; /* control has priority over effects */ + } + if (o->render_phase == PHASE_BLACK || o->powered_off) + continue; + dr_effect_packet p = {.magic = DR_LIVE_MAGIC, .kind = DR_EFFECTS, .count = 1}; + live_effect(&p.effects[0], o, false, now); + if (!p.effects[0].until && !p.effects[0].audio_time) + p.count = 0; + if (memcmp(&p, &o->sent_effects, sizeof(p)) && + send(o->report_fd, &p, sizeof(p), MSG_DONTWAIT | MSG_NOSIGNAL) == sizeof(p)) + o->sent_effects = p; + } + if (A.locker >= 0) { + if (U.locker_revision != C.revision) { + dr_config_packet p = {.magic = DR_LIVE_MAGIC, + .kind = DR_CONFIG, + .revision = C.revision, + .config = G.config}; + if (send(A.locker, &p, sizeof(p), MSG_DONTWAIT | MSG_NOSIGNAL) == sizeof(p)) + U.locker_revision = C.revision; + else + return; + } + if (U.locker_black) + return; + dr_effect_packet p = {.magic = DR_LIVE_MAGIC, .kind = DR_EFFECTS, .count = G.output_count}; + for (int i = 0; i < G.output_count; i++) + live_effect(&p.effects[i], &G.outputs[i], true, now); + if (memcmp(&p, &U.locker_effects, sizeof(p)) && + send(A.locker, &p, sizeof(p), MSG_DONTWAIT | MSG_NOSIGNAL) == sizeof(p)) + U.locker_effects = p; + } +} +static void live_status(struct json_object *o, bool event) { + (void)event; + double now = mgr_monotime(); + int count = 0; + for (int i = 0; i < 64; i++) + count += U.alerts[i].until > now; + json_object_object_add(o, "alert_count", json_object_new_int(count)); + json_object_object_add(o, "alert_revision", json_object_new_int64(U.sequence)); + struct json_object *audio = json_object_new_object(); + json_object_object_add(audio, "enabled", json_object_new_boolean(G.config.audio_enabled)); + json_object_object_add(audio, "source", json_object_new_string(G.config.audio_source)); + json_object_object_add(audio, "state", + json_object_new_string(!G.config.audio_enabled ? "disabled" + : !live_audio_needed() ? "idle" + : U.audio_fd < 0 ? "unavailable" + : U.sample.available && now - U.sample.time < 2 + ? "monitoring" + : "waiting_for_source")); + json_object_object_add(o, "audio", audio); + struct json_object *effects = json_object_new_object(); + for (int i = 0; i < G.output_count; i++) { + const struct dr_alert *a = live_winner(G.outputs[i].name, now, api_locked()); + struct json_object *e = json_object_new_object(); + if (a) { + json_object_object_add(e, "source", json_object_new_string(a->source)); + json_object_object_add(e, "id", json_object_new_string(a->id)); + json_object_object_add(e, "severity", json_object_new_string(alert_names[a->severity])); + } + json_object_object_add( + e, "visible", + json_object_new_boolean(a && + (api_locked() ? !U.locker_black + : G.outputs[i].render_phase >= 0 && + G.outputs[i].render_phase != PHASE_BLACK && + !strcmp(G.outputs[i].state, "off") && + G.outputs[i].preview_until <= now))); + json_object_object_add(effects, G.outputs[i].name, e); + } + json_object_object_add(o, "alerts_by_output", effects); +} +static bool live_string(struct json_object *o, const char *key, size_t max, const char **out) { + struct json_object *v; + *out = dr_string(o, key); + return *out && **out && strlen(*out) <= max && json_object_object_get_ex(o, key, &v) && + json_object_get_string_len(v) == (int)strlen(*out); +} +static struct json_object *live_alert_request(const char *method, struct json_object *req) { + bool list = !strcmp(method, "list_alerts"), clear = !strcmp(method, "clear_alert"); + json_object_object_foreach(req, key, val) { + (void)val; + if (!strcmp(key, "version") || !strcmp(key, "method")) + continue; + if (list && (!strcmp(key, "offset") || !strcmp(key, "limit"))) + continue; + if (!list && (!strcmp(key, "source") || !strcmp(key, "id"))) + continue; + if (!list && !clear && + (!strcmp(key, "severity") || !strcmp(key, "seconds") || !strcmp(key, "output") || + !strcmp(key, "message"))) + continue; + return dr_error("unexpected alert field"); + } + double now = mgr_monotime(); + if (list) { + double offset = 0, limit = 16; + struct json_object *v; + if ((json_object_object_get_ex(req, "offset", &v) && + (!api_number(req, "offset", &offset) || offset < 0 || offset > 64 || + floor(offset) != offset)) || + (json_object_object_get_ex(req, "limit", &v) && + (!api_number(req, "limit", &limit) || limit < 1 || limit > 16 || + floor(limit) != limit))) + return dr_error("offset 0..64 and limit 1..16 required"); + struct json_object *o = json_object_new_object(), *arr = json_object_new_array(); + int seen = 0; + for (int i = 0; i < 64; i++) + if (U.alerts[i].until > now) { + struct dr_alert *a = &U.alerts[i]; + int index = seen++; + if (index < offset || index >= offset + limit) + continue; + struct json_object *v = json_object_new_object(); + json_object_object_add(v, "source", json_object_new_string(a->source)); + json_object_object_add(v, "id", json_object_new_string(a->id)); + json_object_object_add(v, "output", json_object_new_string(a->output)); + json_object_object_add(v, "severity", + json_object_new_string(alert_names[a->severity])); + json_object_object_add(v, "message", json_object_new_string(a->message)); + json_object_object_add(v, "remaining_seconds", + json_object_new_double(a->until - now)); + json_object_array_add(arr, v); + } + json_object_object_add(o, "alerts", arr); + json_object_object_add(o, "total", json_object_new_int(seen)); + json_object_object_add( + o, "next_offset", + json_object_new_int(offset + limit < seen ? (int)(offset + limit) : -1)); + return o; + } + const char *source, *id; + if (!live_string(req, "source", 63, &source) || !live_string(req, "id", 63, &id)) + return dr_error("source and id required (1..63 bytes)"); + int slot = -1, free_slot = -1; + for (int i = 0; i < 64; i++) { + if (U.alerts[i].until <= now) + free_slot = i; + if (!strcmp(U.alerts[i].source, source) && !strcmp(U.alerts[i].id, id)) + slot = i; + } + if (clear) { + if (slot >= 0) + U.alerts[slot].until = 0; + U.sequence++; + return json_object_new_object(); + } + const char *severity = dr_string(req, "severity"), *output = dr_string(req, "output"), + *message = dr_string(req, "message"); + int level = -1; + for (int i = 0; severity && i < 4; i++) + if (!strcmp(severity, alert_names[i])) + level = i; + double seconds; + struct json_object *v; + if (level < 0 || !api_number(req, "seconds", &seconds) || seconds < 1 || seconds > 86400) + return dr_error("severity and seconds 1..86400 required"); + if (json_object_object_get_ex(req, "output", &v) && (!output || !mgr_find_output(output))) + return dr_error("unknown physical output"); + if (json_object_object_get_ex(req, "message", &v) && + (!message || strlen(message) > 127 || + json_object_get_string_len(v) != (int)strlen(message))) + return dr_error("message must be <=127 bytes"); + if (slot < 0) + slot = free_slot; + if (slot < 0) + return dr_error("64 active alerts already exist"); + struct dr_alert *a = &U.alerts[slot]; + memset(a, 0, sizeof(*a)); + snprintf(a->source, sizeof(a->source), "%s", source); + snprintf(a->id, sizeof(a->id), "%s", id); + snprintf(a->output, sizeof(a->output), "%s", output ? output : ""); + snprintf(a->message, sizeof(a->message), "%s", message ? message : ""); + a->severity = level; + a->until = now + seconds; + a->sequence = ++U.sequence; + struct json_object *o = json_object_new_object(); + json_object_object_add(o, "accepted", json_object_new_boolean(true)); + return o; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-live-render.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-live-render.h new file mode 100644 index 000000000000..ea7cca333a1f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-live-render.h @@ -0,0 +1,219 @@ +static int render_effect_index(int x, int y) { + for (unsigned i = 0; i < S.effects.count; i++) { + dr_effect_t *e = &S.effects.effects[i]; + if (x >= e->x && (e->width == 0 || x - e->x < e->width) && y >= e->y && + (e->height == 0 || y - e->y < e->height)) + return i; + } + return -1; +} +static double render_column_speed(int x, int y) { + int i = render_effect_index(x, y); + return i < 0 ? 1 : 1 + S.effect_energy[i] * S.effects.effects[i].speed; +} +static rgb_t render_column_color(int x, int y, rgb_t base, float *brightness) { + *brightness = 1; + int i = render_effect_index(x, y); + if (i < 0) + return base; + const dr_effect_t *e = &S.effects.effects[i]; + float t = S.effect_mix[i]; + base.r += (e->color[0] - base.r) * t; + base.g += (e->color[1] - base.g) * t; + base.b += (e->color[2] - base.b) * t; + *brightness += S.effect_peak[i] * e->brightness; + if (t > .001 && e->pulse > 0) + *brightness *= 1 - e->pulse * t * (1 + sin(S.effect_tick * 3.141592653589793)) * .5; + return base; +} +static void render_effect_tick(void) { + double now = render_monotime(), + dt = S.effect_tick > 0 ? fmin(.1, now - S.effect_tick) : 1.0 / TARGET_FPS; + S.effect_tick = now; + bool remains = false; + for (unsigned i = 0; i < S.effects.count; i++) { + dr_effect_t *e = &S.effects.effects[i]; + float target = now < e->until ? 1 : 0; + S.effect_mix[i] += (target - S.effect_mix[i]) * (1 - exp(-dt / .12)); + bool fresh = now - e->audio_time < .3; + float energy = fresh ? e->energy : 0, peak = fresh ? e->peak : 0; + double ms = energy > S.effect_energy[i] ? S.audio_attack : S.audio_release; + if (ms <= 0) + ms = 100; + float blend = 1 - exp(-dt * 1000 / ms); + S.effect_energy[i] += (energy - S.effect_energy[i]) * blend; + S.effect_peak[i] += (peak - S.effect_peak[i]) * blend; + remains |= target > 0 || energy > 0 || peak > 0 || S.effect_mix[i] > .0001 || + S.effect_energy[i] > .0001 || S.effect_peak[i] > .0001; + } + if (!remains) + S.effects.count = 0; + if (now < S.palette_until && !S.adaptive_color && S.color_mode == COLOR_FIXED) { + float t = 1 - exp(-dt / .12); + for (int l = 0; l < S.num_depth_layers; l++) + for (int c = 0; c < S.layers[l].cols; c++) { + rgb_t *color = &S.layers[l].columns[c].color; + color->r += (S.base_color.r - color->r) * t; + color->g += (S.base_color.g - color->g) * t; + color->b += (S.base_color.b - color->b) * t; + } + } +} +static void render_apply_effects(const dr_effect_packet *packet) { + dr_effect_packet next = *packet; + float mix[MAX_OUTPUTS] = {0}, energy[MAX_OUTPUTS] = {0}, peak[MAX_OUTPUTS] = {0}; + if (!next.count && S.effects.count) { + next = S.effects; + for (unsigned i = 0; i < next.count; i++) { + next.effects[i].until = 0; + next.effects[i].audio_time = 0; + } + } + for (unsigned i = 0; i < next.count; i++) + for (unsigned j = 0; j < S.effects.count; j++) + if (!strcmp(next.effects[i].output, S.effects.effects[j].output)) { + mix[i] = S.effect_mix[j]; + energy[i] = S.effect_energy[j]; + peak[i] = S.effect_peak[j]; + if (next.effects[i].until <= render_monotime()) + memcpy(next.effects[i].color, S.effects.effects[j].color, + sizeof(next.effects[i].color)); + break; + } + S.effects = next; + memcpy(S.effect_mix, mix, sizeof(mix)); + memcpy(S.effect_energy, energy, sizeof(energy)); + memcpy(S.effect_peak, peak, sizeof(peak)); +} +static void render_layers_free(depth_layer_t *layers) { + for (int i = 0; i < MAX_DEPTH_LAYERS; i++) { + free(layers[i].columns); + free(layers[i].cells); + } +} +static bool render_apply_config(const dr_config_packet *packet, bool locking) { + if (!dr_config_valid(packet)) + return false; + if (packet->revision == S.applied_revision) + return true; + S.attempted_revision = packet->revision; + const dr_config_t *c = &packet->config; + __typeof__(S) old = S; + bool resources = + S.font_size != c->matrix_font_size || S.num_depth_layers != c->matrix_depth_layers; + S.font_size = c->matrix_font_size; + S.num_depth_layers = c->matrix_depth_layers; + S.min_speed = c->matrix_min_speed; + S.max_speed = c->matrix_max_speed; + S.avg_speed = c->matrix_avg_speed; + S.min_trail = c->matrix_min_trail; + S.max_trail = c->matrix_max_trail; + S.min_density = c->matrix_min_density; + S.max_density = c->matrix_max_density; + S.layer_near_density = c->matrix_layer_near_density; + S.layer_far_density = c->matrix_layer_far_density; + S.head_hold_frames = c->matrix_head_hold_frames; + S.head_mutation_chance = c->matrix_head_mutation_chance; + S.head_white_min = c->matrix_head_white_min; + S.head_white_max = c->matrix_head_white_max; + S.transparent_timeout = c->matrix_transparent_timeout; + S.black_rain_timeout = c->matrix_black_rain_timeout; + if (old.fade_frames > 0) + S.phase_frame = (int)((double)old.phase_frame * c->matrix_fade_frames / old.fade_frames); + S.fade_frames = c->matrix_fade_frames; + S.audio_attack = c->audio_attack; + S.audio_release = c->audio_release; + if (strcmp(S.live_color, c->matrix_color)) { + S.palette_until = render_monotime() + 1; + snprintf(S.live_color, sizeof(S.live_color), "%s", c->matrix_color); + S.adaptive_color = is_adaptive_color(c->matrix_color); + S.color_mode = !strcmp(c->matrix_color, "random_trail_named") ? COLOR_RANDOM + : !strcmp(c->matrix_color, "random_trail_hex") ? COLOR_RANDOM_HEX + : COLOR_FIXED; + if (S.adaptive_color) { + render_load_adaptive_snapshot(); + S.base_color = + locking || S.background_black ? S.adaptive_black_color : S.adaptive_desktop_color; + } else + S.base_color = render_lookup_color(c->matrix_color); + } + if (resources) { + memset(S.layers, 0, sizeof(S.layers)); + memset(&S.atlas, 0, sizeof(S.atlas)); + int font = (int)(S.font_size * LAYER_NEAR_FONT_SCALE + .5); + if (locking && font < 18) + font = 18; + while (glGetError() != GL_NO_ERROR) { + } + if (!render_build_atlas(font) || glGetError() != GL_NO_ERROR || + !render_init_matrix(S.surface_width, S.surface_height)) { + render_layers_free(S.layers); + if (S.atlas.texture) + glDeleteTextures(1, &S.atlas.texture); + S = old; + S.live_error = 1; + return false; + } + /* Map existing rain into the new grid; phase clocks and head positions survive. */ + for (int l = 0; l < S.num_depth_layers && l < old.num_depth_layers; l++) { + depth_layer_t *n = &S.layers[l], *o = &old.layers[l]; + for (int x = 0; x < n->cols; x++) { + int ox = (int)((double)x * n->cell_w / o->cell_w); + if (ox >= o->cols) + ox = o->cols - 1; + double scale = (double)o->cell_h / n->cell_h; + n->columns[x] = o->columns[ox]; + n->columns[x].head_pos *= scale; + n->columns[x].last_head_row = n->columns[x].head_pos; + for (int y = 0; y < n->rows; y++) { + int oy = (int)(y / scale); + if (oy >= o->rows) + oy = o->rows - 1; + n->cells[y * n->cols + x] = o->cells[oy * o->cols + ox]; + } + } + } + render_layers_free(old.layers); + if (old.atlas.texture) + glDeleteTextures(1, &old.atlas.texture); + } + for (int l = 0; l < S.num_depth_layers; l++) { + depth_layer_t *layer = &S.layers[l]; + render_get_layer_properties(l, S.num_depth_layers, &layer->font_scale, &layer->opacity, + &layer->speed_scale, &layer->density); + double ratio = old.avg_speed > 0 ? S.avg_speed / old.avg_speed : 1; + for (int x = 0; x < layer->cols; x++) { + render_column_t *col = &layer->columns[x]; + col->speed = fmin(S.max_speed * layer->speed_scale, + fmax(S.min_speed * layer->speed_scale, col->speed * ratio)); + col->trail_len = fmin(S.max_trail, fmax(S.min_trail, col->trail_len)); + } + } + S.applied_revision = packet->revision; + S.live_error = 0; + return true; +} +static void render_live_receive(void) { + if (S.report_fd < 0) + return; + union { + dr_config_packet config; + dr_effect_packet effects; + } packet; + for (int i = 0; i < 8; i++) { + ssize_t n = recv(S.report_fd, &packet, sizeof(packet), MSG_DONTWAIT | MSG_TRUNC); + if (n <= 0) { + if (n == 0) { + close(S.report_fd); + S.report_fd = -1; + if (S.ambient) S.stop = 1; + } + break; + } + if (n == sizeof(dr_config_packet) && dr_config_valid(&packet.config)) + render_apply_config(&packet.config, false); + else if (n == sizeof(dr_effect_packet) && dr_effect_valid(&packet.effects)) + render_apply_effects(&packet.effects); + } + render_report(); +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-live.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-live.h new file mode 100644 index 000000000000..b9ee8f53db6f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-live.h @@ -0,0 +1,76 @@ +/* Private process protocol: fixed-size, bounded snapshots; never public JSON. */ +#ifndef DIGITALRAIN_LIVE_H +#define DIGITALRAIN_LIVE_H +#define DR_LIVE_MAGIC UINT32_C(0x44524c32) +enum { DR_CONFIG = 1, DR_EFFECTS = 2, DR_REPORT = 3 }; +typedef struct { + char output[64]; + int x, y, width, height; + double until, audio_time; + float color[3], pulse, energy, peak, speed, brightness; +} dr_effect_t; +typedef struct { + uint32_t magic, kind, revision, count; + dr_effect_t effects[MAX_OUTPUTS]; +} dr_effect_packet; +typedef struct { + uint32_t magic, kind, revision; + dr_config_t config; +} dr_config_packet; +typedef struct { + uint32_t magic, kind, revision; + int phase, power, error, font_size, depth_layers; +} dr_report_packet; +static bool dr_effect_valid(const dr_effect_packet *p) { + if (p->magic != DR_LIVE_MAGIC || p->kind != DR_EFFECTS || p->count > MAX_OUTPUTS) + return false; + for (unsigned i = 0; i < p->count; i++) { + const dr_effect_t *e = &p->effects[i]; + if (!memchr(e->output, 0, 64) || e->x < 0 || e->y < 0 || e->width < 0 || e->height < 0 || + !isfinite(e->until) || !isfinite(e->audio_time) || !isfinite(e->pulse) || + e->pulse < 0 || e->pulse > .25 || !isfinite(e->energy) || e->energy < 0 || + e->energy > 1 || !isfinite(e->peak) || e->peak < 0 || e->peak > 1 || + !isfinite(e->speed) || e->speed < 0 || e->speed > 2 || !isfinite(e->brightness) || + e->brightness < 0 || e->brightness > 1) + return false; + for (int j = 0; j < 3; j++) + if (!isfinite(e->color[j]) || e->color[j] < 0 || e->color[j] > 1) + return false; + } + return true; +} +static bool dr_config_valid(const dr_config_packet *p) { + if (p->magic != DR_LIVE_MAGIC || p->kind != DR_CONFIG) + return false; + const dr_config_t *c = &p->config; + for (size_t i = 0; i < SETTING_COUNT; i++) { + const struct api_setting *s = &api_settings[i]; + const char *v = (const char *)c + s->offset; + if (s->type >= 2) { + if (!memchr(v, 0, s->width) || (s->type == 2 && !api_color_valid(v))) + return false; + } else { + double n = s->type == 1 ? *(const double *)v : *(const int *)v; + if (!isfinite(n) || n < s->lo || n > s->hi) + return false; + } + } + if (cf_relationships(c) || c->output_count < 0 || c->output_count > 64 || + c->audio_output_count < 0 || c->audio_output_count > 8) + return false; + for (int i = 0; i < c->output_count; i++) + if (!memchr(c->outputs[i].name, 0, 64) || !memchr(c->outputs[i].color, 0, 32) || + (c->outputs[i].color[0] && !api_color_valid(c->outputs[i].color)) || + c->outputs[i].timeout < -1 || c->outputs[i].timeout > 86400 || + c->outputs[i].power_off < -1 || c->outputs[i].power_off > 1) + return false; + for (int i = 0; i < c->audio_output_count; i++) + if (!memchr(c->audio_outputs[i], 0, 64)) + return false; + for (int i = 0; i < 4; i++) + if (!memchr(c->themes[i].color, 0, 32) || !api_color_valid(c->themes[i].color) || + !isfinite(c->themes[i].pulse) || c->themes[i].pulse < 0 || c->themes[i].pulse > .25) + return false; + return true; +} +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-lock.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-lock.c new file mode 100644 index 000000000000..3f3903d0f4a5 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-lock.c @@ -0,0 +1,1146 @@ +/* Native ext-session-lock client. The compositor, not a saver overlay, owns + * protection. Reuse the existing rain engine without linking PAM into it. */ +#define main digitalrain_unused_main +#include "digitalrain.c" +#undef main +#include "digitalrain-control.h" +#include "ext-session-lock-v1-client-protocol.h" +#include <xkbcommon/xkbcommon.h> +#include <xkbcommon/xkbcommon-compose.h> +#include <locale.h> +#include <sys/resource.h> +#include "../hyperspace/hyperspace-lock.h" + +#define LOCK_OUTPUTS 64 +struct lock_output { + uint32_t global; + struct wl_output *output; + struct ext_session_lock_surface_v1 *lock_surface; + __typeof__(S) render; + bool ready, dirty; + void *space, *composite; + char name[128]; +}; +static struct { + struct wl_display *display; + struct wl_compositor *compositor; + struct ext_session_lock_manager_v1 *manager; + struct ext_session_lock_v1 *lock; + struct wl_seat *seat; + struct wl_keyboard *keyboard; + struct wl_pointer *pointer; + struct xkb_context *xkb; + struct xkb_keymap *keymap; + struct xkb_state *keys; + struct xkb_compose_table *compose_table; + struct xkb_compose_state *compose; + struct lock_output outputs[LOCK_OUTPUTS]; + saver_target_t targets[MAX_OUTPUTS]; + int target_count; + int power_fds[MAX_OUTPUTS]; + bool power_tried, topology_dirty; + int listener, clients[DR_CLIENTS]; + bool watching[DR_CLIENTS]; + int auth_fd; + pid_t auth_pid; + bool confirmed, finished, failed, dirty, prompt, black, caps; + bool auth_byte; + double started, last_input, retry_after, next_frame, auth_started; + unsigned generation; + dr_config_t power_config; + dr_config_packet appearance; + dr_effect_packet effects; + char password[1024]; + size_t password_len; + char message[80]; + struct json_object *theme, *space_settings, *matrix_settings, *scenes; + unsigned theme_revision; + bool ambient_theme; + char background[24]; + double settings_due, fade_updated, opacity, ui_opacity; + time_t clock_minute; +} L; +static double bounded(double v, double lo, double hi); +static void refresh_appearance(void); +static bool theme_bool(const char *key); +static double theme_number(const char *key, double fallback, double lo, double hi); +static struct json_object *theme_value(const char *key); +static bool controls_on(const char *name, const char *desktop); +static volatile sig_atomic_t lock_stop; +static void stop_lock(int sig) { + (void)sig; + lock_stop = 1; +} +static void lock_changed(void) { + L.dirty = true; + ++L.generation; +} +static void restore_monitors(void) { + for (int i = 0; i < MAX_OUTPUTS; i++) + if (L.power_fds[i] >= 0) { + render_desktop_request(L.power_fds[i], "digitalrain/power_on", "{}"); + close(L.power_fds[i]); + L.power_fds[i] = -1; + } + L.power_tried = false; +} +static void power_monitors(void) { + if (L.power_tried || !L.confirmed || !L.black || L.prompt) + return; + L.power_tried = true; + for (int i = 0; i < G.output_count; i++) { + int power_off = L.power_config.matrix_power_off; + for (int j = 0; j < L.power_config.output_count; j++) + if (!strcmp(G.outputs[i].name, L.power_config.outputs[j].name) && L.power_config.outputs[j].power_off >= 0) power_off = L.power_config.outputs[j].power_off; + if (!power_off) + continue; + int fd = mgr_ipc_connect(); + if (fd < 0) + continue; + struct json_object *data = json_object_new_object(); + json_object_object_add(data, "output", json_object_new_string(G.outputs[i].name)); + bool ok = + render_desktop_request(fd, "digitalrain/power_off", + json_object_to_json_string_ext(data, JSON_C_TO_STRING_PLAIN)); + json_object_put(data); + if (ok) + L.power_fds[i] = fd; + else + close(fd); + } +} + +static struct json_object *lock_status(void) { + struct json_object *o = json_object_new_object(); + json_object_object_add(o, "state", json_object_new_string(L.confirmed ? "locked" : "locking")); + json_object_object_add(o, "pid", json_object_new_int(getpid())); + json_object_object_add(o, "phase", json_object_new_string(L.black ? "black" : "rain")); + json_object_object_add(o, "background", json_object_new_string(L.ambient_theme ? L.background : "digitalrain")); + json_object_object_add(o, "background_opacity", json_object_new_double(L.ambient_theme ? L.opacity : !L.black)); + json_object_object_add(o, "clock_visible", json_object_new_boolean(L.ui_opacity > 0 && theme_bool("show_clock"))); + json_object_object_add(o, "controls_opacity", json_object_new_double(L.ui_opacity)); + json_object_object_add(o, "prompt_visible", json_object_new_boolean(L.ui_opacity > 0 && (L.prompt || theme_bool("ui_on_interaction")))); + struct json_object *controls = json_object_new_array(); + for (int i=0;i<L.target_count;i++) + if (controls_on(L.targets[i].name,L.targets[i].desktop)) + json_object_array_add(controls,json_object_new_string(L.targets[i].name)); + json_object_object_add(o, "controls_outputs", controls); + json_object_object_add(o, "theme_revision", json_object_new_int(L.theme_revision)); + json_object_object_add(o, "desired_revision", json_object_new_int(L.appearance.revision)); + struct json_object *revisions = json_object_new_object(); + for (int i = 0; i < LOCK_OUTPUTS; i++) if (L.outputs[i].ready) { + struct json_object *v = json_object_new_object(); + json_object_object_add(v, "applied_revision", json_object_new_int(L.ambient_theme ? L.appearance.revision : L.outputs[i].render.applied_revision)); + json_object_object_add(v, "appearance_error", json_object_new_boolean(L.outputs[i].render.live_error)); + json_object_object_add(revisions, L.outputs[i].name, v); + } + json_object_object_add(o, "outputs", revisions); + json_object_object_add(o, "generation", json_object_new_int64(L.generation)); + return o; +} +static void input_activity(void) { + if (L.power_tried) + restore_monitors(); + L.last_input = render_monotime(); + L.fade_updated = L.last_input; + L.next_frame = L.last_input; + if (L.ambient_theme) { L.started=L.last_input;L.black=false; } + if (!L.prompt) { + L.prompt = true; + lock_changed(); + } + L.dirty = true; +} +static void erase_password(void) { + explicit_bzero(L.password, sizeof(L.password)); + L.password_len = 0; + if (L.compose) + xkb_compose_state_reset(L.compose); +} +static void authenticate(void) { + if (!L.confirmed || L.auth_pid || !L.password_len || render_monotime() < L.retry_after) + return; + int input[2], output[2]; + char helper[PATH_MAX]; + if (dr_sibling(helper, sizeof(helper), "digitalrain-auth") || pipe2(input, O_CLOEXEC)) + goto fail; + if (pipe2(output, O_CLOEXEC | O_NONBLOCK)) { + close(input[0]); + close(input[1]); + goto fail; + } + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_adddup2(&actions, input[0], STDIN_FILENO); + posix_spawn_file_actions_adddup2(&actions, output[1], STDOUT_FILENO); + posix_spawn_file_actions_addclosefrom_np(&actions, 3); + char *argv[] = {helper, NULL}; + int rc = posix_spawn(&L.auth_pid, helper, &actions, NULL, argv, environ); + posix_spawn_file_actions_destroy(&actions); + close(input[0]); + close(output[1]); + if (rc) { + close(input[1]); + close(output[0]); + L.auth_pid = 0; + goto fail; + } + /* Less than PIPE_BUF: one bounded write into a fresh pipe. */ + ssize_t n = write(input[1], L.password, L.password_len); + close(input[1]); + if (n != (ssize_t)L.password_len) + kill(L.auth_pid, SIGKILL); + erase_password(); + L.auth_fd = output[0]; + L.auth_started = render_monotime(); + L.auth_byte = false; + snprintf(L.message, sizeof(L.message), "Checking password..."); + lock_changed(); + return; +fail: + erase_password(); + snprintf(L.message, sizeof(L.message), "Authentication unavailable"); + L.retry_after = render_monotime() + 3; + lock_changed(); +} +static bool finish_auth(void) { + if (!L.auth_pid) + return false; + char buf[2]; + ssize_t n = read(L.auth_fd, buf, sizeof(buf)); + if (n == 1 && buf[0] == 'Y') + L.auth_byte = true; + else if (n > 0) { + L.auth_byte = false; + kill(L.auth_pid, SIGKILL); + } + if (render_monotime() - L.auth_started > 60) + kill(L.auth_pid, SIGKILL); + int status; + if (waitpid(L.auth_pid, &status, WNOHANG) != L.auth_pid) + return false; + /* The worker may write and exit between the nonblocking read and waitpid. */ + if (!L.auth_byte && n < 0 && errno == EAGAIN) { + n = read(L.auth_fd, buf, sizeof(buf)); + L.auth_byte = n == 1 && buf[0] == 'Y'; + } + close(L.auth_fd); + L.auth_fd = -1; + L.auth_pid = 0; + if (L.auth_byte && WIFEXITED(status) && WEXITSTATUS(status) == 0 && L.confirmed) { + ext_session_lock_v1_unlock_and_destroy(L.lock); + L.lock = NULL; + if (wl_display_roundtrip(L.display) < 0) { + L.failed = true; + return false; + } + struct json_object *o = json_object_new_object(); + json_object_object_add(o, "state", json_object_new_string("unlocked")); + for (int i = 0; i < DR_CLIENTS; ++i) + if (L.clients[i] >= 0 && L.watching[i]) + dr_send(L.clients[i], o); + json_object_put(o); + return true; + } + erase_password(); + snprintf(L.message, sizeof(L.message), "Authentication failed - try again"); + L.retry_after = render_monotime() + 2; + lock_changed(); + return false; +} + +static void keymap(void *data, struct wl_keyboard *kb, uint32_t format, int fd, uint32_t size) { + (void)data; + (void)kb; + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 || !size || size > 1024 * 1024) { + close(fd); + return; + } + char *map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if (map == MAP_FAILED) + return; + if (!memchr(map, 0, size)) { + munmap(map, size); + return; + } + struct xkb_keymap *km = xkb_keymap_new_from_string(L.xkb, map, XKB_KEYMAP_FORMAT_TEXT_V1, 0); + munmap(map, size); + if (!km) + return; + struct xkb_state *ks = xkb_state_new(km); + if (!ks) { + xkb_keymap_unref(km); + return; + } + if (L.keys) + xkb_state_unref(L.keys); + if (L.keymap) + xkb_keymap_unref(L.keymap); + L.keys = ks; + L.keymap = km; +} +static void key_enter(void *d, struct wl_keyboard *k, uint32_t s, struct wl_surface *sf, + struct wl_array *keys) { + (void)d; + (void)k; + (void)s; + (void)sf; + (void)keys; +} +static void key_leave(void *d, struct wl_keyboard *k, uint32_t s, struct wl_surface *sf) { + (void)d; + (void)k; + (void)s; + (void)sf; + erase_password(); + L.dirty = true; +} +static void key(void *d, struct wl_keyboard *k, uint32_t serial, uint32_t time, uint32_t code, + uint32_t state) { + (void)d; + (void)k; + (void)serial; + (void)time; + if (state != WL_KEYBOARD_KEY_STATE_PRESSED || !L.keys) + return; + input_activity(); + if (L.auth_pid || render_monotime() < L.retry_after) + return; + xkb_keysym_t sym = xkb_state_key_get_one_sym(L.keys, code + 8); + if (sym == XKB_KEY_Return || sym == XKB_KEY_KP_Enter) { + authenticate(); + return; + } + if (sym == XKB_KEY_Escape) { + erase_password(); + L.prompt = false; + L.message[0] = 0; + lock_changed(); + return; + } + if (sym == XKB_KEY_BackSpace) { + if (L.password_len) { + size_t end = L.password_len; + --L.password_len; + while (L.password_len && ((unsigned char)L.password[L.password_len] & 0xc0) == 0x80) + --L.password_len; + explicit_bzero(L.password + L.password_len, end - L.password_len); + } + return; + } + if (xkb_state_mod_name_is_active(L.keys, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) { + if (sym == XKB_KEY_u || sym == XKB_KEY_U) + erase_password(); + return; + } + char text[64] = {0}; + int len = 0; + if (L.compose && xkb_compose_state_feed(L.compose, sym) == XKB_COMPOSE_FEED_ACCEPTED) { + switch (xkb_compose_state_get_status(L.compose)) { + case XKB_COMPOSE_COMPOSING: + return; + case XKB_COMPOSE_CANCELLED: + xkb_compose_state_reset(L.compose); + return; + case XKB_COMPOSE_COMPOSED: + len = xkb_compose_state_get_utf8(L.compose, text, sizeof(text)); + xkb_compose_state_reset(L.compose); + break; + default: + len = xkb_state_key_get_utf8(L.keys, code + 8, text, sizeof(text)); + } + } else + len = xkb_state_key_get_utf8(L.keys, code + 8, text, sizeof(text)); + if (len > 0 && len < (int)sizeof(text) && (unsigned char)text[0] >= 32 && + (unsigned char)text[0] != 127 && L.password_len + len < sizeof(L.password)) { + memcpy(L.password + L.password_len, text, len); + L.password_len += len; + L.message[0] = 0; + } + explicit_bzero(text, sizeof(text)); +} +static void modifiers(void *d, struct wl_keyboard *k, uint32_t s, uint32_t depressed, + uint32_t latched, uint32_t locked, uint32_t group) { + (void)d; + (void)k; + (void)s; + if (!L.keys) + return; + xkb_state_update_mask(L.keys, depressed, latched, locked, 0, 0, group); + L.caps = xkb_state_mod_name_is_active(L.keys, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_EFFECTIVE) > 0; + L.dirty = true; +} +static void repeat(void *d, struct wl_keyboard *k, int32_t rate, int32_t delay) { + (void)d; + (void)k; + (void)rate; + (void)delay; +} +static const struct wl_keyboard_listener keyboard_listener = {keymap, key_enter, key_leave, + key, modifiers, repeat}; +static void pointer_enter(void *d, struct wl_pointer *p, uint32_t s, struct wl_surface *sf, + wl_fixed_t x, wl_fixed_t y) { + (void)d; + (void)sf; + (void)x; + (void)y; + wl_pointer_set_cursor(p, s, NULL, 0, 0); +} +static void pointer_leave(void *d, struct wl_pointer *p, uint32_t s, struct wl_surface *sf) { + (void)d; + (void)p; + (void)s; + (void)sf; +} +static void pointer_motion(void *d, struct wl_pointer *p, uint32_t t, wl_fixed_t x, wl_fixed_t y) { + (void)d; + (void)p; + (void)t; + (void)x; + (void)y; + input_activity(); +} +static void pointer_button(void *d, struct wl_pointer *p, uint32_t s, uint32_t t, uint32_t b, + uint32_t st) { + (void)d; + (void)p; + (void)s; + (void)t; + (void)b; + (void)st; + input_activity(); +} +static void pointer_axis(void *d, struct wl_pointer *p, uint32_t t, uint32_t a, wl_fixed_t v) { + (void)d; + (void)p; + (void)t; + (void)a; + (void)v; + input_activity(); +} +static const struct wl_pointer_listener pointer_listener = {.enter = pointer_enter, + .leave = pointer_leave, + .motion = pointer_motion, + .button = pointer_button, + .axis = pointer_axis}; +static void seat_caps(void *d, struct wl_seat *seat, uint32_t caps) { + (void)d; + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !L.keyboard) { + L.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(L.keyboard, &keyboard_listener, NULL); + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && L.keyboard) { + wl_keyboard_release(L.keyboard); + L.keyboard = NULL; + erase_password(); + } + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !L.pointer) { + L.pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(L.pointer, &pointer_listener, NULL); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && L.pointer) { + wl_pointer_release(L.pointer); + L.pointer = NULL; + } +} +static void seat_name(void *d, struct wl_seat *s, const char *n) { + (void)d; + (void)s; + (void)n; +} +static const struct wl_seat_listener seat_listener = {seat_caps, seat_name}; +static void output_geometry(void *d, struct wl_output *o, int32_t x, int32_t y, int32_t pw, + int32_t ph, int32_t sp, const char *m, const char *model, int32_t tr) { + (void)d; + (void)o; + (void)x; + (void)y; + (void)pw; + (void)ph; + (void)sp; + (void)m; + (void)model; + (void)tr; +} +static void output_mode(void *d, struct wl_output *o, uint32_t f, int32_t w, int32_t h, int32_t r) { + (void)d; + (void)o; + (void)f; + (void)w; + (void)h; + (void)r; +} +static void output_done(void *d, struct wl_output *o) { + (void)d; + (void)o; +} +static void output_scale(void *d, struct wl_output *o, int32_t s) { + (void)d; + (void)o; + (void)s; +} +static void output_name(void *d, struct wl_output *o, const char *name) { + (void)o; + snprintf(((struct lock_output *)d)->name, 128, "%s", name); +} +static void output_description(void *d, struct wl_output *o, const char *s) { + (void)d; + (void)o; + (void)s; +} +static const struct wl_output_listener output_listener = { + output_geometry, output_mode, output_done, output_scale, output_name, output_description}; + +static void configure(void *data, struct ext_session_lock_surface_v1 *surface, uint32_t serial, + uint32_t width, uint32_t height) { + struct lock_output *o = data; + if (!width || !height || width > 32768 || height > 32768) { + L.failed = true; + return; + } + ext_session_lock_surface_v1_ack_configure(surface, serial); + o->render.surface_width = width; + o->render.surface_height = height; + o->render.configured = true; + o->dirty = true; + /* Finish output reconfiguration while its display can deliver frames. */ + restore_monitors(); +} +static const struct ext_session_lock_surface_v1_listener surface_listener = {configure}; +static void create_surface(struct lock_output *o) { + o->render.surface = wl_compositor_create_surface(L.compositor); + o->lock_surface = ext_session_lock_v1_get_lock_surface(L.lock, o->render.surface, o->output); + ext_session_lock_surface_v1_add_listener(o->lock_surface, &surface_listener, o); +} +static void free_output(struct lock_output *o) { + if (o->ready) { + S = o->render; + eglMakeCurrent(S.egl_display, S.egl_surface, S.egl_surface, S.egl_context); + ambient_space_destroy(o->space); + ambient_lock_composite_destroy(o->composite); + for (int i = 0; i < S.num_depth_layers; i++) { + free(S.layers[i].columns); + free(S.layers[i].cells); + } + free(S.vertex_buf); + glDeleteTextures(1, &S.atlas.texture); + glDeleteBuffers(1, &S.vbo); + glDeleteProgram(S.shader_program); + FT_Done_Face(S.ft_face); + FT_Done_FreeType(S.ft_library); + eglMakeCurrent(S.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(S.egl_display, S.egl_surface); + eglDestroyContext(S.egl_display, S.egl_context); + wl_egl_window_destroy(S.egl_window); + /* EGLDisplay is shared: never terminate it while another output lives. */ + } + if (o->lock_surface) + ext_session_lock_surface_v1_destroy(o->lock_surface); + if (o->render.surface) + wl_surface_destroy(o->render.surface); + if (o->output) + wl_output_destroy(o->output); + memset(o, 0, sizeof(*o)); +} +static void global(void *d, struct wl_registry *reg, uint32_t id, const char *iface, + uint32_t version) { + (void)d; + if (!strcmp(iface, wl_compositor_interface.name)) + L.compositor = + wl_registry_bind(reg, id, &wl_compositor_interface, version < 4 ? version : 4); + else if (!strcmp(iface, ext_session_lock_manager_v1_interface.name)) + L.manager = wl_registry_bind(reg, id, &ext_session_lock_manager_v1_interface, 1); + else if (!strcmp(iface, wl_seat_interface.name) && !L.seat) { + L.seat = wl_registry_bind(reg, id, &wl_seat_interface, version < 4 ? version : 4); + wl_seat_add_listener(L.seat, &seat_listener, NULL); + } else if (!strcmp(iface, wl_output_interface.name)) { + L.topology_dirty = true; + for (int i = 0; i < LOCK_OUTPUTS; i++) + if (!L.outputs[i].output) { + struct lock_output *o = &L.outputs[i]; + o->global = id; + o->output = + wl_registry_bind(reg, id, &wl_output_interface, version < 4 ? version : 4); + wl_output_add_listener(o->output, &output_listener, o); + if (L.lock) + create_surface(o); + return; + } + L.failed = true; + } +} +static void global_remove(void *d, struct wl_registry *reg, uint32_t id) { + (void)d; + (void)reg; + for (int i = 0; i < LOCK_OUTPUTS; i++) + if (L.outputs[i].global == id) + free_output(&L.outputs[i]); + L.topology_dirty = true; +} +static const struct wl_registry_listener registry_listener = {global, global_remove}; +static void locked(void *d, struct ext_session_lock_v1 *l) { + (void)d; + (void)l; + L.confirmed = true; + lock_changed(); +} +static void finished(void *d, struct ext_session_lock_v1 *l) { + (void)d; + L.finished = true; + if (!L.confirmed) + ext_session_lock_v1_destroy(l); + /* Never send unlock as a response to an unexpected finished event. */ + L.lock = NULL; +} +static const struct ext_session_lock_v1_listener lock_listener = {locked, finished}; +static double bounded(double v, double lo, double hi) { + return isfinite(v) ? fmin(hi, fmax(lo, v)) : lo; +} +static struct json_object *theme_value(const char *key) { + struct json_object *v=NULL; + if (L.theme) json_object_object_get_ex(L.theme,key,&v); + return v; +} +static double theme_number(const char *key,double fallback,double lo,double hi) { + struct json_object *v=theme_value(key); + return v ? bounded(json_object_get_double(v),lo,hi) : fallback; +} +static bool theme_bool(const char *key) { + return L.ambient_theme && json_object_get_boolean(theme_value(key)); +} +static bool controls_on(const char *name,const char *desktop) { + const char *selected=json_object_get_string(theme_value("output")); + if (!selected || !*selected || !strcmp(selected,"all")) return true; + for (int i=0;i<L.target_count;i++) + if (!strcmp(selected,L.targets[i].name) || !strcmp(selected,L.targets[i].desktop)) + return !strcmp(selected,name) || !strcmp(selected,desktop); + // Retain the saved choice, but never leave a disconnected monitor as the + // only place where a user can see authentication feedback. + if (L.target_count) return !strcmp(name,L.targets[0].name); + for (int i=0;i<LOCK_OUTPUTS;i++) + if (L.outputs[i].output && L.outputs[i].render.configured) + return !strcmp(desktop,L.outputs[i].name); + return false; +} +static void refresh_appearance(void) { + L.settings_due=render_monotime()+1; + struct json_object *status=mgr_ipc_request("ambient/status","{}"),*theme=NULL,*space=NULL,*matrix=NULL; + if (!status || !json_object_object_get_ex(status,"lock_screen",&theme) || + !json_object_object_get_ex(status,"hyperspace",&space) || + !json_object_object_get_ex(status,"matrix",&matrix)) { + if (status) json_object_put(status); + return; // The independent locker retains its last valid appearance. + } + const char *selected=dr_string(theme,"background"); + if (!selected) { json_object_put(status);return; } + if (!strcmp(selected,"inherit")) selected=dr_string(status,"backend"); + if (!selected || (strcmp(selected,"hyperspace") && strcmp(selected,"nebula") && strcmp(selected,"matrix") && + strcmp(selected,"digitalrain") && strcmp(selected,"black"))) { + json_object_put(status);return; + } + if (!strcmp(selected,"nebula") && !json_object_object_get_ex(status,"nebula",&space)) { + json_object_put(status);return; + } + bool backend_changed=!L.ambient_theme || strcmp(L.background,selected); + if (backend_changed && L.scenes) { json_object_put(L.scenes);L.scenes=NULL; } + bool changed=backend_changed || + !json_object_equal(L.theme,theme) || !json_object_equal(L.space_settings,space) || + !json_object_equal(L.matrix_settings,matrix); + if (changed) { + json_object_get(theme);json_object_get(space);json_object_get(matrix); + if (L.theme) json_object_put(L.theme); + if (L.space_settings) json_object_put(L.space_settings); + if (L.matrix_settings) json_object_put(L.matrix_settings); + L.theme=theme;L.space_settings=space;L.matrix_settings=matrix; + snprintf(L.background,sizeof(L.background),"%s",selected); + L.ambient_theme=true;++L.theme_revision;lock_changed(); + } + if (!L.scenes) { + struct json_object *scenes=NULL; + if (json_object_object_get_ex(status,!strcmp(selected,"nebula") ? "nebula_scenes" : "scenes",&scenes)) L.scenes=json_object_get(scenes); + } + json_object_put(status); +} +static dr_config_packet rain_appearance(void) { + dr_config_packet packet=L.appearance; + if (!L.ambient_theme) return packet; + packet.magic=DR_LIVE_MAGIC;packet.kind=DR_CONFIG; + packet.revision=0x80000000u+(L.theme_revision%0x7fffffffu); + packet.config=G.config; + json_object_object_foreach(L.matrix_settings,key,value) { + char full[128];snprintf(full,sizeof(full),"matrix.%s",key); + const struct api_setting *field=cf_setting(full); + if (field) cf_set(&packet.config,field,value,false); + } + // WCM permits changing related sliders in either order. + dr_config_t *c=&packet.config; + c->matrix_max_speed=fmax(c->matrix_min_speed,c->matrix_max_speed); + c->matrix_avg_speed=bounded(c->matrix_avg_speed,c->matrix_min_speed,c->matrix_max_speed); + c->matrix_max_trail=fmax(c->matrix_min_trail,c->matrix_max_trail); + c->matrix_max_density=fmax(c->matrix_min_density,c->matrix_max_density); + c->matrix_head_white_max=fmax(c->matrix_head_white_min,c->matrix_head_white_max); + struct json_object *monitor=NULL; + json_object_object_get_ex(L.matrix_settings,"MONITOR_COLOURS",&monitor); + if (!json_object_get_boolean(monitor) || theme_bool("colour_override")) + for (int i=0;i<c->output_count;i++) c->outputs[i].color[0]=0; + if (theme_bool("colour_override")) { + float r=0,g=0,b=0,a=1;const char *text=json_object_get_string(theme_value("colour")); + bool valid=text && sscanf(text,"%f %f %f %f",&r,&g,&b,&a)>=3; + if (text && text[0]=='#') { rgb_t colour=render_parse_hex(text);r=colour.r;g=colour.g;b=colour.b;valid=true; } + if (valid) + snprintf(c->matrix_color,sizeof(c->matrix_color),"#%02x%02x%02x", + (unsigned)(bounded(r,0,1)*255),(unsigned)(bounded(g,0,1)*255),(unsigned)(bounded(b,0,1)*255)); + } + return packet; +} +static struct json_object *space_appearance(struct lock_output *o,bool initial) { + struct json_object *settings=NULL; + json_object_deep_copy(L.space_settings,&settings,NULL); + if (!settings) return NULL; + bool nebula=!strcmp(L.background,"nebula"); + json_object_object_add(settings,"_backend",json_object_new_string(nebula ? "nebula" : "hyperspace")); + struct json_object *speed=NULL; + json_object_object_get_ex(settings,nebula ? "nebula_speed" : "hyperspace_speed",&speed); + json_object_object_add(settings,nebula ? "nebula_speed" : "hyperspace_speed",json_object_new_double( + bounded(json_object_get_double(speed)*theme_number("speed",1,0,3),0,5))); + json_object_object_add(settings,"hyperspace_surge",json_object_new_double(0)); + if (theme_bool("colour_override")) { + json_object_object_add(settings,nebula ? "nebula_color" : "hyperspace_color",json_object_get(theme_value("colour"))); + json_object_object_add(settings,nebula ? "nebula_cloud_colours" : "hyperspace_cloud_colours",json_object_new_double(0)); + } + struct json_object *scene=NULL; + (void)initial; + if (L.scenes && json_object_object_get_ex(L.scenes,o->name,&scene)) + json_object_object_add(settings,"_scene",json_object_get(scene)); + return settings; +} +static bool initialize_output(struct lock_output *o) { + S = o->render; + S.display = L.display; + S.compositor = L.compositor; + S.font_size = bounded(G.config.matrix_font_size, 6, 72); + S.num_depth_layers = bounded(G.config.matrix_depth_layers, 1, 8); + S.min_speed = bounded(G.config.matrix_min_speed, .001, 100); + S.max_speed = bounded(G.config.matrix_max_speed, S.min_speed, 100); + S.avg_speed = bounded(G.config.matrix_avg_speed, S.min_speed, S.max_speed); + S.min_trail = bounded(G.config.matrix_min_trail, 1, 1000); + S.max_trail = bounded(G.config.matrix_max_trail, S.min_trail, 1000); + S.min_density = bounded(G.config.matrix_min_density, 0, 1); + S.max_density = bounded(G.config.matrix_max_density, S.min_density, 1); + S.layer_near_density = bounded(G.config.matrix_layer_near_density, 0, 2); + S.layer_far_density = bounded(G.config.matrix_layer_far_density, 0, 2); + S.head_hold_frames = bounded(G.config.matrix_head_hold_frames, 0, 120); + S.head_mutation_chance = bounded(G.config.matrix_head_mutation_chance, 0, 1); + S.head_white_min = bounded(G.config.matrix_head_white_min, 0, 1); + S.head_white_max = bounded(G.config.matrix_head_white_max, S.head_white_min, 1); + S.base_color = (rgb_t){0, 1, 0}; + S.fade_opacity = 1; + if (G.config.matrix_color[0] == '#' || strlen(G.config.matrix_color) == 6) + S.base_color = render_parse_hex(G.config.matrix_color); + for (size_t i = 0; i < RENDER_NUM_COLORS; i++) + if (!strcmp(G.config.matrix_color, RENDER_COLOR_TABLE[i].name)) + S.base_color = RENDER_COLOR_TABLE[i].color; + if (!strcmp(G.config.matrix_color, "random_trail_named")) + S.color_mode = 1; + if (!strcmp(G.config.matrix_color, "random_trail_hex")) + S.color_mode = 2; + /* Adaptive lock rain uses its black-background palette, never desktop capture. */ + if (is_adaptive_color(G.config.matrix_color)) { + snprintf(S.target_name, sizeof(S.target_name), "%s", o->name); + for (int i = 0; i < L.target_count; i++) + if (!strcmp(L.targets[i].desktop, o->name)) { + snprintf(S.target_name, sizeof(S.target_name), "%s", L.targets[i].name); + break; + } + S.adaptive_black_color = S.base_color; + render_load_adaptive_snapshot(); + S.base_color = S.adaptive_black_color; + } + if (!render_init_egl() || !render_init_shaders()) + return false; + render_set_projection(S.surface_width, S.surface_height); + int font = fmax(18, S.font_size * LAYER_NEAR_FONT_SCALE); + if (!render_init_freetype(font) || !render_build_atlas(font)) + return false; + render_init_matrix(S.surface_width, S.surface_height); + o->render = S; + o->composite=ambient_lock_composite_create(); + o->ready = true; + o->dirty = false; + return true; +} +static void text_line(const char *text, int cx, int y, float scale) { + int advance = S.atlas.cell_w; + float x = cx - (strlen(text) * advance * scale) / 2; + for (const unsigned char *p = (const unsigned char *)text; *p; p++, x += advance * scale) { + if (*p == ' ') + continue; + const glyph_info_t *g = render_find_glyph(*p); + if (!g) + continue; + float left = x + g->bearing_x * scale, top = y - g->bearing_y * scale; + render_push_quad(left, top, left + g->width * scale, top + g->height * scale, g->u0, g->v0, + g->u1, g->v1, 1, 1, 1, 1); + } + render_flush_quads(); +} +static void prompt_at(int x, int y, int width, int height) { + int cx = x + width / 2, cy = y + height / 2; + if (theme_bool("show_clock")) cy += fmin(height*.15,80); + int boxw = fmin(width, 650), boxh = fmin(height*.5, 180); + glEnable(GL_SCISSOR_TEST); + glScissor(cx - boxw / 2, S.surface_height - (cy + boxh / 2), boxw, boxh); + glClearColor(0, 0, 0, .85); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glUseProgram(S.shader_program); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, S.atlas.texture); + glUniform1i(S.u_atlas, 0); + float scale = fmin(1.0, (float)width / 650); + text_line("Locked", cx, cy - 45*scale, scale); + char masked[64] = "Password: "; + size_t count = 0; + for (size_t i = 0; i < L.password_len; i++) + if (((unsigned char)L.password[i] & 0xc0) != 0x80) + count++; + memset(masked + 10, '*', count < 32 ? count : 32); + masked[10 + (count < 32 ? count : 32)] = 0; + text_line(masked, cx, cy, scale); + text_line(L.message[0] ? L.message : (L.caps ? "Caps Lock is on" : "Enter to unlock"), cx, + cy + 40*scale, scale); +} +static void clock_at(int x,int y,int width,int height) { + if (!theme_bool("show_clock")) return; + glEnable(GL_BLEND);glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);glUseProgram(S.shader_program); + glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D,S.atlas.texture);glUniform1i(S.u_atlas,0); + time_t now=time(NULL);struct tm local;localtime_r(&now,&local); + char clock[80];strftime(clock,sizeof(clock),"%H:%M",&local); + int cy=y+height/2-((L.prompt || theme_bool("ui_on_interaction")) ? fmin(height/3,180) : 20),cx=x+width/2; + float scale=fmin(1.8,(float)width/400); + text_line(clock,cx,cy,scale); + if (theme_bool("show_date")) { + strftime(clock,sizeof(clock),"%a, %b %d",&local);text_line(clock,cx,cy+40*scale,.65*scale); + } +} +static bool draw_output(struct lock_output *o, bool animate) { + if (!o->render.configured) + return true; + if (!o->ready && !initialize_output(o)) + return false; + S = o->render; + if (!eglMakeCurrent(S.egl_display, S.egl_surface, S.egl_surface, S.egl_context)) + return false; + dr_config_packet appearance=rain_appearance(); + if (appearance.revision && appearance.revision != S.attempted_revision) { + unsigned before = S.applied_revision; + render_apply_config(&appearance, true); + if (before != S.applied_revision || S.live_error) ++L.generation; + } + dr_effect_packet effects = {.magic=DR_LIVE_MAGIC, .kind=DR_EFFECTS}; + for (unsigned i = 0; i < L.effects.count; i++) for (int j = 0; j < L.target_count; j++) + if (!strcmp(L.effects.effects[i].output, L.targets[j].name) && !strcmp(L.targets[j].desktop, o->name)) { + if (L.effects.effects[i].until > render_monotime() || L.effects.effects[i].audio_time > 0) effects.effects[effects.count++] = L.effects.effects[i]; + break; + } + render_apply_effects(&effects); + if (o->dirty) { + wl_egl_window_resize(S.egl_window, S.surface_width, S.surface_height, 0, 0); + render_set_projection(S.surface_width, S.surface_height); + render_init_matrix(S.surface_width, S.surface_height); + } + glViewport(0, 0, S.surface_width, S.surface_height); + if (!ambient_lock_composite_begin(o->composite,S.surface_width,S.surface_height)) return false; + glDisable(GL_SCISSOR_TEST); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + if (animate && L.ambient_theme && (!strcmp(L.background,"hyperspace") || !strcmp(L.background,"nebula"))) { + struct json_object *settings=space_appearance(o,!o->space); + if (!settings) return false; + if (!o->space) o->space=ambient_space_create(settings); + bool ok=o->space && ambient_space_update(o->space,settings) && + ambient_space_draw(o->space,S.surface_width,S.surface_height,theme_number("speed",1,0,3)>0); + json_object_put(settings);if (!ok) return false; + } else if (animate && (!L.ambient_theme || strcmp(L.background,"black"))) { + render_simulation_speed=theme_number("speed",1,0,3); + if (render_simulation_speed>0) render_tick_matrix(); + render_frame(); + } + float opacity=L.ambient_theme ? L.opacity*theme_number("brightness",.65,0,2) : 1; + if (!ambient_lock_composite_end(o->composite,S.surface_width,S.surface_height,opacity)) return false; + if (L.ui_opacity > 0) { + if (!ambient_lock_composite_begin(o->composite,S.surface_width,S.surface_height)) return false; + glDisable(GL_SCISSOR_TEST);glClearColor(0,0,0,0);glClear(GL_COLOR_BUFFER_BIT); + bool known = false; + for (int i = 0; i < L.target_count; i++) + if (!strcmp(L.targets[i].desktop, o->name)) { + saver_target_t *t = &L.targets[i]; + if (t->x + t->width <= S.surface_width && t->y + t->height <= S.surface_height) { + known = true; + if (!controls_on(t->name,t->desktop)) continue; + clock_at(t->x,t->y,t->width,t->height); + if (L.prompt || theme_bool("ui_on_interaction")) prompt_at(t->x,t->y,t->width,t->height); + } + } + if (!known && controls_on(o->name,o->name)) { + clock_at(0,0,S.surface_width,S.surface_height); + if (L.prompt || theme_bool("ui_on_interaction")) prompt_at(0,0,S.surface_width,S.surface_height); + } + if (!ambient_lock_composite_overlay(o->composite,S.surface_width,S.surface_height,L.ui_opacity)) return false; + } + bool ok = eglSwapBuffers(S.egl_display, S.egl_surface); + o->render = S; + o->dirty = false; + return ok; +} + +int main(int argc, char **argv) { + if (argc > 1) { + if (!strcmp(argv[1], "--help")) { + puts("digitalrain-lock: native Wayland/PAM lock screen; no password arguments"); + return 0; + } + return 2; + } + struct rlimit core = {0, 0}; + setrlimit(RLIMIT_CORE, &core); + prctl(PR_SET_DUMPABLE, 0); + mlock(L.password, sizeof(L.password)); + signal(SIGPIPE, SIG_IGN); + signal(SIGTERM, stop_lock); + signal(SIGINT, stop_lock); + /* Saver signals cannot unlock or dismiss a locker. */ + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); + for (int i = 0; i < DR_CLIENTS; i++) + L.clients[i] = -1; + for (int i = 0; i < MAX_OUTPUTS; i++) + L.power_fds[i] = -1; + L.auth_fd = -1; + char path[PATH_MAX]; + if (dr_path(path, sizeof(path), "locker.instance")) + return 1; + int instance = open(path, O_CREAT | O_RDWR | O_CLOEXEC | O_NOFOLLOW, 0600); + if (instance < 0 || flock(instance, LOCK_EX | LOCK_NB)) + return 1; + L.listener = dr_socket("lock.sock", true); + if (L.listener < 0) + return 1; + pthread_mutex_init(&G.lock, NULL); + mgr_set_defaults(); + if (!mgr_load_config()) { fprintf(stderr, "%s\n", C.error); return 1; } + L.power_config = G.config; + L.opacity=1; + refresh_appearance(); + srand(time(NULL) ^ getpid()); + L.xkb = xkb_context_new(0); + if (!L.xkb) + return 1; + const char *locale = setlocale(LC_CTYPE, ""); + L.compose_table = xkb_compose_table_new_from_locale(L.xkb, locale ? locale : "C", 0); + if (L.compose_table) + L.compose = xkb_compose_state_new(L.compose_table, 0); + struct json_object *topology = mgr_ipc_request("window-rules/list-outputs", "{}"); + L.target_count = parse_targets(topology, L.targets); + if (topology) + json_object_put(topology); + L.display = wl_display_connect(NULL); + if (!L.display) + return 1; + struct wl_registry *registry = wl_display_get_registry(L.display); + wl_registry_add_listener(registry, ®istry_listener, NULL); + if (wl_display_roundtrip(L.display) < 0 || !L.compositor || !L.manager || L.failed) { + fprintf(stderr, "Digital Rain: session-lock protocol unavailable\n"); + return 1; + } + L.lock = ext_session_lock_manager_v1_lock(L.manager); + ext_session_lock_v1_add_listener(L.lock, &lock_listener, NULL); + for (int i = 0; i < LOCK_OUTPUTS; i++) + if (L.outputs[i].output) + create_surface(&L.outputs[i]); + L.started = L.last_input = render_monotime(); + L.fade_updated=L.started; + L.dirty = true; + unsigned broadcast = 0; + bool unlocked = false; + while (!lock_stop && !L.finished && !L.failed) { + if (wl_display_dispatch_pending(L.display) < 0) + break; + if (L.topology_dirty) { + restore_monitors(); + struct json_object *o = mgr_ipc_request("window-rules/list-outputs", "{}"); + int n = parse_targets(o, L.targets); + if (o) + json_object_put(o); + if (n >= 0) { + L.target_count = n; + G.output_count = n; + for (int i = 0; i < n; i++) + mgr_init_output(&G.outputs[i], L.targets[i].name, render_monotime()); + G.config_mtime = 0; + mgr_load_config(); + } + L.topology_dirty = false; + } + double now = render_monotime(); + if (now>=L.settings_due) refresh_appearance(); + double target=1; + if (L.ambient_theme) { + double delay=theme_number("black_delay",600,0,86400); + if (delay>0 && now-L.started>=delay) target=0; + double step=fmax(0,now-L.fade_updated)/theme_number("fade_duration",2,.1,30); + double old=L.opacity; + L.opacity=target>old ? fmin(target,old+step) : fmax(target,old-step); + bool black=L.opacity<=0; + if (L.black!=black) { L.black=black;lock_changed(); } + if (old!=L.opacity) L.dirty=true; + } else if (!L.black && G.config.matrix_black_rain_timeout > 0 && + now - L.started >= G.config.matrix_black_rain_timeout) { + L.black = true; + lock_changed(); + } + double hide_delay=theme_number("ui_hide_delay",15,0,600); + if (L.prompt && !L.auth_pid && hide_delay>0 && now - L.last_input >= hide_delay) { + L.prompt = false; + erase_password(); + L.message[0] = 0; + lock_changed(); + } + double ui_target=L.prompt || (L.ambient_theme && !L.black && !theme_bool("ui_on_interaction")) ? 1 : 0; + double old_ui=L.ui_opacity; + double ui_step=fmax(0,now-L.fade_updated)/theme_number("ui_fade_duration",.25,.05,3); + L.ui_opacity=L.ambient_theme ? (ui_target>old_ui ? fmin(ui_target,old_ui+ui_step) : fmax(ui_target,old_ui-ui_step)) : ui_target; + if (old_ui!=L.ui_opacity) { + L.dirty=true; + if (old_ui==0 || L.ui_opacity==0 || L.ui_opacity==1) lock_changed(); + } + L.fade_updated=now; + time_t minute=time(NULL)/60; + if (minute!=L.clock_minute) { L.clock_minute=minute;if (L.ui_opacity>0 && theme_bool("show_clock")) L.dirty=true; } + bool moving=!L.ambient_theme || (strcmp(L.background,"black") && theme_number("speed",1,0,3)>0); + bool fading=L.ambient_theme && L.opacity!=target; + bool ui_fading=L.ui_opacity!=ui_target; + bool frame = ((!L.black && (moving || fading)) || ui_fading) && now >= L.next_frame; + for (int i = 0; i < LOCK_OUTPUTS; i++) + if (L.outputs[i].output && (frame || L.dirty || L.outputs[i].dirty)) + if (!draw_output(&L.outputs[i], !L.black)) + L.failed = true; + if (frame) + L.next_frame = now + 1.0 / TARGET_FPS; + L.dirty = false; + power_monitors(); + if (broadcast != L.generation) { + struct json_object *o = lock_status(); + for (int i = 0; i < DR_CLIENTS; i++) + if (L.clients[i] >= 0 && L.watching[i] && !dr_send(L.clients[i], o)) { + close(L.clients[i]); + L.clients[i] = -1; + } + json_object_put(o); + broadcast = L.generation; + } + if (L.failed) + break; + if (wl_display_prepare_read(L.display)) + continue; + struct pollfd fds[3 + DR_CLIENTS] = {{.fd = wl_display_get_fd(L.display), .events = POLLIN}, + {.fd = L.listener, .events = POLLIN}, + {.fd = L.auth_fd, .events = POLLIN}}; + for (int i = 0; i < DR_CLIENTS; i++) + fds[3 + i] = (struct pollfd){.fd = L.clients[i], .events = POLLIN}; + if (wl_display_flush(L.display) < 0) { + if (errno == EAGAIN) + fds[0].events |= POLLOUT; + else { + wl_display_cancel_read(L.display); + break; + } + } + double deadline = ((!L.black && (moving || fading)) || ui_fading) ? L.next_frame : now+1; + deadline=fmin(deadline,L.settings_due); + if (L.prompt && !L.auth_pid && hide_delay>0) + deadline = fmin(deadline, L.last_input + hide_delay); + if (L.auth_pid) + deadline = fmin(deadline, now + .05); + int timeout = (int)fmax(0, ceil((deadline - render_monotime()) * 1000)); + int rc = poll(fds, 3 + DR_CLIENTS, timeout); + if (rc > 0 && (fds[0].revents & POLLIN)) { + if (wl_display_read_events(L.display) < 0) + break; + } else + wl_display_cancel_read(L.display); + if (rc < 0 && errno != EINTR) + break; + if (fds[0].revents & (POLLHUP | POLLERR | POLLNVAL)) + break; + if (fds[1].revents & POLLIN) { + int fd = dr_accept(L.listener), slot = -1; + for (int i = 0; i < DR_CLIENTS; i++) + if (L.clients[i] < 0) { + slot = i; + break; + } + if (fd >= 0) { + if (slot >= 0) { + L.clients[slot] = fd; + L.watching[slot] = false; + } else + close(fd); + } + } + for (int i = 0; i < DR_CLIENTS; i++) + if (L.clients[i] >= 0 && fds[3 + i].revents) { + uint32_t magic = 0; + if (recv(L.clients[i], &magic, sizeof(magic), MSG_PEEK | MSG_DONTWAIT) == sizeof(magic) && magic == DR_LIVE_MAGIC) { + union { dr_config_packet config; dr_effect_packet effects; } packet; + ssize_t n = recv(L.clients[i], &packet, sizeof(packet), MSG_DONTWAIT | MSG_TRUNC); + if (n == sizeof(dr_config_packet) && dr_config_valid(&packet.config)) { + L.appearance = packet.config; + G.config = packet.config.config; + ++L.theme_revision; + mgr_apply_output_config(); + ++L.generation; + if (!L.black || L.prompt) L.dirty = true; + } else if (n == sizeof(dr_effect_packet) && dr_effect_valid(&packet.effects)) L.effects = packet.effects; + continue; + } + struct json_object *req = dr_receive(L.clients[i]); + const char *method = dr_string(req, "method"); + struct json_object *reply = NULL; + if (method && (!strcmp(method, "status") || !strcmp(method, "watch") || + !strcmp(method, "wake"))) { + if (!strcmp(method, "watch")) + L.watching[i] = true; + if (!strcmp(method, "wake")) + input_activity(); + reply = lock_status(); + } else + reply = dr_error("unsupported locker operation"); + if (!req || !dr_send(L.clients[i], reply)) { + close(L.clients[i]); + L.clients[i] = -1; + } + if (req) + json_object_put(req); + json_object_put(reply); + } + if (L.auth_pid && finish_auth()) { + unlocked = true; + break; + } + } + erase_password(); + restore_monitors(); + if (L.auth_pid) { + kill(L.auth_pid, SIGKILL); + waitpid(L.auth_pid, NULL, 0); + } + /* Disconnecting without authenticated unlock leaves compositor protection. */ + wl_display_disconnect(L.display); + if (!dr_path(path, sizeof(path), "lock.sock")) + unlink(path); + close(L.listener); + close(instance); + return unlocked ? 0 : 1; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-mcp.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-mcp.py new file mode 100755 index 000000000000..2a91fef37076 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-mcp.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Optional stdio MCP adapter. No daemon, network listener, or password channel.""" +import json +import os +import socket +import sys +from pathlib import Path + +MAX_PACKET = 16384 +VERSIONS = ('2025-11-25', '2025-06-18', '2025-03-26', '2024-11-05') +OUTPUT = {'type': 'string', 'description': 'Physical connector name; omit for all monitors.'} +SETTINGS = {'type': 'object', 'description': 'section.KEY values validated against digitalrain://schema.'} +ALERT_ID = {'type': 'string', 'minLength': 1, 'maxLength': 63} +TOOLS = { + 'reload': ('Validate and reload the selected TOML configuration.', {}, []), + 'alert': ('Set or renew a temporary visual alert without activating rain or waking black screens.', {'source': ALERT_ID, 'id': ALERT_ID, 'severity': {'type': 'string', 'enum': ['info', 'success', 'warning', 'error']}, 'seconds': {'type': 'number', 'minimum': 1, 'maximum': 86400}, 'output': OUTPUT, 'message': {'type': 'string', 'maxLength': 127}}, ['source', 'id', 'severity', 'seconds']), + 'clear_alert': ('Clear an alert by producer and ID; safe to repeat.', {'source': ALERT_ID, 'id': ALERT_ID}, ['source', 'id']), + 'list_alerts': ('List active alerts in bounded pages.', {'offset': {'type': 'integer', 'minimum': 0, 'maximum': 64}, 'limit': {'type': 'integer', 'minimum': 1, 'maximum': 16}}, []), + 'status': ('Read monitor phases, focus, deadlines, inhibitors and lock state.', {}, []), + 'explain': ('Explain current monitor behavior.', {}, []), + 'activate': ('Start rain on the selected monitors.', {'output': OUTPUT}, []), + 'black': ('Cover selected monitors with solid black.', {'output': OUTPUT}, []), + 'wake': ('Wake monitors or show the lock prompt; never unlocks the session.', {'output': OUTPUT}, []), + 'lock': ('Request a session-wide native Digital Rain lock; query lock_status for confirmation.', {}, []), + 'lock_status': ('Read confirmed or pending lock state.', {}, []), + 'inhibit': ('Temporarily inhibit saver activation.', {'output': OUTPUT, 'seconds': {'type': 'number', 'exclusiveMinimum': 0, 'maximum': 86400}, 'reason': {'type': 'string', 'maxLength': 127}}, ['seconds', 'reason']), + 'uninhibit': ('Release a timed inhibitor.', {'id': {'type': 'integer', 'minimum': 1}}, ['id']), + 'preview': ('Preview validated settings temporarily; does not save configuration.', {'output': OUTPUT, 'settings': SETTINGS, 'seconds': {'type': 'number', 'exclusiveMinimum': 0, 'maximum': 600}}, ['settings']), + 'settings': ('Apply validated settings; save=true also writes the selected user-owned config.', {'settings': SETTINGS, 'save': {'type': 'boolean', 'default': False}}, ['settings']), +} + + +def request(method, arguments=None): + base = Path(os.environ['XDG_RUNTIME_DIR']) + st = base.lstat() + if not base.is_dir() or base.is_symlink() or st.st_uid != os.getuid() or st.st_mode & 0o077: + raise ValueError('Private user runtime directory required') + payload = json.dumps({'version': 1, 'method': method, **(arguments or {})}, allow_nan=False).encode() + if len(payload) > MAX_PACKET: + raise ValueError('Request too large') + with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as conn: + conn.settimeout(10) + conn.connect(str(base / 'digitalrain/control.sock')) + conn.sendall(payload) + raw, _, flags, _ = conn.recvmsg(MAX_PACKET) + if flags & socket.MSG_TRUNC: + raise ValueError('Oversized manager reply') + return json.loads(raw) + + +def handle(method, params): + if method == 'initialize': + offered = params.get('protocolVersion') + return {'protocolVersion': offered if offered in VERSIONS else VERSIONS[0], + 'serverInfo': {'name': 'digitalrain', 'version': '1.2.0'}, + 'capabilities': {'tools': {}, 'resources': {}}} + if method == 'ping': + return {} + if method == 'tools/list': + return {'tools': [{'name': name, 'description': description, + 'inputSchema': {'type': 'object', 'properties': properties, 'required': required, 'additionalProperties': False}, + 'annotations': {'readOnlyHint': name in ('status', 'explain', 'lock_status', 'list_alerts'), 'openWorldHint': False}} + for name, (description, properties, required) in TOOLS.items()]} + if method == 'tools/call': + name, arguments = params.get('name'), params.get('arguments', {}) + if name not in TOOLS or not isinstance(arguments, dict): + raise ValueError('Unknown tool or invalid arguments') + _, properties, required = TOOLS[name] + if set(arguments) - set(properties) or set(required) - set(arguments): + raise ValueError('Unknown or missing arguments') + try: + result = request(name, arguments) + except (OSError, ValueError, KeyError) as error: + result = {'error': str(error)} + return {'content': [{'type': 'text', 'text': json.dumps(result)}], 'isError': 'error' in result} + if method == 'resources/list': + return {'resources': [{'uri': f'digitalrain://{name}', 'name': name, 'mimeType': 'application/json'} for name in ('status', 'schema')]} + if method == 'resources/read': + uri = params.get('uri') + if uri not in ('digitalrain://status', 'digitalrain://schema'): + raise ValueError('Unknown resource') + return {'contents': [{'uri': uri, 'mimeType': 'application/json', 'text': json.dumps(request(uri.split('://')[1]))}]} + raise LookupError('Method not found') + + +def main(): + initialized = False + while True: + line = sys.stdin.buffer.readline(65537) + if not line: + return + if len(line) > 65536: + return # terminate an oversized stream instead of buffering it + ident = None + try: + msg = json.loads(line) + if not isinstance(msg, dict) or msg.get('jsonrpc') != '2.0' or not isinstance(msg.get('method'), str): + raise ValueError('Invalid JSON-RPC request') + if 'id' not in msg: + continue + ident = msg['id'] + params = msg.get('params', {}) + if not isinstance(params, dict): + raise ValueError('params must be an object') + if not initialized and msg['method'] not in ('initialize', 'ping'): + raise ValueError('Initialize first') + result = handle(msg['method'], params) + initialized |= msg['method'] == 'initialize' + response = {'jsonrpc': '2.0', 'id': ident, 'result': result} + except (ValueError, LookupError, OSError) as error: + response = {'jsonrpc': '2.0', 'id': ident, 'error': {'code': -32601 if isinstance(error, LookupError) else -32602, 'message': str(error)}} + print(json.dumps(response, separators=(',', ':'), allow_nan=False), flush=True) + + +if __name__ == '__main__': + main() diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-power.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-power.cpp new file mode 100644 index 000000000000..bf1073617bdf --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-power.cpp @@ -0,0 +1,110 @@ +#include <map> +#include <memory> +#include <wayfire/core.hpp> +#include <wayfire/output.hpp> +#include <wayfire/output-layout.hpp> +#include <wayfire/output-stitch.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/util.hpp> +#include <wayfire/nonstd/wlroots-full.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> + +namespace +{ +/* Power belongs to the renderer's IPC connection. A crash, disconnect or + * plugin unload releases it, and multiple clients cannot wake each other's + * monitor. Track the actual connector so hotplug cannot restore a replacement. */ +class digitalrain_power_plugin_t : public wf::plugin_interface_t +{ + using client_t = wf::ipc::client_interface_t; + struct lease_t + { + std::string name; + wlr_output *handle; + wf::wl_listener_wrapper destroyed; + lease_t(std::string name, wlr_output *handle) : name(name), handle(handle) + { + destroyed.set_callback([this] (void*) + { + this->handle = nullptr; + destroyed.disconnect(); + }); + destroyed.connect(&handle->events.destroy); + } + }; + wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; + std::map<client_t*, std::unique_ptr<lease_t>> leases; + + bool owned(wlr_output *handle) const + { + for (auto& [client, lease] : leases) + if (lease->handle == handle) return true; + return false; + } + + void restore(client_t *client) + { + auto it = leases.find(client); + if (it == leases.end()) return; + auto lease = std::move(it->second); + leases.erase(it); + if (lease->handle && !owned(lease->handle)) + wf::get_core().output_layout->set_output_power(lease->name, true); + } + + wf::signal::connection_t<wf::ipc::client_disconnected_signal> disconnected = + [this] (auto ev) { restore(ev->client); }; + + public: + void init() override + { + repo->connect(&disconnected); + repo->register_method("digitalrain/power_on", [this] (wf::json_t, client_t *client) + { + restore(client); + return wf::ipc::json_ok(); + }); + repo->register_method("digitalrain/power_off", [this] (wf::json_t data, client_t *client) + { + if (!client || !data.has_member("output") || !data["output"].is_string()) + return wf::ipc::json_error("An IPC connection and physical output name are required"); + auto name = data["output"].as_string(); + auto layout = wf::get_core().output_layout.get(); + wlr_output *handle = nullptr; + for (auto& [head, state] : layout->get_current_configuration()) + if (name == head->name) handle = head; + if (!handle) return wf::ipc::json_error("Output not found"); + auto output = layout->find_output(handle); + if (output) if (auto group=output->get_data<wf::output_stitch_layout_t>()) + { + // The first physical head can be the backing output for a + // stitched desktop. It still has its own power policy. + bool physical=false; + for (const auto& zone:group->zones) physical |= zone.name==name; + if (!physical) + return wf::ipc::json_error("Select a physical monitor, not the combined desktop"); + } + if (leases.count(client) && leases.at(client)->handle == handle) + return wf::ipc::json_ok(); + bool already_owned = owned(handle); + if (!already_owned && !handle->enabled) + return wf::ipc::json_error("Output is already powered off by another owner"); + restore(client); + if (!already_owned && !layout->set_output_power(name, false)) + return wf::ipc::json_error("Output power control failed"); + leases[client] = std::make_unique<lease_t>(name, handle); + return wf::ipc::json_ok(); + }); + } + + void fini() override + { + repo->unregister_method("digitalrain/power_off"); + repo->unregister_method("digitalrain/power_on"); + disconnected.disconnect(); + while (!leases.empty()) restore(leases.begin()->first); + } +}; +} +DECLARE_WAYFIRE_PLUGIN(digitalrain_power_plugin_t); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-power.xml b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-power.xml new file mode 100644 index 000000000000..dd28c0c51111 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-power.xml @@ -0,0 +1,8 @@ +<?xml version='1.0' encoding='utf-8'?> +<wayfire> + <plugin name="digitalrain-power"> + <_short>Monitor power control</_short> + <_long>Optional per-monitor power control after idle black transitions.</_long> + <category>Utility</category> + </plugin> +</wayfire> diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-sampler.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-sampler.cpp new file mode 100644 index 000000000000..832a08f1abb1 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-sampler.cpp @@ -0,0 +1,162 @@ +#include "digitalrain-targets.hpp" +#include <algorithm> +#include <array> +#include <chrono> +#include <cmath> +#include <map> +#include <set> +#include <string> +#include <drm_fourcc.h> +#include <wayfire/core.hpp> +#include <wayfire/output-layout.hpp> +#include <wayfire/output.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/render-manager.hpp> +#include <wayfire/nonstd/wlroots-full.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> + +/* A small on-demand summary of the actual scene below SCREENSAVER. No + * screenshot protocol, wallpaper paths, pixel grids or continuous render hook. */ +namespace { +class digitalrain_sampler_plugin_t : public wf::plugin_interface_t +{ + using clock = std::chrono::steady_clock; + wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; + struct sample_t + { + clock::time_point when; + wf::geometry_t geometry; + double luminance; + }; + std::map<std::string, sample_t> cache; + + static double linear(double c) + { + return c <= 0.04045 ? c / 12.92 : std::pow((c + 0.055) / 1.055, 2.4); + } + + wf::json_t sample() + { + wf::json_t response; + response["version"] = 1; + response["outputs"] = wf::json_t{}; + std::set<std::string> present; + for (auto output : wf::get_core().output_layout->get_outputs()) + { + wf::auxilliary_buffer_t desktop; + bool composed = false; + for (const auto& target_region : digitalrain_targets(output)) { + const std::string name = target_region.name; + present.insert(name); + wf::json_t result; + const auto geometry = target_region.geometry; + const auto now = clock::now(); + if (output->is_inhibited() || geometry.width <= 0 || geometry.height <= 0) + { + cache.erase(name); + result["error"] = "output unavailable or locked"; + response["outputs"][name] = result; + continue; + } + + auto cached = cache.find(name); + if (cached != cache.end() && cached->second.geometry == geometry && + now - cached->second.when < std::chrono::seconds(1)) + { + result["luminance"] = cached->second.luminance; + result["cached"] = true; + response["outputs"][name] = result; + continue; + } + + const double scale = 64.0 / std::max(geometry.width, geometry.height); + const int width = std::max(1, int(std::ceil(geometry.width * scale))); + const int height = std::max(1, int(std::ceil(geometry.height * scale))); + wf::auxilliary_buffer_t buffer; + // Custom scene effects can use output pixel sizes internally. Compose + // at the output's native size, then downsample on the GPU; only the + // tiny summary is read back. Both buffers are released after sampling. + const wf::dimensions_t native{output->handle->width, output->handle->height}; + if (!composed && desktop.allocate(native) != wf::buffer_reallocation_result_t::FAILED) { + wf::render_target_t target{desktop}; + target.geometry = output->get_layout_geometry(); + target.scale = output->handle->scale; + target.wl_transform = output->handle->transform; + composed = output->render->render_desktop(target); + } + bool ok = composed && buffer.allocate({width, height}) != wf::buffer_reallocation_result_t::FAILED; + if (ok) { + // Stitched members are untransformed at scale 1. Ordinary outputs + // retain the original full-buffer sampling for all transforms. + auto source = output->get_data<wf::output_stitch_layout_t>() ? geometry : + wf::geometry_t{0, 0, double(native.width), double(native.height)}; + buffer.get_renderbuffer().blit(desktop, source, {0, 0, double(width), double(height)}); + } + + // ABGR8888 is RGBA byte order on little-endian systems. Reading words + // keeps the channel extraction independent of host byte order. + std::array<uint32_t, 64 * 64> pixels{}; + auto texture = ok ? buffer.get_texture() : nullptr; + if (texture) + { + wlr_texture_read_pixels_options options{}; + options.data = pixels.data(); + options.format = DRM_FORMAT_ABGR8888; + options.stride = width * sizeof(uint32_t); + ok = wlr_texture_read_pixels(texture, &options); + } else + { + ok = false; + } + + if (ok) + { + double luminance = 0.0; + for (int i = 0; i < width * height; ++i) + { + const auto pixel = pixels[i]; + luminance += 0.2126 * linear((pixel & 255) / 255.0) + + 0.7152 * linear(((pixel >> 8) & 255) / 255.0) + + 0.0722 * linear(((pixel >> 16) & 255) / 255.0); + } + + luminance /= width * height; + cache[name] = {now, geometry, luminance}; + result["luminance"] = luminance; + result["cached"] = false; + result["width"] = width; + result["height"] = height; + } else + { + cache.erase(name); + result["error"] = "desktop sample failed"; + } + + response["outputs"][name] = result; + } + } + + for (auto it = cache.begin(); it != cache.end();) + { + if (!present.count(it->first)) { it = cache.erase(it); } else { ++it; } + } + + return response; + } + + public: + void init() override + { + repo->register_method("digitalrain/sample_desktop", [this] (wf::json_t) { return sample(); }); + } + + void fini() override + { + repo->unregister_method("digitalrain/sample_desktop"); + cache.clear(); + } +}; + +} // Keep implementation symbols local when an old helper is still resident. +DECLARE_WAYFIRE_PLUGIN(digitalrain_sampler_plugin_t); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-sampler.xml b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-sampler.xml new file mode 100644 index 000000000000..af5f6165f78b --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-sampler.xml @@ -0,0 +1,8 @@ +<?xml version='1.0' encoding='utf-8'?> +<wayfire> + <plugin name="digitalrain-sampler"> + <_short>Adaptive colour sampling</_short> + <_long>Sample desktop contrast for adaptive Digital Rain colours.</_long> + <category>Utility</category> + </plugin> +</wayfire> diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-targets.hpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-targets.hpp new file mode 100644 index 000000000000..9378692daefb --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain-targets.hpp @@ -0,0 +1,11 @@ +#pragma once +#include <wayfire/output.hpp> +#include <wayfire/output-stitch.hpp> +#include <vector> + +inline std::vector<wf::output_stitch_zone_t> digitalrain_targets(wf::output_t *output) +{ + if (auto stitch = output->get_data<wf::output_stitch_layout_t>()) + if (!stitch->zones.empty()) return stitch->zones; + return {{output->to_string(), output->get_relative_geometry()}}; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain.c new file mode 100644 index 000000000000..fb6d536797fd --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain.c @@ -0,0 +1,4205 @@ +/* + * digitalrain -- combined OLED screensaver: manager + renderer in one binary. + * + * No flags → manager mode (idle detection, Wayfire focus IPC, Wayland outputs) + * --render → renderer mode (Wayland layer-shell Matrix rain) + * + * The manager fork/execs itself with --render --output <name> [--color ...] + * [--font-size ...] [--min-speed ...] [--max-speed ...] etc. using /proc/self/exe. + * + * Build: make -C ~/.local/src digitalrain + * Requires: json-c, pthreads, wayland-client, wayland-egl, EGL, GLESv2, freetype2 + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <time.h> +#include <math.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <stdarg.h> +#include <stdint.h> +#include <poll.h> +#include <dirent.h> + +#include <json-c/json.h> +#include <wayland-client.h> +#include <wayland-egl.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <ft2build.h> +#include FT_FREETYPE_H + +#include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" +#include "ext-idle-notify-v1-client-protocol.h" +#include "digitalrain-control.h" + +/* A saver target is a physical region of a Wayland desktop output. */ +#define MAX_OUTPUTS 8 +#define MAX_OUTPUT_NAME 64 +typedef struct { + char name[MAX_OUTPUT_NAME], desktop[MAX_OUTPUT_NAME]; + int x, y, width, height; + int layout_x, layout_y; + bool region; +} saver_target_t; + +static struct json_object *mgr_ipc_request(const char *method, const char *data_json); + +static bool target_geometry(struct json_object *object, saver_target_t *target) { + struct json_object *g, *x, *y, *w, *h; + if (!json_object_object_get_ex(object, "geometry", &g) || + !json_object_object_get_ex(g, "x", &x) || + !json_object_object_get_ex(g, "y", &y) || + !json_object_object_get_ex(g, "width", &w) || + !json_object_object_get_ex(g, "height", &h)) return false; + target->x = json_object_get_int(x); target->y = json_object_get_int(y); + target->width = json_object_get_int(w); target->height = json_object_get_int(h); + return target->width > 0 && target->height > 0; +} + +/* -1 means discovery failed: retain the previous topology in that case. */ +static int parse_targets(struct json_object *outputs, saver_target_t targets[MAX_OUTPUTS]) { + if (!outputs || !json_object_is_type(outputs, json_type_array)) return -1; + int count = 0; + for (size_t i = 0; i < json_object_array_length(outputs); ++i) { + struct json_object *output = json_object_array_get_idx(outputs, i), *name, *members; + saver_target_t desktop = {0}; + if (!json_object_object_get_ex(output, "name", &name) || + !json_object_is_type(name, json_type_string) || !target_geometry(output, &desktop)) return -1; + snprintf(desktop.name, sizeof(desktop.name), "%s", json_object_get_string(name)); + snprintf(desktop.desktop, sizeof(desktop.desktop), "%s", desktop.name); + desktop.layout_x = desktop.x; desktop.layout_y = desktop.y; + desktop.x = desktop.y = 0; + if (json_object_object_get_ex(output, "members", &members) && + json_object_is_type(members, json_type_array) && json_object_array_length(members)) { + for (size_t j = 0; j < json_object_array_length(members); ++j) { + struct json_object *member = json_object_array_get_idx(members, j); + saver_target_t target = desktop; + if (!json_object_object_get_ex(member, "name", &name) || + !json_object_is_type(name, json_type_string) || !target_geometry(member, &target)) return -1; + if (target.x < 0 || target.y < 0 || target.width > desktop.width - target.x || + target.height > desktop.height - target.y || count == MAX_OUTPUTS) return -1; + snprintf(target.name, sizeof(target.name), "%s", json_object_get_string(name)); + target.region = true; + targets[count++] = target; + } + } else { + if (count == MAX_OUTPUTS) return -1; + targets[count++] = desktop; + } + } + return count; +} + +/* =================================================================== + * RENDERER MODE (Wayland layer-shell Matrix rain) + * =================================================================== */ + +/* ── Tuning constants ─────────────────────────────────────────────── */ + +#define TARGET_FPS 30 +#define FRAME_INTERVAL_US (1000000 / TARGET_FPS) + +/* These are now defaults; actual values come from CLI flags */ +#define DEFAULT_MIN_DENSITY 0.25 +#define DEFAULT_MAX_DENSITY 0.45 +#define DEFAULT_MIN_TRAIL 4 +#define DEFAULT_MAX_TRAIL 28 +#define DEFAULT_MIN_SPEED 0.4 +#define DEFAULT_MAX_SPEED 2.0 +#define DEFAULT_AVG_SPEED 0.8 +#define DEFAULT_FONT_SIZE 12 +#define MIN_FONT_SIZE 6 +#define MAX_FONT_SIZE 72 +#define DEFAULT_HEAD_HOLD_FRAMES 5 +#define DEFAULT_HEAD_MUTATION_CHANCE 0.15 +#define DEFAULT_HEAD_WHITE_MIN 0.75 +#define DEFAULT_HEAD_WHITE_MAX 1.00 +#define MUTATION_CHANCE 0.07 +#define RESPAWN_DELAY_MAX 40 +#define FADE_LEVELS 6 +#define HEAD_GLOW_EXTRA 2 + +#define DEFAULT_DEPTH_LAYERS 4 +#define MIN_DEPTH_LAYERS 1 +#define MAX_DEPTH_LAYERS 8 + +/* Character sets */ +static const uint32_t KATAKANA_START = 0xFF66; +static const uint32_t KATAKANA_END = 0xFF9D; +#define NUM_KATAKANA (KATAKANA_END - KATAKANA_START + 1) + +static const char *LATIN_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +static const char *DIGIT_CHARS = "0123456789"; +static const char *SYMBOL_CHARS = "!@#$%^&*()-=+[]{}|;:<>?/~"; + +/* ── Color presets ────────────────────────────────────────────────── */ + +typedef struct { float r, g, b; } rgb_t; + +static const struct { const char *name; rgb_t color; } RENDER_COLOR_TABLE[] = { + { "green", { 0.0f, 1.0f, 0.0f } }, + { "red", { 1.0f, 0.2f, 0.2f } }, + { "blue", { 0.3f, 0.5f, 1.0f } }, + { "white", { 1.0f, 1.0f, 1.0f } }, + { "yellow", { 1.0f, 1.0f, 0.0f } }, + { "cyan", { 0.0f, 1.0f, 1.0f } }, + { "magenta", { 1.0f, 0.0f, 1.0f } }, +}; +#define RENDER_NUM_COLORS (sizeof(RENDER_COLOR_TABLE) / sizeof(RENDER_COLOR_TABLE[0])) + +#include "digitalrain-config.h" +#include "digitalrain-live.h" + +/* ── Glyph atlas types ────────────────────────────────────────────── */ + +#define ATLAS_MAX_GLYPHS 256 + +typedef struct { + uint32_t codepoint; + float u0, v0, u1, v1; /* UV coords in atlas */ + int width, height; /* glyph bitmap size */ + int bearing_x, bearing_y; + int advance; /* horizontal advance in pixels (26.6 fixed >> 6) */ +} glyph_info_t; + +typedef struct { + GLuint texture; + int tex_width, tex_height; + glyph_info_t glyphs[ATLAS_MAX_GLYPHS]; + int glyph_count; + int cell_w, cell_h; /* max advance and line height for grid calc */ +} glyph_atlas_t; + +/* ── Data types (renderer) ────────────────────────────────────────── */ + +typedef struct { + uint32_t codepoint; + int age; + bool mirrored; /* horizontally flip the glyph */ + float white_mix; /* white fraction used while the glyph is near the head */ +} render_cell_t; + +typedef struct { + bool active; + double speed; + double head_pos; + int trail_len; + int respawn_timer; + float x_offset; /* horizontal jitter in pixels */ + bool skip; /* permanently inactive column for gaps */ + int flash_timer; /* when > 0, column is flashing */ + float flash_brightness; /* brightness boost during flash */ + rgb_t color; /* per-trail color */ + int last_head_row; + int head_hold_frames; +} render_column_t; + +typedef struct { + int cols, rows; + int cell_w, cell_h; + double opacity; + double speed_scale; + double density; + double font_scale; + render_column_t *columns; + render_cell_t *cells; +} depth_layer_t; + +/* Depth layer extreme values for interpolation */ +#define LAYER_FAR_FONT_SCALE 0.5 +#define LAYER_FAR_OPACITY 0.2 +#define LAYER_FAR_SPEED_SCALE 0.4 +#define LAYER_FAR_DENSITY 0.35 + +#define LAYER_NEAR_FONT_SCALE 1.25 +#define LAYER_NEAR_OPACITY 1.0 +#define LAYER_NEAR_SPEED_SCALE 1.4 +#define LAYER_NEAR_DENSITY 0.15 + +/* ── Vertex type for batched quad rendering ───────────────────────── */ + +typedef struct { + float x, y; + float u, v; + float r, g, b, a; +} vertex_t; + +/* Max quads per draw call (each quad = 6 vertices) */ +#define MAX_QUADS (256 * 256) +#define VERTS_PER_QUAD 6 + +/* ── Render phase lifecycle ────────────────────────────────────────── */ + +typedef enum { + PHASE_FADE_TO_BLACK, /* desktop fades to black overlay */ + PHASE_FADE_IN, /* matrix rain fades in on black */ + PHASE_RUNNING, /* normal matrix rain */ + PHASE_FADE_RAIN_TO_BLACK, /* timed rain fade, keeping the black overlay */ + PHASE_BLACK, /* retain a static black surface until activity */ + PHASE_FADE_OUT, /* matrix rain fades out to black */ + PHASE_FADE_TO_DESKTOP /* black fades to reveal desktop */ +} render_phase_t; + +/* ── Renderer global state ────────────────────────────────────────── */ + +static struct { + /* Wayland globals */ + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct zwlr_layer_shell_v1 *layer_shell; + struct zxdg_output_manager_v1 *xdg_output_manager; + + /* Target output */ + struct wl_output *target_output; + char target_name[128]; + saver_target_t target; + bool output_found; + int output_width; + int output_height; + int output_scale; + + /* Surface */ + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; + uint32_t configure_serial; + bool configured; + bool surface_dirty; + int surface_width; + int surface_height; + + /* EGL */ + EGLDisplay egl_display; + EGLContext egl_context; + EGLSurface egl_surface; + struct wl_egl_window *egl_window; + + /* OpenGL */ + GLuint shader_program; + GLint u_projection; + GLint u_atlas; + GLint a_pos; + GLint a_uv; + GLint a_color; + GLuint vbo; + vertex_t *vertex_buf; + int vertex_count; + + /* FreeType */ + FT_Library ft_library; + FT_Face ft_face; + + /* Glyph atlas (one at largest font size) */ + glyph_atlas_t atlas; + + /* Matrix state */ + int num_depth_layers; + double simulation_credit; + depth_layer_t layers[MAX_DEPTH_LAYERS]; + rgb_t base_color; + bool adaptive_color; + rgb_t adaptive_desktop_color; + rgb_t adaptive_black_color; + int color_mode; /* 0=fixed, 1=random_trail_named, 2=random_trail_hex */ + int font_size; + + /* Configurable ranges (set via CLI flags) */ + double min_speed; + double max_speed; + double avg_speed; + int min_trail; + int max_trail; + double min_density; + double max_density; + double layer_near_density; + double layer_far_density; + int head_hold_frames; + double head_mutation_chance; + double head_white_min; + double head_white_max; + + /* Run control */ + volatile sig_atomic_t stop; + volatile sig_atomic_t fade_out_requested; + volatile sig_atomic_t adaptive_reload; + bool closed; + + /* Phase-based transition state */ + render_phase_t phase; + int phase_frame; + float surface_alpha; /* alpha for glClearColor (0=transparent, 1=opaque) */ + float fade_opacity; /* rain opacity multiplier (0=invisible, 1=full) */ + int fade_frames; /* configurable transition duration */ + double transparent_timeout; + double black_rain_timeout; + double black_start_time; + double render_start_time; + int blacken_frames; + bool background_black; + bool ambient; /* background client; lifetime belongs to its controller */ + bool transparent; /* keep the desktop visible behind the rain */ + bool hide_desktop; /* leave only wallpaper behind transparent rain */ + int desktop_fd; /* held IPC connection; close restores desktop */ + bool desktop_resuming; + bool power_off; /* opt-in: replace final solid black with DPMS */ + int power_fd; /* compositor restores power on disconnect */ + bool power_attempted; + int report_fd, reported_phase, reported_power; + volatile sig_atomic_t black_requested; + unsigned applied_revision, attempted_revision, reported_revision; + int live_error, reported_error; + dr_effect_packet effects; + float effect_mix[MAX_OUTPUTS], effect_energy[MAX_OUTPUTS], effect_peak[MAX_OUTPUTS]; + double effect_tick, palette_until; + double audio_attack, audio_release; + char live_color[32]; +} S; + +static double render_column_speed(int x, int y); +static rgb_t render_column_color(int x, int y, rgb_t base, float *brightness); +static void render_effect_tick(void); +static void render_live_receive(void); + +#define DEFAULT_FADE_FRAMES 30 /* 1 second at 30fps */ + +/* ── Renderer helpers ─────────────────────────────────────────────── */ + +static inline double render_randf(void) { return (double)rand() / RAND_MAX; } +static double render_monotime(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec / 1e9; +} +static inline int render_randi(int lo, int hi) { return lo + rand() % (hi - lo + 1); } + +static inline float render_head_white_mix(void) { + return (float)(S.head_white_min + + (S.head_white_max - S.head_white_min) * render_randf()); +} + +/* Generate a random speed biased toward avg, clamped to [lo, hi]. + Uses sum-of-3-uniforms (approx gaussian) for bell-curve distribution. */ +static inline double render_rand_speed(double lo, double hi, double avg) { + double r = (render_randf() + render_randf() + render_randf()) / 3.0; + /* r is ~gaussian centered on 0.5; map to [lo, hi] biased toward avg */ + double v = (r < 0.5) + ? lo + (avg - lo) * (r / 0.5) + : avg + (hi - avg) * ((r - 0.5) / 0.5); + if (v < lo) v = lo; + if (v > hi) v = hi; + return v; +} + +static render_cell_t *render_cell_at_layer(depth_layer_t *layer, int row, int col) { + return &layer->cells[row * layer->cols + col]; +} + +static uint32_t render_random_codepoint(void) { + int choice = rand() % 100; + if (choice < 40) { + return KATAKANA_START + (rand() % NUM_KATAKANA); + } else if (choice < 70) { + return (uint32_t)LATIN_CHARS[rand() % (int)strlen(LATIN_CHARS)]; + } else if (choice < 85) { + return (uint32_t)DIGIT_CHARS[rand() % (int)strlen(DIGIT_CHARS)]; + } else { + return (uint32_t)SYMBOL_CHARS[rand() % (int)strlen(SYMBOL_CHARS)]; + } +} + +static rgb_t render_parse_hex(const char *hex) { + rgb_t c = { 0.0f, 0.0f, 0.0f }; + const char *p = hex; + if (*p == '#') p++; + unsigned int val = 0; + if (sscanf(p, "%06x", &val) == 1) { + c.r = ((val >> 16) & 0xFF) / 255.0f; + c.g = ((val >> 8) & 0xFF) / 255.0f; + c.b = (val & 0xFF) / 255.0f; + } + return c; +} + +static rgb_t render_lookup_color(const char *name) { + /* Hex color: #RRGGBB or RRGGBB */ + if (name[0] == '#' || (strlen(name) == 6 && strspn(name, "0123456789abcdefABCDEF") == 6)) + return render_parse_hex(name); + for (size_t i = 0; i < RENDER_NUM_COLORS; i++) { + if (strcasecmp(name, RENDER_COLOR_TABLE[i].name) == 0) + return RENDER_COLOR_TABLE[i].color; + } + /* Per-output random: pick one color at startup */ + if (strcasecmp(name, "random_named") == 0) + return RENDER_COLOR_TABLE[rand() % RENDER_NUM_COLORS].color; + if (strcasecmp(name, "random_hex") == 0) + return (rgb_t){ + (float)(rand() % 256) / 255.0f, + (float)(rand() % 256) / 255.0f, + (float)(rand() % 256) / 255.0f + }; + /* Per-trail random modes: base_color is fallback, actual color set per-trail */ + if (strcasecmp(name, "random_trail_named") == 0 || + strcasecmp(name, "random_trail_hex") == 0) + return RENDER_COLOR_TABLE[0].color; + return RENDER_COLOR_TABLE[0].color; +} + +enum { COLOR_FIXED = 0, COLOR_RANDOM = 1, COLOR_RANDOM_HEX = 2 }; + +static rgb_t render_random_color(void) { + return RENDER_COLOR_TABLE[rand() % RENDER_NUM_COLORS].color; +} + +static rgb_t render_random_hex_color(void) { + return (rgb_t){ + (float)(rand() % 256) / 255.0f, + (float)(rand() % 256) / 255.0f, + (float)(rand() % 256) / 255.0f + }; +} + +static rgb_t render_trail_color(void) { + switch (S.color_mode) { + case COLOR_RANDOM: return render_random_color(); + case COLOR_RANDOM_HEX: return render_random_hex_color(); + default: return S.base_color; + } +} + +/* ── FreeType / Glyph Atlas ───────────────────────────────────────── */ + +/* Build list of all codepoints we need */ +static int render_build_codepoint_list(uint32_t *out, int max) { + int n = 0; + /* Katakana */ + for (uint32_t cp = KATAKANA_START; cp <= KATAKANA_END && n < max; cp++) + out[n++] = cp; + /* Latin */ + for (const char *p = LATIN_CHARS; *p && n < max; p++) + out[n++] = (uint32_t)*p; + /* Digits */ + for (const char *p = DIGIT_CHARS; *p && n < max; p++) + out[n++] = (uint32_t)*p; + /* Symbols */ + for (const char *p = SYMBOL_CHARS; *p && n < max; p++) + out[n++] = (uint32_t)*p; + return n; +} + +static bool render_init_freetype(int font_pixel_size) { + if (FT_Init_FreeType(&S.ft_library)) { + fprintf(stderr, "Failed to init FreeType\n"); + return false; + } + + /* Try fonts in order of preference */ + static const char *font_paths[] = { + "/usr/share/fonts/noto-cjk/NotoSansMonoCJKjp-Regular.otf", + "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/google-noto-cjk/NotoSansMonoCJKjp-Regular.otf", + "/usr/share/fonts/noto/NotoSansMono-Regular.ttf", + "/usr/share/fonts/liberation-mono/LiberationMono-Regular.ttf", + "/usr/share/fonts/liberation/LiberationMono-Regular.ttf", + "/usr/share/fonts/TTF/LiberationMono-Regular.ttf", + "/usr/share/fonts/dejavu/DejaVuSansMono.ttf", + "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", + NULL + }; + + bool loaded = false; + for (int i = 0; font_paths[i]; i++) { + if (FT_New_Face(S.ft_library, font_paths[i], 0, &S.ft_face) == 0) { + fprintf(stderr, "Loaded font: %s\n", font_paths[i]); + loaded = true; + break; + } + } + + if (!loaded) { + fprintf(stderr, "No suitable font found, trying fc-match\n"); + /* Last resort: use fontconfig via popen */ + FILE *fp = popen("fc-match -f '%{file}' 'monospace'", "r"); + if (fp) { + char path[512]; + if (fgets(path, sizeof(path), fp)) { + char *nl = strchr(path, '\n'); + if (nl) *nl = '\0'; + if (FT_New_Face(S.ft_library, path, 0, &S.ft_face) == 0) { + fprintf(stderr, "Loaded font via fc-match: %s\n", path); + loaded = true; + } + } + pclose(fp); + } + } + + if (!loaded) { + fprintf(stderr, "Could not load any font\n"); + FT_Done_FreeType(S.ft_library); + return false; + } + + FT_Set_Pixel_Sizes(S.ft_face, 0, font_pixel_size); + return true; +} + +static bool render_build_atlas(int font_pixel_size) { + uint32_t codepoints[ATLAS_MAX_GLYPHS]; + int cp_count = render_build_codepoint_list(codepoints, ATLAS_MAX_GLYPHS); + + /* First pass: render all glyphs to measure total atlas size */ + FT_Set_Pixel_Sizes(S.ft_face, 0, font_pixel_size); + + /* Estimate atlas dimensions: pack in rows */ + int row_height = 0; + int atlas_w = 0, atlas_h = 0; + int padding = 2; + int max_row_w = 2048; /* max texture width */ + + /* Temporary storage for bitmaps */ + typedef struct { + uint32_t cp; + int w, h, bearing_x, bearing_y, advance; + unsigned char *bitmap; + } tmp_glyph_t; + + tmp_glyph_t *tmp_glyphs = calloc(cp_count, sizeof(tmp_glyph_t)); + if (!tmp_glyphs) return false; + int valid_count = 0; + int max_cell_w = 0, max_cell_h = 0; + + for (int i = 0; i < cp_count; i++) { + if (FT_Load_Char(S.ft_face, codepoints[i], FT_LOAD_RENDER)) { + continue; /* skip glyphs that fail to render */ + } + FT_GlyphSlot g = S.ft_face->glyph; + int gw = g->bitmap.width; + int gh = g->bitmap.rows; + + tmp_glyph_t *tg = &tmp_glyphs[valid_count]; + tg->cp = codepoints[i]; + tg->w = gw; + tg->h = gh; + tg->bearing_x = g->bitmap_left; + tg->bearing_y = g->bitmap_top; + tg->advance = (int)(g->advance.x >> 6); + + if (tg->advance > max_cell_w) max_cell_w = tg->advance; + if (gh > max_cell_h) max_cell_h = gh; + + /* Copy bitmap data */ + tg->bitmap = malloc(gw * gh); + if (!tg->bitmap && gw * gh > 0) { + for (int i = 0; i < valid_count; i++) free(tmp_glyphs[i].bitmap); + free(tmp_glyphs); return false; + } + if (tg->bitmap && gw > 0 && gh > 0) { + for (int r = 0; r < gh; r++) + memcpy(tg->bitmap + r * gw, + g->bitmap.buffer + r * g->bitmap.pitch, gw); + } + + valid_count++; + } + + /* Calculate atlas layout */ + int cx = 0, cy = 0; + row_height = 0; + for (int i = 0; i < valid_count; i++) { + tmp_glyph_t *tg = &tmp_glyphs[i]; + int gw = tg->w + padding; + int gh = tg->h + padding; + + if (cx + gw > max_row_w) { + cy += row_height; + cx = 0; + row_height = 0; + } + if (gh > row_height) row_height = gh; + cx += gw; + if (cx > atlas_w) atlas_w = cx; + } + atlas_h = cy + row_height; + + /* Round up to power of 2 */ + int pot_w = 1; while (pot_w < atlas_w) pot_w <<= 1; + int pot_h = 1; while (pot_h < atlas_h) pot_h <<= 1; + atlas_w = pot_w; + atlas_h = pot_h; + + /* Allocate atlas pixel data (single channel) */ + unsigned char *atlas_data = calloc(atlas_w * atlas_h, 1); + if (!atlas_data) { for (int i = 0; i < valid_count; i++) free(tmp_glyphs[i].bitmap); free(tmp_glyphs); return false; } + + /* Second pass: blit glyphs and record UVs */ + cx = 0; cy = 0; row_height = 0; + S.atlas.glyph_count = 0; + + for (int i = 0; i < valid_count; i++) { + tmp_glyph_t *tg = &tmp_glyphs[i]; + int gw = tg->w; + int gh = tg->h; + + if (cx + gw + padding > atlas_w) { + cy += row_height; + cx = 0; + row_height = 0; + } + if (gh + padding > row_height) row_height = gh + padding; + + /* Blit */ + if (tg->bitmap && gw > 0 && gh > 0) { + for (int r = 0; r < gh; r++) { + memcpy(atlas_data + (cy + r) * atlas_w + cx, + tg->bitmap + r * gw, gw); + } + } + + /* Record glyph info */ + glyph_info_t *gi = &S.atlas.glyphs[S.atlas.glyph_count++]; + gi->codepoint = tg->cp; + gi->u0 = (float)cx / atlas_w; + gi->v0 = (float)cy / atlas_h; + gi->u1 = (float)(cx + gw) / atlas_w; + gi->v1 = (float)(cy + gh) / atlas_h; + gi->width = gw; + gi->height = gh; + gi->bearing_x = tg->bearing_x; + gi->bearing_y = tg->bearing_y; + gi->advance = tg->advance; + + cx += gw + padding; + + free(tg->bitmap); + } + free(tmp_glyphs); + + S.atlas.tex_width = atlas_w; + S.atlas.tex_height = atlas_h; + /* Use font metrics for consistent cell size */ + S.atlas.cell_w = max_cell_w > 0 ? max_cell_w : font_pixel_size; + /* Use ascender + descender for proper line height */ + int line_height = (int)((S.ft_face->size->metrics.height + 63) >> 6); + S.atlas.cell_h = line_height > 0 ? line_height : (int)(font_pixel_size * 1.4); + if (max_cell_h > S.atlas.cell_h) S.atlas.cell_h = max_cell_h; + + /* Create GL texture */ + glGenTextures(1, &S.atlas.texture); + glBindTexture(GL_TEXTURE_2D, S.atlas.texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, atlas_w, atlas_h, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, atlas_data); + + free(atlas_data); + + fprintf(stderr, "Atlas: %dx%d, %d glyphs, cell=%dx%d\n", + atlas_w, atlas_h, S.atlas.glyph_count, + S.atlas.cell_w, S.atlas.cell_h); + + return true; +} + +static const glyph_info_t *render_find_glyph(uint32_t codepoint) { + for (int i = 0; i < S.atlas.glyph_count; i++) { + if (S.atlas.glyphs[i].codepoint == codepoint) + return &S.atlas.glyphs[i]; + } + /* Fallback to '?' or first glyph */ + for (int i = 0; i < S.atlas.glyph_count; i++) { + if (S.atlas.glyphs[i].codepoint == '?') + return &S.atlas.glyphs[i]; + } + return S.atlas.glyph_count > 0 ? &S.atlas.glyphs[0] : NULL; +} + +/* ── EGL setup ────────────────────────────────────────────────────── */ + +static bool render_init_egl(void) { + S.egl_display = eglGetDisplay((EGLNativeDisplayType)S.display); + if (S.egl_display == EGL_NO_DISPLAY) { + /* Try platform extension */ + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = + (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + if (eglGetPlatformDisplayEXT) { + S.egl_display = eglGetPlatformDisplayEXT( + EGL_PLATFORM_WAYLAND_KHR, S.display, NULL); + } + } + if (S.egl_display == EGL_NO_DISPLAY) { + fprintf(stderr, "Failed to get EGL display\n"); + return false; + } + + EGLint major, minor; + if (!eglInitialize(S.egl_display, &major, &minor)) { + fprintf(stderr, "Failed to initialize EGL\n"); + return false; + } + fprintf(stderr, "EGL %d.%d initialized\n", major, minor); + + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + fprintf(stderr, "Failed to bind OpenGL ES API\n"); + return false; + } + + EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLConfig config; + EGLint num_configs; + if (!eglChooseConfig(S.egl_display, config_attribs, &config, 1, &num_configs) || + num_configs == 0) { + fprintf(stderr, "Failed to choose EGL config\n"); + return false; + } + + EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + S.egl_context = eglCreateContext(S.egl_display, config, EGL_NO_CONTEXT, + context_attribs); + if (S.egl_context == EGL_NO_CONTEXT) { + fprintf(stderr, "Failed to create EGL context\n"); + return false; + } + + /* Create EGL window surface */ + S.egl_window = wl_egl_window_create(S.surface, + S.surface_width, S.surface_height); + if (!S.egl_window) { + fprintf(stderr, "Failed to create wl_egl_window\n"); + return false; + } + + S.egl_surface = eglCreateWindowSurface(S.egl_display, config, + (EGLNativeWindowType)S.egl_window, NULL); + if (S.egl_surface == EGL_NO_SURFACE) { + fprintf(stderr, "Failed to create EGL surface\n"); + return false; + } + + if (!eglMakeCurrent(S.egl_display, S.egl_surface, S.egl_surface, S.egl_context)) { + fprintf(stderr, "eglMakeCurrent failed\n"); + return false; + } + + /* Set swap interval to 0 for manual frame pacing */ + eglSwapInterval(S.egl_display, 0); + + fprintf(stderr, "EGL surface created: %dx%d\n", S.surface_width, S.surface_height); + return true; +} + +/* ── Shader compilation ───────────────────────────────────────────── */ + +static const char *vertex_shader_src = + "attribute vec2 a_pos;\n" + "attribute vec2 a_uv;\n" + "attribute vec4 a_color;\n" + "varying vec2 v_uv;\n" + "varying vec4 v_color;\n" + "uniform mat4 u_projection;\n" + "void main() {\n" + " gl_Position = u_projection * vec4(a_pos, 0.0, 1.0);\n" + " v_uv = a_uv;\n" + " v_color = a_color;\n" + "}\n"; + +static const char *fragment_shader_src = + "precision mediump float;\n" + "varying vec2 v_uv;\n" + "varying vec4 v_color;\n" + "uniform sampler2D u_atlas;\n" + "void main() {\n" + " float a = texture2D(u_atlas, v_uv).r;\n" + " if (a < 0.01) discard;\n" + " float scanline = 1.0 - 0.08 * mod(gl_FragCoord.y, 2.0);\n" + " a *= clamp(v_color.a, 0.0, 1.0);\n" + " gl_FragColor = vec4(v_color.rgb * scanline * a, a);\n" + "}\n"; + +static GLuint render_compile_shader(GLenum type, const char *src) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + + GLint compiled; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + char log[512]; + glGetShaderInfoLog(shader, sizeof(log), NULL, log); + fprintf(stderr, "Shader compile error: %s\n", log); + glDeleteShader(shader); + return 0; + } + return shader; +} + +static bool render_init_shaders(void) { + GLuint vs = render_compile_shader(GL_VERTEX_SHADER, vertex_shader_src); + GLuint fs = render_compile_shader(GL_FRAGMENT_SHADER, fragment_shader_src); + if (!vs || !fs) return false; + + S.shader_program = glCreateProgram(); + glAttachShader(S.shader_program, vs); + glAttachShader(S.shader_program, fs); + glLinkProgram(S.shader_program); + + GLint linked; + glGetProgramiv(S.shader_program, GL_LINK_STATUS, &linked); + if (!linked) { + char log[512]; + glGetProgramInfoLog(S.shader_program, sizeof(log), NULL, log); + fprintf(stderr, "Shader link error: %s\n", log); + return false; + } + + glDeleteShader(vs); + glDeleteShader(fs); + + S.u_projection = glGetUniformLocation(S.shader_program, "u_projection"); + S.u_atlas = glGetUniformLocation(S.shader_program, "u_atlas"); + S.a_pos = glGetAttribLocation(S.shader_program, "a_pos"); + S.a_uv = glGetAttribLocation(S.shader_program, "a_uv"); + S.a_color = glGetAttribLocation(S.shader_program, "a_color"); + + /* Create VBO */ + glGenBuffers(1, &S.vbo); + + /* Allocate CPU-side vertex buffer */ + S.vertex_buf = malloc(MAX_QUADS * VERTS_PER_QUAD * sizeof(vertex_t)); + S.vertex_count = 0; + + fprintf(stderr, "Shaders compiled and linked\n"); + return true; +} + +static void render_set_projection(int width, int height) { + /* Orthographic projection: (0,0) top-left, (w,h) bottom-right */ + float proj[16] = { + 2.0f / width, 0.0f, 0.0f, 0.0f, + 0.0f, -2.0f / height, 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, 1.0f, + }; + glUseProgram(S.shader_program); + glUniformMatrix4fv(S.u_projection, 1, GL_FALSE, proj); +} + +/* ── Vertex buffer helpers ────────────────────────────────────────── */ + +static void render_push_quad(float x0, float y0, float x1, float y1, + float u0, float v0, float u1, float v1, + float r, float g, float b, float a) { + if (S.vertex_count + VERTS_PER_QUAD > MAX_QUADS * VERTS_PER_QUAD) + return; + + vertex_t *v = &S.vertex_buf[S.vertex_count]; + + /* Triangle 1: top-left, top-right, bottom-left */ + v[0] = (vertex_t){ x0, y0, u0, v0, r, g, b, a }; + v[1] = (vertex_t){ x1, y0, u1, v0, r, g, b, a }; + v[2] = (vertex_t){ x0, y1, u0, v1, r, g, b, a }; + + /* Triangle 2: top-right, bottom-right, bottom-left */ + v[3] = (vertex_t){ x1, y0, u1, v0, r, g, b, a }; + v[4] = (vertex_t){ x1, y1, u1, v1, r, g, b, a }; + v[5] = (vertex_t){ x0, y1, u0, v1, r, g, b, a }; + + S.vertex_count += VERTS_PER_QUAD; +} + +static void render_flush_quads(void) { + if (S.vertex_count == 0) return; + + glBindBuffer(GL_ARRAY_BUFFER, S.vbo); + glBufferData(GL_ARRAY_BUFFER, S.vertex_count * sizeof(vertex_t), + S.vertex_buf, GL_STREAM_DRAW); + + glEnableVertexAttribArray(S.a_pos); + glEnableVertexAttribArray(S.a_uv); + glEnableVertexAttribArray(S.a_color); + + glVertexAttribPointer(S.a_pos, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), + (void *)offsetof(vertex_t, x)); + glVertexAttribPointer(S.a_uv, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), + (void *)offsetof(vertex_t, u)); + glVertexAttribPointer(S.a_color, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), + (void *)offsetof(vertex_t, r)); + + glDrawArrays(GL_TRIANGLES, 0, S.vertex_count); + + glDisableVertexAttribArray(S.a_pos); + glDisableVertexAttribArray(S.a_uv); + glDisableVertexAttribArray(S.a_color); + + S.vertex_count = 0; +} + +/* ── Matrix engine ────────────────────────────────────────────────── */ + +static void render_tick_layer(depth_layer_t *layer); + +static void render_get_layer_properties(int layer_idx, int num_layers, + double *font_scale, double *opacity, + double *speed_scale, double *density) { + if (num_layers == 1) { + *font_scale = 1.0; + *opacity = 1.0; + *speed_scale = 1.0; + /* density from config, use midpoint of min/max */ + *density = (S.min_density + S.max_density) * 0.5; + return; + } + + double t = (double)layer_idx / (num_layers - 1); + *font_scale = LAYER_FAR_FONT_SCALE + t * (LAYER_NEAR_FONT_SCALE - LAYER_FAR_FONT_SCALE); + *opacity = LAYER_FAR_OPACITY + t * (LAYER_NEAR_OPACITY - LAYER_FAR_OPACITY); + *speed_scale = LAYER_FAR_SPEED_SCALE + t * (LAYER_NEAR_SPEED_SCALE - LAYER_FAR_SPEED_SCALE); + *density = S.layer_far_density + t * (S.layer_near_density - S.layer_far_density); +} + +static bool render_init_layer(depth_layer_t *layer, int width, int height, + int layer_idx) { + render_get_layer_properties(layer_idx, S.num_depth_layers, + &layer->font_scale, &layer->opacity, + &layer->speed_scale, &layer->density); + + /* Cell size: scale the atlas cell size by font_scale */ + layer->cell_w = (int)(S.atlas.cell_w * layer->font_scale + 0.5); + layer->cell_h = (int)(S.atlas.cell_h * layer->font_scale + 0.5); + if (layer->cell_w < 4) layer->cell_w = 4; + if (layer->cell_h < 6) layer->cell_h = 6; + + layer->cols = width / layer->cell_w; + layer->rows = height / layer->cell_h; + if (layer->cols < 1) layer->cols = 1; + if (layer->rows < 1) layer->rows = 1; + + free(layer->columns); + free(layer->cells); + layer->columns = calloc(layer->cols, sizeof(render_column_t)); + layer->cells = calloc(layer->rows * layer->cols, sizeof(render_cell_t)); + + if (!layer->columns || !layer->cells) return false; + + double min_spd = S.min_speed * layer->speed_scale; + double max_spd = S.max_speed * layer->speed_scale; + double avg_spd = S.avg_speed * layer->speed_scale; + + for (int c = 0; c < layer->cols; c++) { + render_column_t *col = &layer->columns[c]; + col->x_offset = (render_randf() - 0.5f) * layer->cell_w * 0.5f; + col->skip = (render_randf() < 0.15); + col->last_head_row = -1; + col->head_hold_frames = 0; + if (render_randf() < layer->density) { + col->active = true; + col->color = render_trail_color(); + col->speed = render_rand_speed(min_spd, max_spd, avg_spd); + /* Spread start positions across the full screen height + so columns appear mid-fall from the start */ + col->head_pos = render_randf() * layer->rows * 2 - layer->rows; + col->trail_len = render_randi(S.min_trail, S.max_trail); + } else { + col->active = false; + col->respawn_timer = render_randi(1, RESPAWN_DELAY_MAX); + } + } + + /* Pre-populate cells for columns that are already mid-screen */ + for (int i = 0; i < layer->rows * layer->cols; i++) { + layer->cells[i].codepoint = render_random_codepoint(); + layer->cells[i].age = 9999; + layer->cells[i].mirrored = (render_randf() < 0.20); + layer->cells[i].white_mix = (float)S.head_white_max; + } + for (int c = 0; c < layer->cols; c++) { + render_column_t *col = &layer->columns[c]; + if (!col->active || col->skip) continue; + int head = (int)col->head_pos; + /* Fill trail behind the head with aged characters */ + for (int t = 0; t < col->trail_len && head - t >= 0; t++) { + int r = head - t; + if (r < layer->rows) { + render_cell_t *cell = render_cell_at_layer(layer, r, c); + cell->codepoint = render_random_codepoint(); + cell->age = t; + cell->mirrored = (render_randf() < 0.20); + cell->white_mix = render_head_white_mix(); + } + } + } + return true; +} + +static bool render_init_matrix(int width, int height) { + S.surface_width = width; + S.surface_height = height; + + for (int l = 0; l < S.num_depth_layers; l++) { + if (!render_init_layer(&S.layers[l], width, height, l)) return false; + } + + /* Pre-run the simulation so first frame looks like it's been + running for a while — no visible startup sweep */ + int warmup = 60 + rand() % 120; /* 2-6 seconds at 30fps */ + for (int f = 0; f < warmup; f++) { + for (int l = 0; l < S.num_depth_layers; l++) { + render_tick_layer(&S.layers[l]); + } + } + return true; +} + +static void render_tick_layer(depth_layer_t *layer) { + double min_spd = S.min_speed * layer->speed_scale; + double max_spd = S.max_speed * layer->speed_scale; + double avg_spd = S.avg_speed * layer->speed_scale; + + for (int c = 0; c < layer->cols; c++) { + render_column_t *col = &layer->columns[c]; + if (col->skip) continue; + + if (!col->active) { + if (--col->respawn_timer <= 0) { + col->active = true; + col->color = render_trail_color(); + col->speed = render_rand_speed(min_spd, max_spd, avg_spd); + col->head_pos = -1.0; + col->trail_len = render_randi(S.min_trail, S.max_trail); + col->last_head_row = -1; + col->head_hold_frames = 0; + } + continue; + } + + col->head_pos += col->speed * render_column_speed(c * layer->cell_w, (int)fmax(0, fmin(S.surface_height - 1, col->head_pos * layer->cell_h))); + int head_row = (int)col->head_pos; + bool head_visible = head_row >= 0 && head_row < layer->rows; + + if (head_visible) { + render_cell_t *cell = render_cell_at_layer(layer, head_row, c); + if (head_row != col->last_head_row) { + cell->codepoint = render_random_codepoint(); + cell->mirrored = (render_randf() < 0.20); + cell->white_mix = render_head_white_mix(); + col->last_head_row = head_row; + col->head_hold_frames = 0; + } else { + col->head_hold_frames++; + if (col->head_hold_frames >= S.head_hold_frames && + render_randf() < S.head_mutation_chance) { + cell->codepoint = render_random_codepoint(); + cell->mirrored = (render_randf() < 0.20); + cell->white_mix = render_head_white_mix(); + col->head_hold_frames = 0; + } + } + cell->age = 0; + } + + /* Flash columns: ~0.3% chance per frame of starting a flash */ + if (col->flash_timer > 0) { + col->flash_timer--; + } else if (render_randf() < 0.003) { + col->flash_timer = 6 + rand() % 5; /* 6-10 frames */ + col->flash_brightness = 1.8f; + } + + for (int r = 0; r < layer->rows; r++) { + render_cell_t *cell = render_cell_at_layer(layer, r, c); + if (head_visible && r == head_row) + continue; + if (cell->age < 9999) { + cell->age++; + } + if (cell->age > 0 && cell->age < col->trail_len && render_randf() < MUTATION_CHANCE) { + cell->codepoint = render_random_codepoint(); + } + } + + if (head_row > layer->rows + col->trail_len) { + col->active = false; + col->respawn_timer = render_randi(1, RESPAWN_DELAY_MAX); + col->last_head_row = -1; + col->head_hold_frames = 0; + for (int r = 0; r < layer->rows; r++) { + render_cell_at_layer(layer, r, c)->age = 9999; + } + } + } +} + +static double render_simulation_speed = 1; +static void render_tick_matrix(void) { + render_effect_tick(); + S.simulation_credit += render_simulation_speed; + while (S.simulation_credit >= 1) { + for (int l = 0; l < S.num_depth_layers; l++) render_tick_layer(&S.layers[l]); + S.simulation_credit -= 1; + } +} + +/* ── Rendering ────────────────────────────────────────────────────── */ + +static void render_frame(void) { + int w = S.surface_width; + int h = S.surface_height; + + glViewport(0, 0, w, h); + /* glClearColor/glClear is handled by the phase logic in the main loop */ + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glUseProgram(S.shader_program); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, S.atlas.texture); + glUniform1i(S.u_atlas, 0); + + float fade = S.fade_opacity; + + /* Render layers back-to-front (0=far, N-1=near) */ + for (int l = 0; l < S.num_depth_layers; l++) { + depth_layer_t *layer = &S.layers[l]; + /* Depth and trail age control intensity, not surface transparency. + * Otherwise windows remain visible through the bodies of the glyphs, + * making the rain appear behind their text and images. Only glyph + * coverage and the overall transition affect stroke alpha. */ + float intensity = (float)layer->opacity; + float scale = (float)layer->font_scale; + + S.vertex_count = 0; + + for (int c = 0; c < layer->cols; c++) { + render_column_t *col = &layer->columns[c]; + if (col->skip) continue; + + for (int r = 0; r < layer->rows; r++) { + render_cell_t *cell = render_cell_at_layer(layer, r, c); + + if (cell->age >= 9999) continue; + + float brightness; + bool is_head = false; + + if (cell->age == 0 && col->active) { + is_head = true; + brightness = 1.0f; + } else if (cell->age < col->trail_len) { + float t = 1.0f - (float)cell->age / col->trail_len; + brightness = t * t; + if (brightness < 0.05f) continue; + } else { + continue; + } + + /* Flash column boost */ + if (col->flash_timer > 0) { + float flash_t = (float)col->flash_timer / 10.0f; + float boost = 1.0f + (col->flash_brightness - 1.0f) * flash_t; + brightness *= boost; + if (brightness > 1.0f && !is_head) brightness = 1.0f; + } + + const glyph_info_t *gi = render_find_glyph(cell->codepoint); + if (!gi) continue; + + /* Position the glyph within the cell */ + float cell_x = (float)(c * layer->cell_w) + layer->columns[c].x_offset; + float cell_y = (float)(r * layer->cell_h); + + /* Scale glyph dimensions by layer font_scale relative to atlas */ + float gw = gi->width * scale; + float gh = gi->height * scale; + float bx = gi->bearing_x * scale; + float by = gi->bearing_y * scale; + + /* Center glyph in cell: x based on bearing, y based on bearing */ + float x0 = cell_x + bx; + float y0 = cell_y + (layer->cell_h - by); + float x1 = x0 + gw; + float y1 = y0 + gh; + + /* UV coordinates — possibly mirrored horizontally */ + float qu0 = cell->mirrored ? gi->u1 : gi->u0; + float qu1 = cell->mirrored ? gi->u0 : gi->u1; + + /* Determine color: white-to-trail gradient for head and near-head */ + float live_brightness; + rgb_t tc = render_column_color(c * layer->cell_w, r * layer->cell_h, col->color, &live_brightness); + float cr, cg, cb; + /* Keep the monitor's adaptive hue and contrast at the head too. */ + float white_mix = S.adaptive_color ? 0.0f : cell->white_mix; + if (white_mix < 0.0f) white_mix = 0.0f; + if (white_mix > 1.0f) white_mix = 1.0f; + if (is_head) { + cr = white_mix + (1.0f - white_mix) * tc.r; + cg = white_mix + (1.0f - white_mix) * tc.g; + cb = white_mix + (1.0f - white_mix) * tc.b; + } else if (cell->age == 1) { + white_mix *= 0.7f; + cr = white_mix + (1.0f - white_mix) * tc.r; + cg = white_mix + (1.0f - white_mix) * tc.g; + cb = white_mix + (1.0f - white_mix) * tc.b; + } else if (cell->age == 2) { + white_mix *= 0.4f; + cr = white_mix + (1.0f - white_mix) * tc.r; + cg = white_mix + (1.0f - white_mix) * tc.g; + cb = white_mix + (1.0f - white_mix) * tc.b; + } else { + /* pure trail color */ + cr = tc.r; cg = tc.g; cb = tc.b; + } + + if (is_head) { + /* Enhanced "type-in" glow: 50% larger pad, stronger alpha */ + float glow_pad = HEAD_GLOW_EXTRA * scale * 1.5f; + if (glow_pad < 1.5f) glow_pad = 1.5f; + float ga = 0.6f * intensity * fade; + render_push_quad(x0 - glow_pad, y0 - glow_pad, + x1 + glow_pad, y1 + glow_pad, + qu0, gi->v0, qu1, gi->v1, + cr, cg, cb, ga); + + /* Solid glyph body; antialiased edges and phase fades. */ + render_push_quad(x0, y0, x1, y1, + qu0, gi->v0, qu1, gi->v1, + fminf(1, cr * intensity * live_brightness), fminf(1, cg * intensity * live_brightness), fminf(1, cb * intensity * live_brightness), fade); + } else { + float b = fminf(1, brightness * intensity * live_brightness); + render_push_quad(x0, y0, x1, y1, + qu0, gi->v0, qu1, gi->v1, + cr * b, cg * b, cb * b, fade); + } + } + } + + render_flush_quads(); + } +} + +/* ── Wayland output tracking ──────────────────────────────────────── */ + +struct render_output_info { + struct wl_output *output; + struct zxdg_output_v1 *xdg_output; + char name[128]; + int width, height; + int scale; + struct wl_list link; +}; + +static struct wl_list render_output_list; + +static void render_xdg_output_handle_name(void *data, + struct zxdg_output_v1 *xdg_output, const char *name) { + (void)xdg_output; + struct render_output_info *info = data; + snprintf(info->name, sizeof(info->name), "%s", name); + + if (strcmp(name, S.target.desktop) == 0) { + S.target_output = info->output; + S.output_found = true; + S.output_width = info->width; + S.output_height = info->height; + S.output_scale = info->scale > 0 ? info->scale : 1; + } +} + +static void render_xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { + (void)data; (void)xdg_output; (void)x; (void)y; +} + +static void render_xdg_output_handle_logical_size(void *data, + struct zxdg_output_v1 *xdg_output, int32_t w, int32_t h) { + (void)xdg_output; + struct render_output_info *info = data; + info->width = w; + info->height = h; + if (strcmp(info->name, S.target.desktop) == 0) { + S.output_width = w; + S.output_height = h; + } +} + +static void render_xdg_output_handle_done(void *data, + struct zxdg_output_v1 *xdg_output) { + (void)data; (void)xdg_output; +} + +static void render_xdg_output_handle_description(void *data, + struct zxdg_output_v1 *xdg_output, const char *desc) { + (void)data; (void)xdg_output; (void)desc; +} + +static const struct zxdg_output_v1_listener render_xdg_output_listener = { + .logical_position = render_xdg_output_handle_logical_position, + .logical_size = render_xdg_output_handle_logical_size, + .done = render_xdg_output_handle_done, + .name = render_xdg_output_handle_name, + .description = render_xdg_output_handle_description, +}; + +static void render_output_handle_geometry(void *data, struct wl_output *output, + int32_t x, int32_t y, int32_t pw, int32_t ph, int32_t subpixel, + const char *make, const char *model, int32_t transform) { + (void)data; (void)output; (void)x; (void)y; + (void)pw; (void)ph; (void)subpixel; + (void)make; (void)model; (void)transform; +} + +static void render_output_handle_mode(void *data, struct wl_output *output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + (void)output; (void)refresh; + struct render_output_info *info = data; + if (flags & WL_OUTPUT_MODE_CURRENT) { + info->width = width; + info->height = height; + } +} + +static void render_output_handle_scale(void *data, struct wl_output *output, + int32_t factor) { + (void)output; + struct render_output_info *info = data; + info->scale = factor; +} + +static void render_output_handle_done(void *data, struct wl_output *output) { + (void)data; (void)output; +} + +static void render_output_handle_name(void *data, struct wl_output *output, + const char *name) { + (void)output; + struct render_output_info *info = data; + snprintf(info->name, sizeof(info->name), "%s", name); + + if (strcmp(name, S.target.desktop) == 0) { + S.target_output = info->output; + S.output_found = true; + S.output_width = info->width; + S.output_height = info->height; + S.output_scale = info->scale > 0 ? info->scale : 1; + } +} + +static void render_output_handle_description(void *data, struct wl_output *output, + const char *desc) { + (void)data; (void)output; (void)desc; +} + +static const struct wl_output_listener render_output_listener = { + .geometry = render_output_handle_geometry, + .mode = render_output_handle_mode, + .done = render_output_handle_done, + .scale = render_output_handle_scale, + .name = render_output_handle_name, + .description = render_output_handle_description, +}; + +/* ── Layer surface events ─────────────────────────────────────────── */ + +static void render_layer_surface_configure(void *data, + struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) { + (void)data; + S.configure_serial = serial; + S.surface_width = width ? (int)width : S.output_width; + S.surface_height = height ? (int)height : S.output_height; + S.configured = true; + S.surface_dirty = true; + + zwlr_layer_surface_v1_ack_configure(surface, serial); +} + +static void render_layer_surface_closed(void *data, + struct zwlr_layer_surface_v1 *surface) { + (void)data; (void)surface; + S.closed = true; +} + +static const struct zwlr_layer_surface_v1_listener render_layer_surface_listener = { + .configure = render_layer_surface_configure, + .closed = render_layer_surface_closed, +}; + +/* ── Registry ─────────────────────────────────────────────────────── */ + +static void render_registry_handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + (void)data; + + if (strcmp(interface, wl_compositor_interface.name) == 0) { + S.compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 4); + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + S.layer_shell = wl_registry_bind(registry, name, + &zwlr_layer_shell_v1_interface, version < 3 ? version : 3); + } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { + S.xdg_output_manager = wl_registry_bind(registry, name, + &zxdg_output_manager_v1_interface, version < 3 ? version : 3); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + struct render_output_info *info = calloc(1, sizeof(*info)); + info->output = wl_registry_bind(registry, name, + &wl_output_interface, version < 4 ? version : 4); + info->scale = 1; + wl_output_add_listener(info->output, &render_output_listener, info); + wl_list_insert(&render_output_list, &info->link); + } +} + +static void render_registry_handle_global_remove(void *data, + struct wl_registry *registry, uint32_t name) { + (void)data; (void)registry; (void)name; +} + +static const struct wl_registry_listener render_registry_listener = { + .global = render_registry_handle_global, + .global_remove = render_registry_handle_global_remove, +}; + +/* ── Renderer signal handling ─────────────────────────────────────── */ + +static void render_sig_handler(int sig) { + (void)sig; + S.stop = 1; +} + +static void render_sigusr1_handler(int sig) { + (void)sig; + S.fade_out_requested = 1; +} + +static void render_black_handler(int sig) { (void)sig; S.black_requested = 1; } + +static void render_report(void) { + int power = S.power_fd >= 0; + if (S.report_fd < 0 || (S.reported_phase == (int)S.phase && S.reported_power == power && + S.reported_revision == S.applied_revision && S.reported_error == S.live_error)) return; + dr_report_packet p = {.magic=DR_LIVE_MAGIC, .kind=DR_REPORT, .revision=S.applied_revision, + .phase=S.phase, .power=power, .error=S.live_error, .font_size=S.font_size, .depth_layers=S.num_depth_layers}; + if (send(S.report_fd, &p, sizeof(p), MSG_DONTWAIT | MSG_NOSIGNAL) == sizeof(p)) { + S.reported_phase = S.phase; S.reported_power = power; S.reported_revision = S.applied_revision; S.reported_error = S.live_error; + } +} + +static void render_adaptive_handler(int sig) { + (void)sig; + S.adaptive_reload = 1; +} + +/* A black surface needs no animation. Atomically unblock wake signals while + * waiting so an activity signal cannot be lost between checking and sleeping. */ +static void render_dispatch_events(void) { + render_live_receive(); + sigset_t wake_signals, previous_mask; + sigemptyset(&wake_signals); + sigaddset(&wake_signals, SIGINT); + sigaddset(&wake_signals, SIGTERM); + sigaddset(&wake_signals, SIGUSR1); + sigaddset(&wake_signals, SIGRTMIN + 1); + if (S.adaptive_color) sigaddset(&wake_signals, SIGRTMIN); + sigprocmask(SIG_BLOCK, &wake_signals, &previous_mask); + + if (wl_display_prepare_read(S.display) == 0) { + struct pollfd pfds[2] = {{.fd=wl_display_get_fd(S.display), .events=POLLIN}, {.fd=S.report_fd, .events=POLLIN}}; +#define pfd pfds[0] + if (wl_display_flush(S.display) < 0) { + if (errno == EAGAIN) pfd.events |= POLLOUT; + else S.stop = 1; + } + bool wait = S.phase == PHASE_BLACK && !S.surface_dirty && + !S.stop && !S.closed && !S.fade_out_requested && !S.adaptive_reload && !S.black_requested; + struct timespec immediate = {0}; + int ready = ppoll(pfds, 2, wait ? NULL : &immediate, &previous_mask); + int poll_error = errno; + if (ready > 0 && (pfd.revents & POLLIN)) { + if (wl_display_read_events(S.display) < 0) S.stop = 1; + } else { + wl_display_cancel_read(S.display); + } + if ((ready < 0 && poll_error != EINTR) || + (ready > 0 && (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)))) + S.stop = 1; + } + sigprocmask(SIG_SETMASK, &previous_mask, NULL); + if (wl_display_dispatch_pending(S.display) < 0) S.stop = 1; + render_live_receive(); +#undef pfd +} + +static void render_note_black_background(void) { + if (!S.background_black) { + S.background_black = true; + S.black_start_time = render_monotime(); + } +} + +static bool is_adaptive_color(const char *name) { + return !strcasecmp(name, "adaptive") || !strcasecmp(name, "adaptive-light") || + !strcasecmp(name, "adaptive-dark"); +} + +static void render_load_adaptive_snapshot(void) { + S.adaptive_reload = 0; + const char *runtime = getenv("XDG_RUNTIME_DIR"); + if (!runtime || !runtime[0]) return; + char path[512]; + snprintf(path, sizeof(path), "%s/digitalrain/palette.json", runtime); + FILE *f = fopen(path, "r"); + if (!f) return; + fseek(f, 0, SEEK_END); + long size = ftell(f); + rewind(f); + if (size <= 0 || size > 65536) { fclose(f); return; } + char *text = malloc((size_t)size + 1); + if (!text) { fclose(f); return; } + if (fread(text, 1, (size_t)size, f) != (size_t)size) { + free(text); fclose(f); return; + } + text[size] = '\0'; + fclose(f); + struct json_object *root = json_tokener_parse(text); + free(text); + struct json_object *value = NULL; + if (!root || !json_object_object_get_ex(root, S.target_name, &value)) { + if (root) json_object_put(root); + return; + } + struct json_object *desktop = NULL, *black = NULL; + if (json_object_object_get_ex(value, "color", &desktop) && + json_object_object_get_ex(value, "black-color", &black) && + json_object_is_type(desktop, json_type_string) && + json_object_is_type(black, json_type_string)) { + S.adaptive_desktop_color = render_parse_hex(json_object_get_string(desktop)); + S.adaptive_black_color = render_parse_hex(json_object_get_string(black)); + } + json_object_put(root); +} + +static void ensure_wayland_display(void) { + const char *display = getenv("WAYLAND_DISPLAY"); + const char *runtime_dir = getenv("XDG_RUNTIME_DIR"); + + if ((display && display[0] != '\0') || !runtime_dir || runtime_dir[0] == '\0') + return; + + DIR *dir = opendir(runtime_dir); + if (!dir) + return; + + struct dirent *ent; + while ((ent = readdir(dir)) != NULL) { + if (strncmp(ent->d_name, "wayland-", 8) != 0) + continue; + + char path[512]; + struct stat st; + snprintf(path, sizeof(path), "%s/%s", runtime_dir, ent->d_name); + if (stat(path, &st) == 0 && S_ISSOCK(st.st_mode)) { + setenv("WAYLAND_DISPLAY", ent->d_name, 0); + break; + } + } + + closedir(dir); +} + +/* ── Renderer main ────────────────────────────────────────────────── */ + +/* Reuse the manager's Wayfire transport, retaining this one connection until + * wake-up. The compositor releases the hide even if this process is killed. */ +static int mgr_ipc_connect(void); +static int mgr_ipc_send_method(int fd, const char *method, const char *data_json); +static int mgr_ipc_recv(int fd, char **out_payload); + +static bool render_desktop_request(int fd, const char *method, const char *data) { + if (mgr_ipc_send_method(fd, method, data) < 0) return false; + char *text = NULL; + if (mgr_ipc_recv(fd, &text) < 0) return false; + struct json_object *response = json_tokener_parse(text), *result = NULL; + free(text); + bool ok = response && json_object_object_get_ex(response, "result", &result) && + json_object_is_type(result, json_type_string) && + strcmp(json_object_get_string(result), "ok") == 0; + if (response) json_object_put(response); + return ok; +} + +static void render_restore_power(void) { + if (S.power_fd < 0) return; + /* Wait for the restore before drawing: an off output may not send frames. */ + render_desktop_request(S.power_fd, "digitalrain/power_on", "{}"); + close(S.power_fd); + S.power_fd = -1; + S.surface_dirty = true; +} + +static void render_power_off(void) { + if (!S.power_off || S.power_attempted || S.fade_out_requested || S.stop) return; + S.power_attempted = true; + int fd = mgr_ipc_connect(); + if (fd >= 0) { + struct json_object *data = json_object_new_object(); + json_object_object_add(data, "output", json_object_new_string(S.target_name)); + bool ok = render_desktop_request(fd, "digitalrain/power_off", + json_object_to_json_string_ext(data, JSON_C_TO_STRING_PLAIN)); + json_object_put(data); + if (ok) { + S.power_fd = fd; + fprintf(stderr, "[%s] Monitor powered off until activity\n", S.target_name); + return; + } + close(fd); + } + fprintf(stderr, "[%s] Monitor power-off unavailable; enable digitalrain-power in Wayfire; retaining black output\n", + S.target_name); +} + +static void render_release_desktop(void) { + if (S.desktop_fd >= 0) { + close(S.desktop_fd); + S.desktop_fd = -1; + } +} + +static void render_resume_desktop(void) { + if (S.desktop_fd < 0 || S.desktop_resuming) return; + S.desktop_resuming = true; + if (!render_desktop_request(S.desktop_fd, "digitalrain/show_desktop", "{}")) + render_release_desktop(); +} + +static void render_hide_desktop(void) { + if (!S.transparent || !S.hide_desktop) return; + int fd = mgr_ipc_connect(); + if (fd < 0) goto unavailable; + struct timeval timeout = {.tv_sec = 2}; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + struct json_object *data = json_object_new_object(); + json_object_object_add(data, "output", json_object_new_string(S.target_name)); + json_object_object_add(data, "fade_ms", json_object_new_int(S.fade_frames * 1000 / TARGET_FPS)); + bool ok = render_desktop_request(fd, "digitalrain/hide_desktop", + json_object_to_json_string_ext(data, JSON_C_TO_STRING_PLAIN)); + json_object_put(data); + if (ok) { + S.desktop_fd = fd; + return; + } + close(fd); +unavailable: + fprintf(stderr, "HIDE_DESKTOP unavailable: enable Wayfire's digitalrain-desktop plugin; continuing with desktop visible\n"); +} + +#include "digitalrain-live-render.h" + +static int renderer_main(int argc, char **argv) { + S.report_fd = S.reported_phase = S.reported_power = -1; + signal(SIGRTMIN + 1, render_black_handler); + /* Defaults */ + S.target_name[0] = '\0'; + S.font_size = DEFAULT_FONT_SIZE; + S.base_color = (rgb_t){ 0.0f, 1.0f, 0.0f }; + S.output_scale = 1; + S.min_speed = DEFAULT_MIN_SPEED; + S.max_speed = DEFAULT_MAX_SPEED; + S.avg_speed = DEFAULT_AVG_SPEED; + S.min_trail = DEFAULT_MIN_TRAIL; + S.max_trail = DEFAULT_MAX_TRAIL; + S.min_density = DEFAULT_MIN_DENSITY; + S.max_density = DEFAULT_MAX_DENSITY; + S.layer_near_density = LAYER_NEAR_DENSITY; + S.layer_far_density = LAYER_FAR_DENSITY; + S.head_hold_frames = DEFAULT_HEAD_HOLD_FRAMES; + S.head_mutation_chance = DEFAULT_HEAD_MUTATION_CHANCE; + S.head_white_min = DEFAULT_HEAD_WHITE_MIN; + S.head_white_max = DEFAULT_HEAD_WHITE_MAX; + S.num_depth_layers = DEFAULT_DEPTH_LAYERS; + S.fade_frames = DEFAULT_FADE_FRAMES; + S.transparent_timeout = 0.0; + S.black_rain_timeout = 0.0; + S.transparent = false; + S.hide_desktop = false; + S.desktop_fd = -1; + S.power_fd = -1; + S.desktop_resuming = false; + S.adaptive_reload = 0; + + const char *color_name = "green"; + + /* Parse args (skip --render which was already consumed) */ + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--render") == 0) { + continue; /* already handled */ + } else if (strcmp(argv[i], "--ambient") == 0) { + S.ambient = true; + } else if (strcmp(argv[i], "--report-fd") == 0 && i + 1 < argc) { + S.report_fd = atoi(argv[++i]); + } else if (strcmp(argv[i], "--output") == 0 && i + 1 < argc) { + snprintf(S.target_name, sizeof(S.target_name), "%s", argv[++i]); + } else if (strcmp(argv[i], "--color") == 0 && i + 1 < argc) { + color_name = argv[++i]; + } else if (strcmp(argv[i], "--font-size") == 0 && i + 1 < argc) { + S.font_size = atoi(argv[++i]); + if (S.font_size < MIN_FONT_SIZE) S.font_size = MIN_FONT_SIZE; + if (S.font_size > MAX_FONT_SIZE) S.font_size = MAX_FONT_SIZE; + } else if (strcmp(argv[i], "--min-speed") == 0 && i + 1 < argc) { + S.min_speed = atof(argv[++i]); + } else if (strcmp(argv[i], "--max-speed") == 0 && i + 1 < argc) { + S.max_speed = atof(argv[++i]); + } else if (strcmp(argv[i], "--avg-speed") == 0 && i + 1 < argc) { + S.avg_speed = atof(argv[++i]); + } else if (strcmp(argv[i], "--min-trail") == 0 && i + 1 < argc) { + S.min_trail = atoi(argv[++i]); + } else if (strcmp(argv[i], "--max-trail") == 0 && i + 1 < argc) { + S.max_trail = atoi(argv[++i]); + } else if (strcmp(argv[i], "--min-density") == 0 && i + 1 < argc) { + S.min_density = atof(argv[++i]); + } else if (strcmp(argv[i], "--max-density") == 0 && i + 1 < argc) { + S.max_density = atof(argv[++i]); + } else if (strcmp(argv[i], "--speed") == 0 && i + 1 < argc) { + double mult = atof(argv[++i]); + if (mult < 0.1) mult = 0.1; + if (mult > 5.0) mult = 5.0; + S.min_speed *= mult; + S.max_speed *= mult; + } else if (strcmp(argv[i], "--depth-layers") == 0 && i + 1 < argc) { + S.num_depth_layers = atoi(argv[++i]); + if (S.num_depth_layers < MIN_DEPTH_LAYERS) + S.num_depth_layers = MIN_DEPTH_LAYERS; + if (S.num_depth_layers > MAX_DEPTH_LAYERS) + S.num_depth_layers = MAX_DEPTH_LAYERS; + } else if (strcmp(argv[i], "--layer-near-density") == 0 && i + 1 < argc) { + S.layer_near_density = atof(argv[++i]); + } else if (strcmp(argv[i], "--layer-far-density") == 0 && i + 1 < argc) { + S.layer_far_density = atof(argv[++i]); + } else if (strcmp(argv[i], "--head-hold-frames") == 0 && i + 1 < argc) { + S.head_hold_frames = atoi(argv[++i]); + if (S.head_hold_frames < 0) S.head_hold_frames = 0; + if (S.head_hold_frames > 120) S.head_hold_frames = 120; + } else if (strcmp(argv[i], "--head-mutation-chance") == 0 && i + 1 < argc) { + S.head_mutation_chance = atof(argv[++i]); + if (S.head_mutation_chance < 0.0) S.head_mutation_chance = 0.0; + if (S.head_mutation_chance > 1.0) S.head_mutation_chance = 1.0; + } else if (strcmp(argv[i], "--head-white-min") == 0 && i + 1 < argc) { + S.head_white_min = atof(argv[++i]); + } else if (strcmp(argv[i], "--head-white-max") == 0 && i + 1 < argc) { + S.head_white_max = atof(argv[++i]); + } else if (strcmp(argv[i], "--fade-frames") == 0 && i + 1 < argc) { + S.fade_frames = atoi(argv[++i]); + if (S.fade_frames < 1) S.fade_frames = 1; + if (S.fade_frames > 300) S.fade_frames = 300; + } else if (strcmp(argv[i], "--transparent") == 0) { + S.transparent = true; + } else if (strcmp(argv[i], "--hide-desktop") == 0) { + S.hide_desktop = true; + } else if (strcmp(argv[i], "--power-off") == 0 && i + 1 < argc) { + S.power_off = atoi(argv[++i]) != 0; + } else if (strcmp(argv[i], "--transparent-timeout") == 0 && i + 1 < argc) { + S.transparent_timeout = atof(argv[++i]); + if (S.transparent_timeout < 0.0) S.transparent_timeout = 0.0; + } else if (strcmp(argv[i], "--black-rain-timeout") == 0 && i + 1 < argc) { + S.black_rain_timeout = atof(argv[++i]); + if (S.black_rain_timeout < 0.0) S.black_rain_timeout = 0.0; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + fprintf(stderr, + "Usage: %s --render --output <name> [--color <color>] " + "[--font-size <size>]\n" + " [--min-speed <val>] [--max-speed <val>] " + "[--min-trail <val>] [--max-trail <val>]\n" + " [--min-density <val>] [--max-density <val>]\n" + " [--head-hold-frames <frames>] " + "[--head-mutation-chance <0..1>]\n" + " [--head-white-min <0..1>] [--head-white-max <0..1>]\n" + " [--speed <multiplier>] [--depth-layers <N>]\n" + " [--transparent]\n" + " [--hide-desktop]\n" + " [--transparent-timeout <seconds>]\n" + " [--black-rain-timeout <seconds>]\n" + " [--power-off <0|1>] (after the black rain timer)\n" + "\n" + "Colors: green, red, blue, white, yellow, cyan, magenta, adaptive, random\n" + "--speed scales both min/max speed (e.g. --speed 2.0 = double speed)\n" + "--depth-layers: number of parallax layers (1-%d, default %d)\n" + "--transparent: keep the desktop visible behind the matrix rain\n" + "--hide-desktop: with --transparent, hide apps/panels and show wallpaper only (Wayfire)\n" + "--black-rain-timeout: fade out rain after N seconds on black; 0 disables\n" + "Default font-size: %d\n" + "Speed range: %.1f - %.1f Trail range: %d - %d " + "Density range: %.2f - %.2f\n", + argv[0], + MAX_DEPTH_LAYERS, DEFAULT_DEPTH_LAYERS, + DEFAULT_FONT_SIZE, + DEFAULT_MIN_SPEED, DEFAULT_MAX_SPEED, + DEFAULT_MIN_TRAIL, DEFAULT_MAX_TRAIL, + DEFAULT_MIN_DENSITY, DEFAULT_MAX_DENSITY); + return 0; + } else { + fprintf(stderr, "Unknown option: %s\n", argv[i]); + return 1; + } + } + + /* Clamp values */ + if (S.min_speed < 0.001) S.min_speed = 0.001; + if (S.max_speed < S.min_speed) S.max_speed = S.min_speed; + if (S.min_trail < 1) S.min_trail = 1; + if (S.max_trail < S.min_trail) S.max_trail = S.min_trail; + if (S.min_density < 0) S.min_density = 0; + if (S.min_density > 1.0) S.min_density = 1.0; + if (S.max_density < S.min_density) S.max_density = S.min_density; + if (S.max_density > 1.0) S.max_density = 1.0; + if (S.head_white_min < 0.0) S.head_white_min = 0.0; + if (S.head_white_min > 1.0) S.head_white_min = 1.0; + if (S.head_white_max < 0.0) S.head_white_max = 0.0; + if (S.head_white_max > 1.0) S.head_white_max = 1.0; + if (S.head_white_max < S.head_white_min) { + double tmp = S.head_white_min; + S.head_white_min = S.head_white_max; + S.head_white_max = tmp; + } + + if (S.target_name[0] == '\0') { + fprintf(stderr, "Error: --output is required in render mode\n"); + return 1; + } + + srand((unsigned)time(NULL) ^ (unsigned)getpid()); + + S.base_color = render_lookup_color(color_name); + bool adaptive_color = is_adaptive_color(color_name); + S.adaptive_color = adaptive_color; + if (adaptive_color) { + S.base_color = (rgb_t){ 1.0f, 1.0f, 1.0f }; + S.adaptive_desktop_color = S.adaptive_black_color = S.base_color; + render_load_adaptive_snapshot(); + S.base_color = S.transparent ? S.adaptive_desktop_color : S.adaptive_black_color; + } + if (strcasecmp(color_name, "random_trail_named") == 0) + S.color_mode = COLOR_RANDOM; + else if (strcasecmp(color_name, "random_trail_hex") == 0) + S.color_mode = COLOR_RANDOM_HEX; + else + S.color_mode = COLOR_FIXED; + + /* Signal handlers */ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = render_sig_handler; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + struct sigaction sa_usr1; + memset(&sa_usr1, 0, sizeof(sa_usr1)); + sa_usr1.sa_handler = render_sigusr1_handler; + sigemptyset(&sa_usr1.sa_mask); + sigaction(SIGUSR1, &sa_usr1, NULL); + + { + struct sigaction sa_adaptive; + memset(&sa_adaptive, 0, sizeof(sa_adaptive)); + sigemptyset(&sa_adaptive.sa_mask); + sa_adaptive.sa_handler = render_adaptive_handler; + sigaction(SIGRTMIN, &sa_adaptive, NULL); + } + + signal(SIGPIPE, SIG_IGN); + + snprintf(S.target.desktop, sizeof(S.target.desktop), "%s", S.target_name); + saver_target_t targets[MAX_OUTPUTS]; + struct json_object *outputs = mgr_ipc_request("window-rules/list-outputs", "{}"); + int count = parse_targets(outputs, targets); + for (int i = 0; i < count; ++i) + if (!strcmp(targets[i].name, S.target_name)) S.target = targets[i]; + if (outputs) json_object_put(outputs); + + /* Connect to Wayland */ + ensure_wayland_display(); + S.display = wl_display_connect(NULL); + if (!S.display) { + fprintf(stderr, "Cannot connect to Wayland display\n"); + return 1; + } + + wl_list_init(&render_output_list); + + S.registry = wl_display_get_registry(S.display); + wl_registry_add_listener(S.registry, &render_registry_listener, NULL); + + wl_display_roundtrip(S.display); + + if (S.xdg_output_manager) { + struct render_output_info *info; + wl_list_for_each(info, &render_output_list, link) { + info->xdg_output = zxdg_output_manager_v1_get_xdg_output( + S.xdg_output_manager, info->output); + zxdg_output_v1_add_listener(info->xdg_output, + &render_xdg_output_listener, info); + } + } + + wl_display_roundtrip(S.display); + + if (!S.output_found || !S.target_output) { + fprintf(stderr, "Output '%s' not found. Available outputs:\n", S.target_name); + struct render_output_info *info; + wl_list_for_each(info, &render_output_list, link) { + fprintf(stderr, " %s (%dx%d)\n", info->name, info->width, info->height); + } + wl_display_disconnect(S.display); + return 1; + } + + if (!S.compositor || !S.layer_shell) { + fprintf(stderr, "Missing required Wayland interfaces " + "(compositor=%p layer_shell=%p)\n", + (void*)S.compositor, (void*)S.layer_shell); + wl_display_disconnect(S.display); + return 1; + } + + fprintf(stderr, "Target output: %s (%dx%d, scale=%d, depth_layers=%d, head_hold=%d, head_mutation=%.2f, head_white=%.2f-%.2f, speed=%.1f-%.1f, trail=%d-%d, density=%.2f-%.2f)\n", + S.target_name, S.output_width, S.output_height, S.output_scale, + S.num_depth_layers, S.head_hold_frames, S.head_mutation_chance, + S.head_white_min, S.head_white_max, + S.min_speed, S.max_speed, S.min_trail, S.max_trail, + S.min_density, S.max_density); + + /* Create surface */ + S.surface = wl_compositor_create_surface(S.compositor); + + S.layer_surface = zwlr_layer_shell_v1_get_layer_surface( + S.layer_shell, S.surface, S.target_output, + S.ambient ? ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND : ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + S.ambient ? "ambient-matrix" : "screensaver"); + + if (S.target.region) { + zwlr_layer_surface_v1_set_anchor(S.layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); + zwlr_layer_surface_v1_set_size(S.layer_surface, S.target.width, S.target.height); + zwlr_layer_surface_v1_set_margin(S.layer_surface, S.target.y, 0, 0, S.target.x); + } else { + zwlr_layer_surface_v1_set_anchor(S.layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT); + zwlr_layer_surface_v1_set_size(S.layer_surface, 0, 0); + } + zwlr_layer_surface_v1_set_exclusive_zone(S.layer_surface, -1); + zwlr_layer_surface_v1_set_keyboard_interactivity(S.layer_surface, 0); + + /* Keep the visual overlay from receiving pointer/touch input. */ + struct wl_region *empty_input = wl_compositor_create_region(S.compositor); + wl_surface_set_input_region(S.surface, empty_input); + wl_region_destroy(empty_input); + + zwlr_layer_surface_v1_add_listener(S.layer_surface, + &render_layer_surface_listener, NULL); + + wl_surface_commit(S.surface); + wl_display_roundtrip(S.display); + + if (!S.configured) { + fprintf(stderr, "Layer surface was not configured\n"); + wl_display_disconnect(S.display); + return 1; + } + + fprintf(stderr, "Surface configured: %dx%d\n", S.surface_width, S.surface_height); + + /* Initialize EGL */ + if (!render_init_egl()) { + fprintf(stderr, "Failed to initialize EGL\n"); + wl_display_disconnect(S.display); + return 1; + } + + /* Initialize shaders */ + if (!render_init_shaders()) { + fprintf(stderr, "Failed to initialize shaders\n"); + wl_display_disconnect(S.display); + return 1; + } + + render_set_projection(S.surface_width, S.surface_height); + + /* Initialize FreeType and build glyph atlas */ + int atlas_font_size = (int)(S.font_size * LAYER_NEAR_FONT_SCALE + 0.5); + if (atlas_font_size < S.font_size) atlas_font_size = S.font_size; + + if (!render_init_freetype(atlas_font_size)) { + fprintf(stderr, "Failed to initialize FreeType\n"); + wl_display_disconnect(S.display); + return 1; + } + + if (!render_build_atlas(atlas_font_size)) { + fprintf(stderr, "Failed to build glyph atlas\n"); + wl_display_disconnect(S.display); + return 1; + } + + fprintf(stderr, "Initializing %d depth layers (font_size=%d, atlas_size=%d)\n", + S.num_depth_layers, S.font_size, atlas_font_size); + + render_init_matrix(S.surface_width, S.surface_height); + + for (int l = 0; l < S.num_depth_layers; l++) { + depth_layer_t *dl = &S.layers[l]; + fprintf(stderr, "Layer %d: font_scale=%.2f cell=%dx%d grid=%dx%d opacity=%.2f speed=%.2fx density=%.0f%%\n", + l, dl->font_scale, dl->cell_w, dl->cell_h, + dl->cols, dl->rows, dl->opacity, dl->speed_scale, + dl->density * 100.0); + } + fprintf(stderr, "Starting matrix rain with depth layers (OpenGL ES 2.0)\n"); + + /* Initialize phase state. Transparent mode skips the black overlay. */ + S.phase = S.transparent ? PHASE_FADE_IN : PHASE_FADE_TO_BLACK; + S.phase_frame = 0; + S.surface_alpha = 0.0f; + S.fade_opacity = 0.0f; + S.render_start_time = render_monotime(); + S.blacken_frames = 0; + S.background_black = false; + + render_hide_desktop(); + + /* Main render loop */ + struct timespec ts_start, ts_end; + while (!S.stop && !S.closed) { + clock_gettime(CLOCK_MONOTONIC, &ts_start); + + render_dispatch_events(); + + if (S.stop || S.closed) break; + if (S.black_requested) { + S.black_requested = 0; S.phase = PHASE_BLACK; S.transparent = false; + S.surface_dirty = true; S.fade_opacity = 0; + } + if (S.fade_out_requested) { + render_restore_power(); + render_resume_desktop(); + } + if (S.fade_out_requested && + (S.phase == PHASE_BLACK || S.phase == PHASE_FADE_RAIN_TO_BLACK)) { + if (S.phase == PHASE_BLACK) { + S.phase = PHASE_FADE_TO_DESKTOP; + S.phase_frame = S.fade_frames; + } else { + /* Continue fading from the current rain opacity on activity. */ + S.phase = PHASE_FADE_OUT; + } + S.fade_out_requested = 0; + } + + if (S.adaptive_color && S.adaptive_reload) + render_load_adaptive_snapshot(); + + if (S.phase == PHASE_BLACK && !S.surface_dirty) continue; + + /* Keep configure events pending while asleep; swap may wait for a + * frame callback that a powered-off standalone output cannot deliver. */ + if (S.phase == PHASE_BLACK && S.power_fd >= 0) { + S.surface_dirty = false; + continue; + } + + if (S.adaptive_color && S.phase != PHASE_BLACK) { + /* Match the palette to this renderer's own background transition. */ + float t = S.transparent ? 0.0f : + (S.blacken_frames > 0 ? 1.0f - (float)S.blacken_frames / S.fade_frames : 1.0f); + S.base_color = (rgb_t){ + S.adaptive_desktop_color.r * (1.0f - t) + S.adaptive_black_color.r * t, + S.adaptive_desktop_color.g * (1.0f - t) + S.adaptive_black_color.g * t, + S.adaptive_desktop_color.b * (1.0f - t) + S.adaptive_black_color.b * t, + }; + for (int l = 0; l < S.num_depth_layers; l++) { + for (int c = 0; c < S.layers[l].cols; c++) + S.layers[l].columns[c].color = S.base_color; + } + } + + if (S.surface_dirty) { + wl_egl_window_resize(S.egl_window, S.surface_width, S.surface_height, 0, 0); + render_set_projection(S.surface_width, S.surface_height); + S.surface_dirty = false; + } + glViewport(0, 0, S.surface_width, S.surface_height); + float background_alpha; + + switch (S.phase) { + case PHASE_FADE_TO_BLACK: + S.phase_frame++; + S.surface_alpha = (float)S.phase_frame / S.fade_frames; + if (S.surface_alpha >= 1.0f) { + S.surface_alpha = 1.0f; + render_note_black_background(); + S.phase = PHASE_FADE_IN; + S.phase_frame = 0; + } + /* Clear with transitioning alpha, don't render rain */ + glClearColor(0.0f, 0.0f, 0.0f, + S.transparent ? 0.0f : S.surface_alpha); + glClear(GL_COLOR_BUFFER_BIT); + break; + + case PHASE_FADE_IN: + S.phase_frame++; + S.fade_opacity = (float)S.phase_frame / S.fade_frames; + if (S.fade_opacity >= 1.0f) { + S.fade_opacity = 1.0f; + S.phase = PHASE_RUNNING; + } + glClearColor(0.0f, 0.0f, 0.0f, S.transparent ? 0.0f : 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + render_tick_matrix(); + render_frame(); + break; + + case PHASE_RUNNING: + if (S.transparent && S.transparent_timeout > 0.0 && + render_monotime() - S.render_start_time >= S.transparent_timeout) { + S.transparent = false; + S.blacken_frames = S.fade_frames; + S.background_black = false; + } + if (S.fade_out_requested) { + S.phase = PHASE_FADE_OUT; + S.phase_frame = S.fade_frames; + S.fade_out_requested = 0; + } + background_alpha = S.transparent ? 0.0f : + (S.blacken_frames > 0 ? + 1.0f - (float)S.blacken_frames / S.fade_frames : 1.0f); + if (background_alpha >= 1.0f) render_note_black_background(); + if (S.phase == PHASE_RUNNING && S.background_black && + S.black_rain_timeout > 0.0 && + render_monotime() - S.black_start_time >= S.black_rain_timeout) { + S.phase = PHASE_FADE_RAIN_TO_BLACK; + S.phase_frame = S.fade_frames; + } + glClearColor(0.0f, 0.0f, 0.0f, background_alpha); + glClear(GL_COLOR_BUFFER_BIT); + render_tick_matrix(); + render_frame(); + if (S.blacken_frames > 0) { + S.blacken_frames--; + } + break; + + case PHASE_FADE_RAIN_TO_BLACK: + case PHASE_FADE_OUT: + S.phase_frame--; + S.fade_opacity = (float)S.phase_frame / S.fade_frames; + if (S.fade_opacity <= 0.0f) { + S.fade_opacity = 0.0f; + S.phase = S.phase == PHASE_FADE_RAIN_TO_BLACK ? + PHASE_BLACK : PHASE_FADE_TO_DESKTOP; + S.phase_frame = S.fade_frames; + if (S.phase == PHASE_BLACK) + fprintf(stderr, "[%s] Rain stopped; retaining black output\n", S.target_name); + } + glClearColor(0.0f, 0.0f, 0.0f, S.transparent ? 0.0f : 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + render_tick_matrix(); + render_frame(); + break; + + case PHASE_BLACK: + /* Repaint only on configure; otherwise keep the committed buffer. */ + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + break; + + case PHASE_FADE_TO_DESKTOP: + S.phase_frame--; + S.surface_alpha = (float)S.phase_frame / S.fade_frames; + if (S.surface_alpha <= 0.0f) { + S.surface_alpha = 0.0f; + S.stop = 1; /* exit */ + } + glClearColor(0.0f, 0.0f, 0.0f, + S.transparent ? 0.0f : S.surface_alpha); + glClear(GL_COLOR_BUFFER_BIT); + /* Don't render rain — just fading black overlay */ + break; + } + + eglSwapBuffers(S.egl_display, S.egl_surface); + render_report(); + if (S.phase == PHASE_BLACK) render_power_off(); + render_report(); + + clock_gettime(CLOCK_MONOTONIC, &ts_end); + long elapsed_us = (ts_end.tv_sec - ts_start.tv_sec) * 1000000L + + (ts_end.tv_nsec - ts_start.tv_nsec) / 1000; + long sleep_us = FRAME_INTERVAL_US - elapsed_us; + if (sleep_us > 0) { + usleep((useconds_t)sleep_us); + } + } + + fprintf(stderr, "Shutting down\n"); + render_restore_power(); + render_release_desktop(); + + /* Cleanup */ + free(S.vertex_buf); + if (S.vbo) glDeleteBuffers(1, &S.vbo); + if (S.shader_program) glDeleteProgram(S.shader_program); + if (S.atlas.texture) glDeleteTextures(1, &S.atlas.texture); + + if (S.egl_display != EGL_NO_DISPLAY) { + eglMakeCurrent(S.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (S.egl_surface != EGL_NO_SURFACE) + eglDestroySurface(S.egl_display, S.egl_surface); + if (S.egl_context != EGL_NO_CONTEXT) + eglDestroyContext(S.egl_display, S.egl_context); + eglTerminate(S.egl_display); + } + if (S.egl_window) wl_egl_window_destroy(S.egl_window); + + if (S.ft_face) FT_Done_Face(S.ft_face); + if (S.ft_library) FT_Done_FreeType(S.ft_library); + + if (S.layer_surface) zwlr_layer_surface_v1_destroy(S.layer_surface); + if (S.surface) wl_surface_destroy(S.surface); + for (int l = 0; l < S.num_depth_layers; l++) { + free(S.layers[l].columns); + free(S.layers[l].cells); + } + + struct render_output_info *info, *tmp; + wl_list_for_each_safe(info, tmp, &render_output_list, link) { + if (info->xdg_output) zxdg_output_v1_destroy(info->xdg_output); + wl_output_release(info->output); + wl_list_remove(&info->link); + free(info); + } + + if (S.xdg_output_manager) zxdg_output_manager_v1_destroy(S.xdg_output_manager); + if (S.registry) wl_registry_destroy(S.registry); + wl_display_disconnect(S.display); + + return 0; +} + +/* =================================================================== + * MANAGER MODE (Wayfire IPC idle blanking) + * =================================================================== */ + +/* ── Constants ─────────────────────────────────────────────────────── */ + +#define MAX_PATH_LEN 512 +#define MAX_CMD_LEN 1024 +#define MAX_PIDS 4 +#define WF_IPC_HEADER_LEN 4 +#define WF_IPC_MAX_PAYLOAD (16 * 1024 * 1024) +#define CURSOR_POLL_INTERVAL 0.25 + +static const char *MGR_COLORS[] = { + "green", "red", "blue", "white", "yellow", "cyan", "magenta" +}; +#define MGR_NUM_COLORS 7 + +/* ── Data structures ───────────────────────────────────────────────── */ + +typedef struct { + char name[MAX_OUTPUT_NAME]; + saver_target_t target; + double last_input; + char state[8]; + pid_t pids[MAX_PIDS]; + int pid_count; + int timeout_override; + int power_off_override; + int video_inhibited; + int report_fd, render_phase; + bool powered_off; + double preview_until; + unsigned sent_revision, applied_revision; + int live_error, applied_font, applied_depth; + dr_effect_packet sent_effects; + char color[32]; + bool palette_ready; + double adaptive_hue, adaptive_hue_time, adaptive_luminance; + /* per-output overrides; negative = use global */ + double min_speed, max_speed; + int min_trail, max_trail; + double min_density, max_density; +} mgr_output_info_t; + +typedef struct { + dr_config_t config; + int inhibitor_warned, manual_activation; + double last_adaptive_sample; + bool sampler_warned; + char config_file[MAX_PATH_LEN]; + double config_mtime; + /* State */ + mgr_output_info_t outputs[MAX_OUTPUTS]; + int output_count; + int refresh_outputs; + char focused_output[MAX_OUTPUT_NAME]; + double last_blank_time; + int seat_idle; + int wayfire_cursor_hidden; + int wayfire_cursor_warned; + volatile sig_atomic_t stop; + volatile sig_atomic_t activate_all; /* SIGUSR1: blank all outputs now */ + volatile sig_atomic_t lock_now; /* SIGUSR2: request native session lock */ + volatile sig_atomic_t adaptive_update; /* SIGRTMIN: request a fresh desktop sample */ + + /* Sync */ + pthread_mutex_t lock; + + /* Logging */ + FILE *log_fp; +} mgr_state_t; + +static mgr_state_t G; + +/* ── Logging ───────────────────────────────────────────────────────── */ + +static void mgr_log_msg(const char *level, const char *fmt, ...) { + time_t now = time(NULL); + struct tm tm; + char tbuf[64]; + localtime_r(&now, &tm); + strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", &tm); + + va_list ap; + + va_start(ap, fmt); + fprintf(stdout, "%s %s ", tbuf, level); + vfprintf(stdout, fmt, ap); + fputc('\n', stdout); + fflush(stdout); + va_end(ap); + + if (G.log_fp) { + va_start(ap, fmt); + fprintf(G.log_fp, "%s %s ", tbuf, level); + vfprintf(G.log_fp, fmt, ap); + fputc('\n', G.log_fp); + fflush(G.log_fp); + va_end(ap); + } +} + +#define LOG_INFO(...) mgr_log_msg("INFO", __VA_ARGS__) +#define LOG_WARNING(...) mgr_log_msg("WARNING", __VA_ARGS__) +#define LOG_ERROR(...) mgr_log_msg("ERROR", __VA_ARGS__) + +/* ── Utility ───────────────────────────────────────────────────────── */ + +static double mgr_monotime(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec / 1e9; +} + +static int mgr_pid_alive(pid_t pid) { + if (pid <= 0) return 0; + return kill(pid, 0) == 0; +} + +static void mgr_mkdirs(const char *path) { + char tmp[MAX_PATH_LEN]; + snprintf(tmp, sizeof(tmp), "%s", path); + for (char *p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + mkdir(tmp, 0755); + *p = '/'; + } + } + mkdir(tmp, 0755); +} + +static void mgr_runtime_path(char *dst, size_t dstsz, const char *name) { + const char *runtime_dir = getenv("XDG_RUNTIME_DIR"); + + if (runtime_dir && runtime_dir[0] != '\0') { + char dir[MAX_PATH_LEN]; + snprintf(dir, sizeof(dir), "%s/digitalrain", runtime_dir); + mgr_mkdirs(dir); + snprintf(dst, dstsz, "%s/%s", dir, name); + return; + } + + snprintf(dst, dstsz, "/tmp/digitalrain-%lu-%s", (unsigned long)getuid(), name); +} + +static void mgr_expand_tilde(const char *src, char *dst, size_t dstsz) { + if (src[0] == '~' && (src[1] == '/' || src[1] == '\0')) { + const char *home = getenv("HOME"); + if (home) + snprintf(dst, dstsz, "%s%s", home, src + 1); + else + snprintf(dst, dstsz, "%s", src); + } else { + snprintf(dst, dstsz, "%s", src); + } +} + +static const char *mgr_random_color(void) { + return MGR_COLORS[rand() % MGR_NUM_COLORS]; +} + +/* ── Wayfire IPC (direct socket) ──────────────────────────────────── */ + +static int mgr_wayfire_ipc_path(char *dst, size_t dstsz) { + const char *sock = getenv("WAYFIRE_SOCKET"); + if (!sock || sock[0] == '\0') + sock = getenv("_WAYFIRE_SOCKET"); + if (sock && sock[0] != '\0') { + snprintf(dst, dstsz, "%s", sock); + return 0; + } + + ensure_wayland_display(); + + const char *runtime_dir = getenv("XDG_RUNTIME_DIR"); + const char *display = getenv("WAYLAND_DISPLAY"); + if (runtime_dir && runtime_dir[0] != '\0' && + display && display[0] != '\0') { + snprintf(dst, dstsz, "%s/wayfire-%s-.socket", runtime_dir, display); + struct stat st; + if (stat(dst, &st) == 0 && S_ISSOCK(st.st_mode)) + return 0; + } + + if (!runtime_dir || runtime_dir[0] == '\0') + return -1; + + DIR *dir = opendir(runtime_dir); + if (!dir) + return -1; + + struct dirent *ent; + while ((ent = readdir(dir)) != NULL) { + const char *name = ent->d_name; + size_t len = strlen(name); + if (strncmp(name, "wayfire-", 8) != 0 || len < 15 || + strcmp(name + len - 7, ".socket") != 0) + continue; + + char candidate[MAX_PATH_LEN]; + struct stat st; + snprintf(candidate, sizeof(candidate), "%s/%s", runtime_dir, name); + if (stat(candidate, &st) == 0 && S_ISSOCK(st.st_mode)) { + snprintf(dst, dstsz, "%s", candidate); + closedir(dir); + return 0; + } + } + + closedir(dir); + return -1; +} + +static int mgr_ipc_connect(void) { + char sock[MAX_PATH_LEN]; + if (mgr_wayfire_ipc_path(sock, sizeof(sock)) < 0) { + LOG_ERROR("Wayfire IPC socket not found"); + return -1; + } + + if (strlen(sock) >= sizeof(((struct sockaddr_un *)0)->sun_path)) { + LOG_ERROR("Wayfire IPC socket path too long: %s", sock); + return -1; + } + + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) return -1; + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", sock); + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + close(fd); + return -1; + } + return fd; +} + +static int mgr_readn(int fd, void *buf, size_t n) { + size_t total = 0; + while (total < n) { + ssize_t r = read(fd, (char *)buf + total, n - total); + if (r < 0 && errno == EINTR) + continue; + if (r <= 0) return -1; + total += r; + } + return 0; +} + +static int mgr_writen(int fd, const void *buf, size_t n) { + size_t total = 0; + while (total < n) { + ssize_t w = write(fd, (const char *)buf + total, n - total); + if (w < 0 && errno == EINTR) + continue; + if (w <= 0) + return -1; + total += w; + } + return 0; +} + +static int mgr_ipc_send_payload(int fd, const char *payload) { + uint32_t len = payload ? (uint32_t)strlen(payload) : 0; + if (len > WF_IPC_MAX_PAYLOAD) + return -1; + + if (mgr_writen(fd, &len, WF_IPC_HEADER_LEN) < 0) + return -1; + if (len > 0 && mgr_writen(fd, payload, len) < 0) + return -1; + return 0; +} + +static int mgr_ipc_send_method(int fd, const char *method, + const char *data_json) { + struct json_object *root = json_object_new_object(); + struct json_object *data = NULL; + + if (!root) + return -1; + + if (data_json && data_json[0] != '\0') + data = json_tokener_parse(data_json); + if (!data) + data = json_object_new_object(); + + json_object_object_add(root, "method", json_object_new_string(method)); + json_object_object_add(root, "data", data); + + const char *payload = json_object_to_json_string_ext( + root, JSON_C_TO_STRING_PLAIN); + int rc = mgr_ipc_send_payload(fd, payload); + + json_object_put(root); + return rc; +} + +static int mgr_ipc_recv(int fd, char **out_payload) { + uint32_t len; + if (mgr_readn(fd, &len, WF_IPC_HEADER_LEN) < 0) + return -1; + if (len > WF_IPC_MAX_PAYLOAD) + return -1; + + char *payload = malloc(len + 1); + if (!payload) return -1; + if (len > 0 && mgr_readn(fd, payload, len) < 0) { + free(payload); + return -1; + } + payload[len] = '\0'; + + if (out_payload) *out_payload = payload; + else free(payload); + return 0; +} + +static struct json_object *mgr_ipc_request(const char *method, + const char *data_json) { + int fd = mgr_ipc_connect(); + if (fd < 0) return NULL; + + if (mgr_ipc_send_method(fd, method, data_json) < 0) { + close(fd); + return NULL; + } + + char *resp = NULL; + if (mgr_ipc_recv(fd, &resp) < 0) { + close(fd); + return NULL; + } + close(fd); + + struct json_object *j = json_tokener_parse(resp); + free(resp); + return j; +} + +/* ── Idle inhibitor detection ──────────────────────────────────────── */ + +static int mgr_response_inhibits_output(struct json_object *response, const char *name) { + struct json_object *outputs = NULL; + if (!response || !json_object_object_get_ex(response, "outputs", &outputs) || + !json_object_is_type(outputs, json_type_array)) + return 0; + for (size_t i = 0; i < json_object_array_length(outputs); i++) { + struct json_object *value = json_object_array_get_idx(outputs, i); + if (json_object_is_type(value, json_type_string) && + strcmp(json_object_get_string(value), name) == 0) + return 1; + } + return 0; +} + +static struct json_object *mgr_check_idle_inhibitors(void) { + if (!G.config.inhibit_video) return NULL; + struct json_object *response = mgr_ipc_request("digitalrain/idle_inhibitors", NULL); + struct json_object *outputs = NULL; + if (!response || !json_object_object_get_ex(response, "outputs", &outputs) || + !json_object_is_type(outputs, json_type_array)) { + if (!G.inhibitor_warned) + LOG_WARNING("Per-output video inhibition unavailable; enable Wayfire's digitalrain-idle plugin"); + G.inhibitor_warned = 1; + } else { + G.inhibitor_warned = 0; + } + return response; +} + +/* ── Config parsing ────────────────────────────────────────────────── */ + +static void mgr_trim(char *s) { + char *start = s; + while (*start == ' ' || *start == '\t') start++; + if (start != s) memmove(s, start, strlen(start) + 1); + char *end = s + strlen(s) - 1; + while (end >= s && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) + *end-- = '\0'; +} + +#include "digitalrain-config-manager.h" + +static mgr_output_info_t *mgr_find_output(const char *name) { + for (int i = 0; i < G.output_count; i++) { + if (strcmp(G.outputs[i].name, name) == 0) + return &G.outputs[i]; + } + return NULL; +} + +static int mgr_ipc_response_has_error(struct json_object *resp) { + struct json_object *error; + if (json_object_object_get_ex(resp, "error", &error)) + return 1; + + struct json_object *result; + if (json_object_object_get_ex(resp, "result", &result)) { + const char *status = json_object_get_string(result); + if (status && strcmp(status, "ok") != 0) + return 1; + } + + return 0; +} + +static int mgr_get_cursor_status_from_ipc(char *output, size_t output_len, + int *hidden) { + if (output_len > 0) + output[0] = '\0'; + if (hidden) + *hidden = -1; + + struct json_object *resp = + mgr_ipc_request("digitalrain/cursor_status", "{}"); + if (!resp) + return 0; + + int ok = 0; + if (mgr_ipc_response_has_error(resp)) + goto out; + + struct json_object *valid; + if (json_object_object_get_ex(resp, "valid", &valid) && + !json_object_get_boolean(valid)) + goto out; + + struct json_object *hidden_obj; + if (hidden && json_object_object_get_ex(resp, "hidden", &hidden_obj)) + *hidden = json_object_get_boolean(hidden_obj) ? 1 : 0; + + struct json_object *name_obj; + if (json_object_object_get_ex(resp, "output", &name_obj)) { + const char *name = json_object_get_string(name_obj); + if (name && name[0] != '\0') { + snprintf(output, output_len, "%s", name); + struct json_object *x, *y; + if (json_object_object_get_ex(resp, "x", &x) && json_object_object_get_ex(resp, "y", &y)) { + double px = json_object_get_double(x), py = json_object_get_double(y); + pthread_mutex_lock(&G.lock); + for (int i = 0; i < G.output_count; ++i) { + saver_target_t *t = &G.outputs[i].target; + int left = t->layout_x + t->x, top = t->layout_y + t->y; + if (t->region && !strcmp(t->desktop, name) && px >= left && py >= top && + px < left + t->width && py < top + t->height) { + snprintf(output, output_len, "%s", t->name); + break; + } + } + pthread_mutex_unlock(&G.lock); + } + ok = 1; + } + } + +out: + json_object_put(resp); + return ok; +} + +static int mgr_set_wayfire_cursor_hidden(int hidden) { + struct json_object *resp = mgr_ipc_request( + hidden ? "digitalrain/hide_cursor" : "digitalrain/unhide_cursor", "{}"); + if (!resp) + return 0; + + int ok = !mgr_ipc_response_has_error(resp); + json_object_put(resp); + return ok; +} + +static void mgr_release_wayfire_cursor(void) { + pthread_mutex_lock(&G.lock); + int was_hidden = G.wayfire_cursor_hidden; + pthread_mutex_unlock(&G.lock); + + if (!was_hidden) + return; + + int ok = mgr_set_wayfire_cursor_hidden(0); + + pthread_mutex_lock(&G.lock); + G.wayfire_cursor_hidden = 0; + pthread_mutex_unlock(&G.lock); + + if (ok) + LOG_INFO("Cursor unhidden"); + else + LOG_WARNING("Cursor unhide request failed"); +} + +static void mgr_update_focused_output(const char *focused); + +static void mgr_update_wayfire_cursor(void) { + static char previous_output[MAX_OUTPUT_NAME]; + char cursor_output[MAX_OUTPUT_NAME]; + int actual_hidden = -1; + + if (!mgr_get_cursor_status_from_ipc(cursor_output, sizeof(cursor_output), + &actual_hidden)) { + pthread_mutex_lock(&G.lock); + int should_warn = !G.wayfire_cursor_warned; + G.wayfire_cursor_warned = 1; + pthread_mutex_unlock(&G.lock); + + if (should_warn) + LOG_WARNING("Cursor tracking unavailable; load Wayfire plugin digitalrain-cursor"); + + mgr_release_wayfire_cursor(); + return; + } + + /* A pointer crossing between members does not emit output-gain-focus. */ + if (strcmp(previous_output, cursor_output)) { + mgr_update_focused_output(cursor_output); + snprintf(previous_output, sizeof(previous_output), "%s", cursor_output); + } + pthread_mutex_lock(&G.lock); + G.wayfire_cursor_warned = 0; + if (actual_hidden >= 0) + G.wayfire_cursor_hidden = actual_hidden; + + mgr_output_info_t *o = mgr_find_output(cursor_output); + /* Input can resume before the renderer finishes fading out. A moving + * pointer must remain visible even while its output is still blanked. */ + int should_hide = o && strcmp(o->state, "off") == 0 && G.seat_idle; + int is_hidden = G.wayfire_cursor_hidden; + pthread_mutex_unlock(&G.lock); + + if (should_hide == is_hidden) + return; + + if (mgr_set_wayfire_cursor_hidden(should_hide)) { + pthread_mutex_lock(&G.lock); + G.wayfire_cursor_hidden = should_hide; + pthread_mutex_unlock(&G.lock); + + LOG_INFO("Cursor %s on %s", + should_hide ? "hidden" : "unhidden", cursor_output); + } else { + pthread_mutex_lock(&G.lock); + int should_warn = !G.wayfire_cursor_warned; + G.wayfire_cursor_warned = 1; + pthread_mutex_unlock(&G.lock); + + if (should_warn) + LOG_WARNING("Cursor %s request failed", + should_hide ? "hide" : "unhide"); + } +} + +static int mgr_get_output_timeout(mgr_output_info_t *o) { + if (o->timeout_override > 0) return o->timeout_override; + return G.config.global_timeout; +} + +static const char *mgr_get_output_color(mgr_output_info_t *o) { + static char hex_buf[8]; + const char *color = o->color[0] ? o->color : G.config.matrix_color; + /* Per-trail modes: pass through to renderer as-is */ + if (strcasecmp(color, "random_trail_named") == 0 || + strcasecmp(color, "random_trail_hex") == 0) + return color; + /* Per-output random: resolve here so each output gets a different color */ + if (strcasecmp(color, "random") == 0 || + strcasecmp(color, "random_named") == 0) + return mgr_random_color(); + if (strcasecmp(color, "random_hex") == 0) { + snprintf(hex_buf, sizeof(hex_buf), "#%02x%02x%02x", + rand() % 256, rand() % 256, rand() % 256); + return hex_buf; + } + return color; +} + +static double mgr_get_val_d(double per_output, double global) { + return per_output >= 0.0 ? per_output : global; +} +static int mgr_get_val_i(int per_output, int global) { + return per_output >= 0 ? per_output : global; +} + +static void mgr_init_output(mgr_output_info_t *out, const char *name, + double now) { + memset(out, 0, sizeof(*out)); + snprintf(out->name, sizeof(out->name), "%s", name); + out->last_input = now; + strcpy(out->state, "on"); + out->report_fd = -1; out->render_phase = -1; + out->timeout_override = -1; + out->power_off_override = -1; + out->min_speed = -1.0; + out->max_speed = -1.0; + out->min_trail = -1; + out->max_trail = -1; + out->min_density = -1.0; + out->max_density = -1.0; +} + +typedef struct { + struct wl_display *display; + struct wl_registry *registry; + struct zxdg_output_manager_v1 *xdg_output_manager; + struct wl_list outputs; +} mgr_outputs_wayland_t; + +typedef struct { + struct wl_output *output; + struct zxdg_output_v1 *xdg_output; + char name[MAX_OUTPUT_NAME]; + int width; + int height; + int scale; + struct wl_list link; +} mgr_wayland_output_info_t; + +static void mgr_output_set_name(mgr_wayland_output_info_t *info, + const char *name) { + if (!name || name[0] == '\0') + return; + snprintf(info->name, sizeof(info->name), "%s", name); +} + +static void mgr_xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { + (void)data; (void)xdg_output; (void)x; (void)y; +} + +static void mgr_xdg_output_handle_logical_size(void *data, + struct zxdg_output_v1 *xdg_output, int32_t w, int32_t h) { + (void)xdg_output; + mgr_wayland_output_info_t *info = data; + info->width = w; + info->height = h; +} + +static void mgr_xdg_output_handle_done(void *data, + struct zxdg_output_v1 *xdg_output) { + (void)data; (void)xdg_output; +} + +static void mgr_xdg_output_handle_name(void *data, + struct zxdg_output_v1 *xdg_output, const char *name) { + (void)xdg_output; + mgr_output_set_name(data, name); +} + +static void mgr_xdg_output_handle_description(void *data, + struct zxdg_output_v1 *xdg_output, const char *desc) { + (void)data; (void)xdg_output; (void)desc; +} + +static const struct zxdg_output_v1_listener mgr_xdg_output_listener = { + .logical_position = mgr_xdg_output_handle_logical_position, + .logical_size = mgr_xdg_output_handle_logical_size, + .done = mgr_xdg_output_handle_done, + .name = mgr_xdg_output_handle_name, + .description = mgr_xdg_output_handle_description, +}; + +static void mgr_output_handle_geometry(void *data, struct wl_output *output, + int32_t x, int32_t y, int32_t pw, int32_t ph, int32_t subpixel, + const char *make, const char *model, int32_t transform) { + (void)data; (void)output; (void)x; (void)y; + (void)pw; (void)ph; (void)subpixel; + (void)make; (void)model; (void)transform; +} + +static void mgr_output_handle_mode(void *data, struct wl_output *output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + (void)output; (void)refresh; + mgr_wayland_output_info_t *info = data; + if (flags & WL_OUTPUT_MODE_CURRENT) { + info->width = width; + info->height = height; + } +} + +static void mgr_output_handle_done(void *data, struct wl_output *output) { + (void)data; (void)output; +} + +static void mgr_output_handle_scale(void *data, struct wl_output *output, + int32_t factor) { + (void)output; + mgr_wayland_output_info_t *info = data; + info->scale = factor; +} + +static void mgr_output_handle_name(void *data, struct wl_output *output, + const char *name) { + (void)output; + mgr_output_set_name(data, name); +} + +static void mgr_output_handle_description(void *data, struct wl_output *output, + const char *desc) { + (void)data; (void)output; (void)desc; +} + +static const struct wl_output_listener mgr_output_listener = { + .geometry = mgr_output_handle_geometry, + .mode = mgr_output_handle_mode, + .done = mgr_output_handle_done, + .scale = mgr_output_handle_scale, + .name = mgr_output_handle_name, + .description = mgr_output_handle_description, +}; + +static void mgr_outputs_registry_handle_global(void *data, + struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + mgr_outputs_wayland_t *ctx = data; + + if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && + !ctx->xdg_output_manager) { + ctx->xdg_output_manager = wl_registry_bind(registry, name, + &zxdg_output_manager_v1_interface, version < 3 ? version : 3); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + mgr_wayland_output_info_t *info = calloc(1, sizeof(*info)); + if (!info) + return; + info->output = wl_registry_bind(registry, name, + &wl_output_interface, version < 4 ? version : 4); + info->scale = 1; + wl_output_add_listener(info->output, &mgr_output_listener, info); + wl_list_insert(&ctx->outputs, &info->link); + } +} + +static void mgr_outputs_registry_handle_global_remove(void *data, + struct wl_registry *registry, uint32_t name) { + (void)data; (void)registry; (void)name; +} + +static const struct wl_registry_listener mgr_outputs_registry_listener = { + .global = mgr_outputs_registry_handle_global, + .global_remove = mgr_outputs_registry_handle_global_remove, +}; + +static void mgr_outputs_wayland_cleanup(mgr_outputs_wayland_t *ctx) { + mgr_wayland_output_info_t *info, *tmp; + wl_list_for_each_safe(info, tmp, &ctx->outputs, link) { + if (info->xdg_output) + zxdg_output_v1_destroy(info->xdg_output); + if (info->output) + wl_output_release(info->output); + wl_list_remove(&info->link); + free(info); + } + if (ctx->xdg_output_manager) + zxdg_output_manager_v1_destroy(ctx->xdg_output_manager); + if (ctx->registry) + wl_registry_destroy(ctx->registry); + if (ctx->display) + wl_display_disconnect(ctx->display); +} + +static void mgr_kill_pids(mgr_output_info_t *o); + +static bool same_target(const saver_target_t *a, const saver_target_t *b) { + return !strcmp(a->name, b->name) && !strcmp(a->desktop, b->desktop) && + a->region == b->region && a->x == b->x && a->y == b->y && + a->width == b->width && a->height == b->height && + a->layout_x == b->layout_x && a->layout_y == b->layout_y; +} + +static void mgr_reconcile_targets(const saver_target_t *targets, int count) { + pthread_mutex_lock(&G.lock); + bool changed = false; + for (int i = 0; i < G.output_count;) { + int found = -1; + for (int j = 0; j < count; ++j) + if (!strcmp(G.outputs[i].name, targets[j].name)) { found = j; break; } + if (found < 0 || !same_target(&G.outputs[i].target, &targets[found])) { + LOG_INFO("Removed or reconfigured output: %s", G.outputs[i].name); + mgr_kill_pids(&G.outputs[i]); + memmove(&G.outputs[i], &G.outputs[i + 1], + (G.output_count - i - 1) * sizeof(G.outputs[0])); + --G.output_count; + changed = true; + } else ++i; + } + for (int j = 0; j < count; ++j) { + if (mgr_find_output(targets[j].name)) continue; + mgr_output_info_t *o = &G.outputs[G.output_count++]; + mgr_init_output(o, targets[j].name, mgr_monotime()); + o->target = targets[j]; + LOG_INFO("Discovered output: %s (desktop %s)", o->name, o->target.desktop); + changed = true; + } + if (changed) G.config_mtime = 0; /* Apply overrides to newly discovered monitors. */ + pthread_mutex_unlock(&G.lock); +} + +static void mgr_refresh_outputs(void) { + saver_target_t targets[MAX_OUTPUTS] = {0}; + struct json_object *outputs = mgr_ipc_request("window-rules/list-outputs", "{}"); + int count = parse_targets(outputs, targets); + if (outputs) json_object_put(outputs); + if (count >= 0) { + mgr_reconcile_targets(targets, count); + return; + } + + mgr_outputs_wayland_t ctx = {0}; + wl_list_init(&ctx.outputs); + ensure_wayland_display(); + ctx.display = wl_display_connect(NULL); + if (!ctx.display) { + LOG_ERROR("Output refresh: cannot connect to Wayland display"); + return; + } + ctx.registry = wl_display_get_registry(ctx.display); + wl_registry_add_listener(ctx.registry, &mgr_outputs_registry_listener, &ctx); + if (wl_display_roundtrip(ctx.display) < 0) goto cleanup; + mgr_wayland_output_info_t *info; + if (ctx.xdg_output_manager) { + wl_list_for_each(info, &ctx.outputs, link) { + info->xdg_output = zxdg_output_manager_v1_get_xdg_output(ctx.xdg_output_manager, info->output); + zxdg_output_v1_add_listener(info->xdg_output, &mgr_xdg_output_listener, info); + } + } + if (wl_display_roundtrip(ctx.display) < 0) goto cleanup; + count = 0; + wl_list_for_each(info, &ctx.outputs, link) { + if (!info->name[0] || count == MAX_OUTPUTS) continue; + saver_target_t *t = &targets[count++]; + *t = (saver_target_t){0}; + snprintf(t->name, sizeof(t->name), "%s", info->name); + snprintf(t->desktop, sizeof(t->desktop), "%s", info->name); + t->width = info->width; t->height = info->height; + } + /* During IPC loss, retain member mappings; Wayland only exposes the parent. */ + pthread_mutex_lock(&G.lock); + bool had_regions = false; + for (int i = 0; i < G.output_count; ++i) had_regions |= G.outputs[i].target.region; + pthread_mutex_unlock(&G.lock); + if (!had_regions) mgr_reconcile_targets(targets, count); +cleanup: + mgr_outputs_wayland_cleanup(&ctx); +} + +static const char *mgr_get_focused_output_from_ipc(void) { + static char buf[MAX_OUTPUT_NAME]; + struct json_object *resp = + mgr_ipc_request("window-rules/get-focused-output", "{}"); + if (!resp) + return NULL; + + const char *result = NULL; + struct json_object *status; + if (json_object_object_get_ex(resp, "result", &status) && + strcmp(json_object_get_string(status), "ok") != 0) + goto out; + + struct json_object *info, *name; + if (json_object_object_get_ex(resp, "info", &info) && + json_object_object_get_ex(info, "name", &name)) { + snprintf(buf, sizeof(buf), "%s", json_object_get_string(name)); + result = buf; + } + +out: + json_object_put(resp); + return result; +} + +static void mgr_update_focused_output(const char *focused) { + if (!focused || focused[0] == '\0') + return; + + pthread_mutex_lock(&G.lock); + bool known = mgr_find_output(focused) != NULL; + pthread_mutex_unlock(&G.lock); + + if (!known) { + pthread_mutex_lock(&G.lock); + G.refresh_outputs = 1; + pthread_mutex_unlock(&G.lock); + return; + } + + pthread_mutex_lock(&G.lock); + char old[MAX_OUTPUT_NAME]; + snprintf(old, sizeof(old), "%s", G.focused_output); + snprintf(G.focused_output, sizeof(G.focused_output), "%s", focused); + if (strcmp(old, focused) != 0) { + /* + * Focus shift counts as fresh attention on both monitors: the new one + * is being looked at, and the old one's unfocused timeout starts now. + */ + double now = mgr_monotime(); + mgr_output_info_t *new_o = mgr_find_output(focused); + mgr_output_info_t *old_o = mgr_find_output(old); + if (new_o) new_o->last_input = now; + if (old_o) old_o->last_input = now; + LOG_INFO("Focus moved: %s -> %s", old, focused); + } + pthread_mutex_unlock(&G.lock); +} + +/* ── Screensaver management ────────────────────────────────────────── */ + +static int mgr_pids_alive(mgr_output_info_t *o) { + if (o->pid_count == 0) return 0; + for (int i = 0; i < o->pid_count; i++) { + if (!mgr_pid_alive(o->pids[i])) return 0; + } + return 1; +} + +static void mgr_kill_pids(mgr_output_info_t *o) { + if (o->report_fd >= 0) { close(o->report_fd); o->report_fd = -1; } + for (int i = 0; i < o->pid_count; i++) { + if (o->pids[i] > 0) + kill(o->pids[i], SIGTERM); + } + o->pid_count = 0; +} + +/* + * Get path to current binary via /proc/self/exe. + */ +static const char *mgr_get_self_exe(void) { + static char path[MAX_PATH_LEN]; + ssize_t n = readlink("/proc/self/exe", path, sizeof(path) - 1); + if (n > 0) { + path[n] = '\0'; + return path; + } + /* Fallback: hope "digitalrain" is in PATH */ + return "digitalrain"; +} + +static void mgr_update_adaptive_colors(void); + +static void mgr_blank_output(const char *output_name) { + /* Publish the initial palette before exec, avoiding an initial white flash. */ + mgr_update_adaptive_colors(); + pthread_mutex_lock(&G.lock); + + mgr_output_info_t *o = mgr_find_output(output_name); + if (!o) { + pthread_mutex_unlock(&G.lock); + return; + } + + if (strcmp(o->state, "off") == 0) { + if (mgr_pids_alive(o)) { + pthread_mutex_unlock(&G.lock); + return; + } + G.last_blank_time = mgr_monotime(); + LOG_INFO("[%s] saver died, restarting", output_name); + mgr_kill_pids(o); + } else { + G.last_blank_time = mgr_monotime(); + } + + const char *color = mgr_get_output_color(o); + double min_speed = mgr_get_val_d(o->min_speed, G.config.matrix_min_speed); + double max_speed = mgr_get_val_d(o->max_speed, G.config.matrix_max_speed); + int min_trail = mgr_get_val_i(o->min_trail, G.config.matrix_min_trail); + int max_trail = mgr_get_val_i(o->max_trail, G.config.matrix_max_trail); + double min_density = mgr_get_val_d(o->min_density, G.config.matrix_min_density); + double max_density = mgr_get_val_d(o->max_density, G.config.matrix_max_density); + int depth_layers = G.config.matrix_depth_layers; + pthread_mutex_unlock(&G.lock); + + const char *bin = mgr_get_self_exe(); + + char font_size_str[16], min_speed_str[16], max_speed_str[16], avg_speed_str[16]; + char min_trail_str[16], max_trail_str[16]; + char min_density_str[16], max_density_str[16]; + char depth_layers_str[16], fade_frames_str[16], head_hold_frames_str[16]; + char head_mutation_chance_str[16], head_white_min_str[16], head_white_max_str[16]; + char layer_near_density_str[16], layer_far_density_str[16], transparent_timeout_str[16]; + char black_rain_timeout_str[16], power_off_str[2]; + snprintf(power_off_str, sizeof(power_off_str), "%d", + mgr_get_val_i(o->power_off_override, G.config.matrix_power_off) != 0); + const char *transparent_arg = G.config.matrix_transparent ? "--transparent" : NULL; + const char *hide_desktop_arg = G.config.matrix_hide_desktop ? "--hide-desktop" : NULL; + snprintf(font_size_str, sizeof(font_size_str), "%d", G.config.matrix_font_size); + snprintf(min_speed_str, sizeof(min_speed_str), "%.2f", min_speed); + snprintf(max_speed_str, sizeof(max_speed_str), "%.2f", max_speed); + snprintf(avg_speed_str, sizeof(avg_speed_str), "%.2f", G.config.matrix_avg_speed); + snprintf(min_trail_str, sizeof(min_trail_str), "%d", min_trail); + snprintf(max_trail_str, sizeof(max_trail_str), "%d", max_trail); + snprintf(min_density_str, sizeof(min_density_str), "%.2f", min_density); + snprintf(max_density_str, sizeof(max_density_str), "%.2f", max_density); + snprintf(depth_layers_str, sizeof(depth_layers_str), "%d", depth_layers); + snprintf(fade_frames_str, sizeof(fade_frames_str), "%d", G.config.matrix_fade_frames); + snprintf(head_hold_frames_str, sizeof(head_hold_frames_str), "%d", G.config.matrix_head_hold_frames); + snprintf(head_mutation_chance_str, sizeof(head_mutation_chance_str), "%.2f", G.config.matrix_head_mutation_chance); + snprintf(head_white_min_str, sizeof(head_white_min_str), "%.2f", G.config.matrix_head_white_min); + snprintf(head_white_max_str, sizeof(head_white_max_str), "%.2f", G.config.matrix_head_white_max); + snprintf(layer_near_density_str, sizeof(layer_near_density_str), "%.2f", G.config.matrix_layer_near_density); + snprintf(layer_far_density_str, sizeof(layer_far_density_str), "%.2f", G.config.matrix_layer_far_density); + snprintf(transparent_timeout_str, sizeof(transparent_timeout_str), "%d", G.config.matrix_transparent_timeout); + snprintf(black_rain_timeout_str, sizeof(black_rain_timeout_str), "%d", G.config.matrix_black_rain_timeout); + + int report[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, report)) return; + char report_str[16]; snprintf(report_str, sizeof(report_str), "%d", report[1]); + pid_t manager_pid = getpid(); + pid_t pid = fork(); + if (pid < 0) { + close(report[0]); close(report[1]); + LOG_ERROR("[%s] fork failed: %s", output_name, strerror(errno)); + return; + } + + if (pid == 0) { + /* Child: exec self in render mode */ + close(report[0]); fcntl(report[1], F_SETFD, 0); + struct sigaction terminate = { .sa_handler = SIG_DFL }; + sigemptyset(&terminate.sa_mask); + sigaction(SIGTERM, &terminate, NULL); + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0 || getppid() != manager_pid) + _exit(1); + int devnull = open("/dev/null", O_RDWR); + if (devnull >= 0) { + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + close(devnull); + } + execl(bin, "digitalrain", + "--render", "--report-fd", report_str, + "--output", output_name, + "--color", color, + "--font-size", font_size_str, + "--min-speed", min_speed_str, + "--max-speed", max_speed_str, + "--avg-speed", avg_speed_str, + "--min-trail", min_trail_str, + "--max-trail", max_trail_str, + "--min-density", min_density_str, + "--max-density", max_density_str, + "--depth-layers", depth_layers_str, + "--fade-frames", fade_frames_str, + "--head-hold-frames", head_hold_frames_str, + "--head-mutation-chance", head_mutation_chance_str, + "--head-white-min", head_white_min_str, + "--head-white-max", head_white_max_str, + "--layer-near-density", layer_near_density_str, + "--layer-far-density", layer_far_density_str, + "--transparent-timeout", transparent_timeout_str, + "--black-rain-timeout", black_rain_timeout_str, + "--power-off", power_off_str, + transparent_arg, + hide_desktop_arg, + (char *)NULL); + _exit(127); + } + + close(report[1]); + /* Parent: wait briefly to check if it exits immediately */ + usleep(500000); + int status; + pid_t w = waitpid(pid, &status, WNOHANG); + if (w > 0) { + close(report[0]); + LOG_WARNING("[%s] renderer exited immediately", output_name); + return; + } + + pthread_mutex_lock(&G.lock); + o = mgr_find_output(output_name); + if (o) { + if (o->report_fd >= 0) close(o->report_fd); + o->sent_revision = o->applied_revision = 0; memset(&o->sent_effects, 0, sizeof(o->sent_effects)); + o->report_fd = report[0]; o->render_phase = -1; o->powered_off = false; + o->pids[0] = pid; + o->pid_count = 1; + strcpy(o->state, "off"); + LOG_INFO("[%s] matrix screensaver active (PID %d, color=%s, speed=%.1f-%.1f, depth=%d)", + output_name, pid, color, min_speed, max_speed, depth_layers); + } + pthread_mutex_unlock(&G.lock); +} + +/* Fading renderers are reaped without blocking API or other monitor wakes. */ +static struct { pid_t pid; double deadline; } mgr_fading[64]; +static void mgr_reap_fading(void) { + double now = mgr_monotime(); + for (size_t i = 0; i < 64; ++i) if (mgr_fading[i].pid > 0) { + int status; + pid_t result = waitpid(mgr_fading[i].pid, &status, WNOHANG); + if (result > 0 || (result < 0 && errno == ECHILD)) mgr_fading[i].pid = 0; + else if (now >= mgr_fading[i].deadline) { + kill(mgr_fading[i].pid, SIGTERM); + mgr_fading[i].deadline = now + 3; + } + } +} +static void mgr_unblank_output(const char *output_name) { + pthread_mutex_lock(&G.lock); + mgr_output_info_t *o = mgr_find_output(output_name); + if (!o || strcmp(o->state, "on") == 0) { pthread_mutex_unlock(&G.lock); return; } + for (int i = 0; i < o->pid_count; ++i) if (o->pids[i] > 0) { + kill(o->pids[i], SIGUSR1); + size_t j; + for (j = 0; j < 64; ++j) if (!mgr_fading[j].pid) { + mgr_fading[j].pid = o->pids[i]; mgr_fading[j].deadline = mgr_monotime() + 3; break; + } + if (j == 64) kill(o->pids[i], SIGTERM); + } + if (o->report_fd >= 0) { close(o->report_fd); o->report_fd = -1; } + o->render_phase = -1; o->powered_off = false; o->pid_count = 0; + strcpy(o->state, "on"); + pthread_mutex_unlock(&G.lock); + mgr_update_wayfire_cursor(); + LOG_INFO("[%s] unblanked", output_name); +} + +/* ── Focus watcher thread ──────────────────────────────────────────── */ + +static const char *mgr_focus_name_from_event(struct json_object *event, + char *buf, size_t bufsz) { + struct json_object *event_name_obj; + const char *event_name = NULL; + + if (json_object_object_get_ex(event, "event", &event_name_obj)) + event_name = json_object_get_string(event_name_obj); + + if (!event_name) + return NULL; + + if (strcmp(event_name, "output-added") == 0 || + strcmp(event_name, "output-removed") == 0) { + pthread_mutex_lock(&G.lock); + G.refresh_outputs = 1; + pthread_mutex_unlock(&G.lock); + return NULL; + } + + if (strcmp(event_name, "output-gain-focus") == 0) { + struct json_object *output, *name; + if (json_object_object_get_ex(event, "output", &output) && + json_object_object_get_ex(output, "name", &name)) { + snprintf(buf, bufsz, "%s", json_object_get_string(name)); + return buf; + } + return NULL; + } + + if (strcmp(event_name, "view-focused") == 0) { + struct json_object *view, *name; + if (json_object_object_get_ex(event, "view", &view) && + json_object_object_get_ex(view, "output-name", &name)) { + struct json_object *monitor; + if (json_object_object_get_ex(view, "physical-monitor", &monitor) && + json_object_is_type(monitor, json_type_string) && json_object_get_string(monitor)[0]) name = monitor; + snprintf(buf, bufsz, "%s", json_object_get_string(name)); + return buf; + } + + return mgr_get_focused_output_from_ipc(); + } + + return NULL; +} + +static void *mgr_focus_watcher(void *arg) { + (void)arg; + + while (!G.stop) { + int fd = mgr_ipc_connect(); + if (fd < 0) { + LOG_ERROR("Focus watcher: cannot connect to Wayfire IPC"); + sleep(2); + continue; + } + + if (mgr_ipc_send_method(fd, "window-rules/events/watch", + "{\"events\":[\"view-focused\",\"output-gain-focus\"," + "\"output-added\",\"output-removed\"]}") < 0) { + close(fd); + sleep(2); + continue; + } + + char *resp = NULL; + if (mgr_ipc_recv(fd, &resp) < 0) { + free(resp); + close(fd); + sleep(2); + continue; + } + + struct json_object *ack = json_tokener_parse(resp); + free(resp); + if (ack) { + struct json_object *status; + if (json_object_object_get_ex(ack, "result", &status) && + strcmp(json_object_get_string(status), "ok") != 0) { + LOG_ERROR("Focus watcher: Wayfire event watch rejected: %s", + json_object_to_json_string_ext( + ack, JSON_C_TO_STRING_PLAIN)); + json_object_put(ack); + close(fd); + sleep(2); + continue; + } + json_object_put(ack); + } + + LOG_INFO("Focus watcher: using Wayfire IPC events"); + + while (!G.stop) { + char *payload = NULL; + if (mgr_ipc_recv(fd, &payload) < 0) { + free(payload); + break; + } + + struct json_object *event = json_tokener_parse(payload); + free(payload); + if (!event) + continue; + + char focused_buf[MAX_OUTPUT_NAME]; + const char *focused = + mgr_focus_name_from_event(event, focused_buf, sizeof(focused_buf)); + json_object_put(event); + if (focused) { + mgr_update_focused_output(focused); + } + } + + close(fd); + } + + return NULL; +} + +/* ── Idle watcher thread ───────────────────────────────────────────── */ + +typedef struct { + struct wl_display *display; + struct wl_registry *registry; + struct wl_seat *seat; + struct ext_idle_notifier_v1 *notifier; + struct ext_idle_notification_v1 *notification; + uint32_t notifier_version; + uint32_t seat_version; +} mgr_idle_wayland_t; + +static void mgr_idle_handle_idled(void *data, + struct ext_idle_notification_v1 *notification) { + (void)data; + (void)notification; + + pthread_mutex_lock(&G.lock); + G.seat_idle = 1; + pthread_mutex_unlock(&G.lock); +} + +static void mgr_idle_handle_resumed(void *data, + struct ext_idle_notification_v1 *notification) { + (void)data; + (void)notification; + + double now = mgr_monotime(); + + pthread_mutex_lock(&G.lock); + G.seat_idle = 0; + if ((now - G.last_blank_time) >= 3.0) { + G.manual_activation = 0; + /* The idle notification covers the whole seat, but renewed input is + * attention on the focused output only. Preserve every other output's + * countdown, just as the manager's continuous-activity path does. */ + mgr_output_info_t *focused = mgr_find_output(G.focused_output); + if (focused) focused->last_input = now; + } + pthread_mutex_unlock(&G.lock); +} + +static const struct ext_idle_notification_v1_listener mgr_idle_listener = { + .idled = mgr_idle_handle_idled, + .resumed = mgr_idle_handle_resumed, +}; + +static void mgr_idle_registry_handle_global(void *data, + struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + mgr_idle_wayland_t *idle = data; + + if (strcmp(interface, wl_seat_interface.name) == 0 && !idle->seat) { + idle->seat_version = version < 7 ? version : 7; + idle->seat = wl_registry_bind(registry, name, + &wl_seat_interface, idle->seat_version); + } else if (strcmp(interface, ext_idle_notifier_v1_interface.name) == 0 && + !idle->notifier) { + idle->notifier_version = version < 2 ? version : 2; + idle->notifier = wl_registry_bind(registry, name, + &ext_idle_notifier_v1_interface, idle->notifier_version); + } +} + +static void mgr_idle_registry_handle_global_remove(void *data, + struct wl_registry *registry, uint32_t name) { + (void)data; + (void)registry; + (void)name; +} + +static const struct wl_registry_listener mgr_idle_registry_listener = { + .global = mgr_idle_registry_handle_global, + .global_remove = mgr_idle_registry_handle_global_remove, +}; + +static void mgr_idle_wayland_cleanup(mgr_idle_wayland_t *idle) { + if (idle->notification) + ext_idle_notification_v1_destroy(idle->notification); + if (idle->notifier) + ext_idle_notifier_v1_destroy(idle->notifier); + if (idle->seat) { + if (idle->seat_version >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_seat_release(idle->seat); + else + wl_seat_destroy(idle->seat); + } + if (idle->registry) + wl_registry_destroy(idle->registry); + if (idle->display) + wl_display_disconnect(idle->display); + memset(idle, 0, sizeof(*idle)); +} + +static void *mgr_idle_watcher(void *arg) { + (void)arg; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + while (!G.stop) { + mgr_idle_wayland_t idle = {0}; + + idle.display = wl_display_connect(NULL); + if (!idle.display) { + LOG_ERROR("Idle watcher: cannot connect to Wayland display"); + sleep(2); + continue; + } + + idle.registry = wl_display_get_registry(idle.display); + wl_registry_add_listener(idle.registry, &mgr_idle_registry_listener, &idle); + wl_display_roundtrip(idle.display); + + if (!idle.notifier || !idle.seat) { + LOG_ERROR("Idle watcher: compositor lacks ext-idle-notify-v1 or wl_seat"); + mgr_idle_wayland_cleanup(&idle); + sleep(5); + continue; + } + + /* Observe physical input independently of application inhibitors, which + * are handled per-output by the manager rather than for the whole seat. */ + if (idle.notifier_version >= 2) { + idle.notification = ext_idle_notifier_v1_get_input_idle_notification( + idle.notifier, 2000, idle.seat); + } else { + idle.notification = ext_idle_notifier_v1_get_idle_notification( + idle.notifier, 2000, idle.seat); + LOG_WARNING("Idle notifier v1: focused output may also stay awake during playback"); + } + ext_idle_notification_v1_add_listener(idle.notification, + &mgr_idle_listener, NULL); + wl_display_flush(idle.display); + + LOG_INFO("Idle watcher: using ext-idle-notify-v1"); + + while (!G.stop) { + int rc = wl_display_dispatch(idle.display); + if (rc < 0) { + LOG_ERROR("Idle watcher: Wayland dispatch failed"); + break; + } + } + + mgr_idle_wayland_cleanup(&idle); + if (!G.stop) + sleep(2); + } + + return NULL; +} + +/* ── Manager signal handling ───────────────────────────────────────── */ + +static void mgr_signal_handler(int sig) { + if (sig == SIGUSR1) + G.activate_all = 1; + else if (sig == SIGUSR2) + G.lock_now = 1; + else if (sig == SIGRTMIN) + G.adaptive_update = 1; + else + G.stop = 1; +} + +/* Palette policy is deliberately independent of the renderer and wallpaper + * rotator: one hue per output, a single contrast shade for the composed scene, + * and a matching bright shade for that output's eventual black background. */ +static rgb_t adaptive_hsl(double hue, double lightness) { + double chroma = (1.0 - fabs(2.0 * lightness - 1.0)) * 0.78; + double h = hue * 6.0; + double x = chroma * (1.0 - fabs(fmod(h, 2.0) - 1.0)); + double m = lightness - chroma / 2.0; + rgb_t rgb; + if (h < 1) rgb = (rgb_t){chroma, x, 0}; + else if (h < 2) rgb = (rgb_t){x, chroma, 0}; + else if (h < 3) rgb = (rgb_t){0, chroma, x}; + else if (h < 4) rgb = (rgb_t){0, x, chroma}; + else if (h < 5) rgb = (rgb_t){x, 0, chroma}; + else rgb = (rgb_t){chroma, 0, x}; + return (rgb_t){rgb.r + m, rgb.g + m, rgb.b + m}; +} + +static void adaptive_hex(char hex[8], rgb_t color) { + snprintf(hex, 8, "#%02x%02x%02x", (unsigned)lroundf(color.r * 255), + (unsigned)lroundf(color.g * 255), (unsigned)lroundf(color.b * 255)); +} + +/* Prefer a hue separated from the other monitors and from this one's last hue. + * Best-of-64 also works when many outputs make the preferred spacing impossible. */ +static double mgr_choose_hue(void) { + double best = 0, best_distance = -1; + for (int attempt = 0; attempt < 64; ++attempt) { + double candidate = rand() / ((double)RAND_MAX + 1.0); + double nearest = 1; + for (int i = 0; i < G.output_count; ++i) { + if (!G.outputs[i].palette_ready) continue; + double d = fabs(candidate - G.outputs[i].adaptive_hue); + nearest = fmin(nearest, fmin(d, 1.0 - d)); + } + if (nearest > best_distance) { + best = candidate; + best_distance = nearest; + } + if (nearest >= 0.12) break; + } + return best; +} + +static void mgr_update_adaptive_colors(void) { + double now = mgr_monotime(); + bool enabled = false, missing = false, hue_due = false; + pthread_mutex_lock(&G.lock); + for (int i = 0; i < G.output_count; ++i) { + mgr_output_info_t *o = &G.outputs[i]; + if (!is_adaptive_color(o->color[0] ? o->color : G.config.matrix_color)) continue; + enabled = true; + missing |= !o->palette_ready; + hue_due |= G.config.adaptive_hue_interval > 0 && + now - o->adaptive_hue_time >= G.config.adaptive_hue_interval; + } + pthread_mutex_unlock(&G.lock); + bool sample_due = missing || now - G.last_adaptive_sample >= G.config.adaptive_sample_interval; + if (!enabled || (!sample_due && !hue_due)) return; + + struct json_object *response = NULL, *samples = NULL, *version = NULL; + if (sample_due) { + G.last_adaptive_sample = now; + response = mgr_ipc_request("digitalrain/sample_desktop", "{}"); + bool valid = response && json_object_object_get_ex(response, "version", &version) && + json_object_get_int(version) == 1 && + json_object_object_get_ex(response, "outputs", &samples) && + json_object_is_type(samples, json_type_object); + if (!valid) { + samples = NULL; + if (!G.sampler_warned) + LOG_WARNING("Desktop sampling unavailable; enable digitalrain-sampler in Wayfire. Retaining palettes."); + G.sampler_warned = true; + } else { + G.sampler_warned = false; + } + } + + struct json_object *palette = json_object_new_object(); + pthread_mutex_lock(&G.lock); + for (int i = 0; i < G.output_count; ++i) { + mgr_output_info_t *o = &G.outputs[i]; + const char *mode = o->color[0] ? o->color : G.config.matrix_color; + if (!is_adaptive_color(mode)) continue; + if (!o->palette_ready || (G.config.adaptive_hue_interval > 0 && + now - o->adaptive_hue_time >= G.config.adaptive_hue_interval)) { + o->adaptive_hue = mgr_choose_hue(); + o->adaptive_hue_time = now; + } + struct json_object *sample = NULL, *luminance = NULL; + if (samples && json_object_object_get_ex(samples, o->name, &sample) && + json_object_object_get_ex(sample, "luminance", &luminance) && + (json_object_is_type(luminance, json_type_double) || + json_object_is_type(luminance, json_type_int))) { + double value = json_object_get_double(luminance); + if (isfinite(value) && value >= 0 && value <= 1) + o->adaptive_luminance = value; + } + /* Linear luminance: black and white contrast ratios cross near 0.18. */ + bool light = o->adaptive_luminance < 0.18; + if (!strcasecmp(mode, "adaptive-light")) light = true; + if (!strcasecmp(mode, "adaptive-dark")) light = false; + char desktop[8], black[8]; + adaptive_hex(desktop, adaptive_hsl(o->adaptive_hue, light ? 0.72 : 0.22)); + adaptive_hex(black, adaptive_hsl(o->adaptive_hue, 0.72)); + struct json_object *entry = json_object_new_object(); + json_object_object_add(entry, "color", json_object_new_string(desktop)); + json_object_object_add(entry, "black-color", json_object_new_string(black)); + json_object_object_add(entry, "hue", json_object_new_double(o->adaptive_hue)); + json_object_object_add(entry, "luminance", json_object_new_double(o->adaptive_luminance)); + json_object_object_add(palette, o->name, entry); + o->palette_ready = true; + } + + const char *runtime = getenv("XDG_RUNTIME_DIR"); + if (runtime && runtime[0]) { + char path[MAX_PATH_LEN], temporary[MAX_PATH_LEN]; + mgr_runtime_path(path, sizeof(path), "palette.json"); + mgr_runtime_path(temporary, sizeof(temporary), "palette.XXXXXX"); + int fd = mkstemp(temporary); + bool published = false; + if (fd >= 0) { + const char *json = json_object_to_json_string_ext(palette, JSON_C_TO_STRING_PLAIN); + size_t length = strlen(json), written = 0; + while (written < length) { + ssize_t n = write(fd, json + written, length - written); + if (n < 0 && errno == EINTR) continue; + if (n <= 0) break; + written += (size_t)n; + } + int closed = close(fd); + published = written == length && closed == 0 && rename(temporary, path) == 0; + if (!published) unlink(temporary); + } + if (published) { + for (int i = 0; i < G.output_count; ++i) { + mgr_output_info_t *o = &G.outputs[i]; + if (!is_adaptive_color(o->color[0] ? o->color : G.config.matrix_color)) continue; + for (int p = 0; p < o->pid_count; ++p) + if (o->pids[p] > 0) kill(o->pids[p], SIGRTMIN); + } + } else { + LOG_WARNING("Unable to publish adaptive palette: %s", strerror(errno)); + } + } + pthread_mutex_unlock(&G.lock); + json_object_put(palette); + if (response) json_object_put(response); +} + +/* ── Lock command ──────────────────────────────────────────────────── */ + + +/* + * Blank all outputs immediately (for activate/lock triggers). + */ +static void mgr_activate_all_outputs(void) { + pthread_mutex_lock(&G.lock); + G.seat_idle = 1; + G.last_blank_time = mgr_monotime(); + G.manual_activation = 1; + int n = G.output_count; + char names[MAX_OUTPUTS][MAX_OUTPUT_NAME]; + for (int i = 0; i < n; i++) + snprintf(names[i], MAX_OUTPUT_NAME, "%s", G.outputs[i].name); + pthread_mutex_unlock(&G.lock); + + for (int i = 0; i < n; i++) + mgr_blank_output(names[i]); + + LOG_INFO("All outputs activated (screensaver forced on)"); +} + +/* ── Manager ownership and PID file for signal delivery ────────────── */ + +/* Return 1 for ownership, 0 for an existing manager, -1 for an error. + * Keep this descriptor until shutdown; never unlink the lock file, since + * competing openers must continue to lock the same inode. O_CLOEXEC prevents + * rain renderers and the independent locker from retaining manager ownership. */ +static int mgr_acquire_instance_lock(int *lock_fd) { + char path[MAX_PATH_LEN]; + mgr_runtime_path(path, sizeof(path), "manager.lock"); + *lock_fd = -1; + + int fd = open(path, O_RDWR | O_CREAT | O_CLOEXEC | O_NOFOLLOW, 0600); + if (fd < 0) { + fprintf(stderr, "digitalrain: cannot open manager lock %s: %s\n", path, strerror(errno)); + return -1; + } + if (flock(fd, LOCK_EX | LOCK_NB) < 0) { + int error = errno; + close(fd); + if (error == EWOULDBLOCK || error == EAGAIN) { + fprintf(stderr, "digitalrain: manager already running; skipping duplicate start\n"); + return 0; + } + fprintf(stderr, "digitalrain: cannot acquire manager lock %s: %s\n", path, strerror(error)); + return -1; + } + *lock_fd = fd; + return 1; +} + +static void mgr_write_pidfile(void) { + char path[MAX_PATH_LEN]; + mgr_runtime_path(path, sizeof(path), "pid"); + + FILE *f = fopen(path, "w"); + if (f) { + fprintf(f, "%d\n", getpid()); + fclose(f); + } +} + +static void mgr_remove_pidfile(void) { + char path[MAX_PATH_LEN]; + mgr_runtime_path(path, sizeof(path), "pid"); + unlink(path); +} + +/* ── Zombie reaper ─────────────────────────────────────────────────── */ + +static void mgr_reap_children(void) { + int status; + while (waitpid(-1, &status, WNOHANG) > 0) + ; +} + +/* The API runs on the manager thread and replaces its existing timed waits. */ +#include "digitalrain-api.h" + +/* ── Manager main ──────────────────────────────────────────────────── */ + +static int manager_main(void) { + srand(time(NULL)); + + memset(&G, 0, sizeof(G)); + pthread_mutex_init(&G.lock, NULL); + ensure_wayland_display(); + + /* Reject duplicate autostarts before changing logs, PID, palette or outputs. */ + int instance_lock_fd; + int ownership = mgr_acquire_instance_lock(&instance_lock_fd); + if (ownership != 1) { + pthread_mutex_destroy(&G.lock); + return ownership == 0 ? 0 : 1; + } + + mgr_set_defaults(); + if (!mgr_load_config()) { fprintf(stderr, "%s\n", C.error); close(instance_lock_fd); return 1; } + snprintf(C.log_destination, sizeof(C.log_destination), "%s", G.config.log_file); + + /* Setup logging */ + { + char logdir[MAX_PATH_LEN]; + snprintf(logdir, sizeof(logdir), "%s", G.config.log_file); + char *slash = strrchr(logdir, '/'); + if (slash) { + *slash = '\0'; + mgr_mkdirs(logdir); + } + } + G.log_fp = fopen(G.config.log_file, "w"); + if (!G.log_fp) + fprintf(stderr, "Warning: cannot open log file %s: %s\n", G.config.log_file, strerror(errno)); + + /* Signal handlers */ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = mgr_signal_handler; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + sigaction(SIGRTMIN, &sa, NULL); + + signal(SIGPIPE, SIG_IGN); + + if (!api_init()) { close(instance_lock_fd); return 1; } + mgr_write_pidfile(); + + /* Initialize outputs, then re-read config for per-output overrides */ + mgr_refresh_outputs(); + G.config_mtime = 0; + mgr_load_config(); + mgr_update_adaptive_colors(); + + const char *focused = mgr_get_focused_output_from_ipc(); + pthread_mutex_lock(&G.lock); + if (focused) + snprintf(G.focused_output, sizeof(G.focused_output), "%s", focused); + pthread_mutex_unlock(&G.lock); + + /* Log startup */ + { + char outlist[512] = ""; + for (int i = 0; i < G.output_count; i++) { + if (i > 0) strcat(outlist, ", "); + strcat(outlist, G.outputs[i].name); + } + LOG_INFO("Started — outputs: [%s], focused: %s", outlist, G.focused_output); + } + + /* Start threads */ + pthread_t focus_tid, idle_tid; + pthread_create(&focus_tid, NULL, mgr_focus_watcher, NULL); + pthread_create(&idle_tid, NULL, mgr_idle_watcher, NULL); + + double last_config_check = 0; + double last_cursor_check = 0; + double last_adaptive_check = mgr_monotime(); + + while (!G.stop) { + double now = mgr_monotime(); + + pthread_mutex_lock(&G.lock); + int refresh = G.refresh_outputs; + G.refresh_outputs = 0; + pthread_mutex_unlock(&G.lock); + if (refresh || now - last_config_check > 10) { + mgr_refresh_outputs(); + mgr_load_config(); + last_config_check = now; + } + + /* Sampling and hue changes have independent deadlines. */ + if (now - last_adaptive_check >= 1) { + mgr_update_adaptive_colors(); + last_adaptive_check = now; + } + + api_tick(now); + mgr_reap_children(); + + /* Handle activate/lock signals */ + if (G.lock_now) { + G.lock_now = 0; + G.activate_all = 0; /* lock implies activate */ + struct json_object *request = api_lock(); + json_object_put(request); + } else if (G.activate_all && !api_locked()) { + G.activate_all = 0; + mgr_activate_all_outputs(); + } + + struct json_object *inhibitors = mgr_check_idle_inhibitors(); + + pthread_mutex_lock(&G.lock); + char focused_buf[MAX_OUTPUT_NAME]; + snprintf(focused_buf, sizeof(focused_buf), "%s", G.focused_output); + int idle = G.seat_idle; + + if (!idle) { + mgr_output_info_t *fo = mgr_find_output(focused_buf); + if (fo) fo->last_input = now; + } + + int n = G.output_count; + char names[MAX_OUTPUTS][MAX_OUTPUT_NAME]; + double last_inputs[MAX_OUTPUTS]; + char states[MAX_OUTPUTS][8]; + int timeouts[MAX_OUTPUTS]; + int is_focused[MAX_OUTPUTS]; + int inhibited[MAX_OUTPUTS]; + + for (int i = 0; i < n; i++) { + snprintf(names[i], MAX_OUTPUT_NAME, "%s", G.outputs[i].name); + int app_inhibited = !G.manual_activation && mgr_response_inhibits_output(inhibitors, names[i]); + inhibited[i] = api_locked() || (G.outputs[i].preview_until <= now && + (api_inhibited(names[i], now) || app_inhibited)); + if (app_inhibited != G.outputs[i].video_inhibited) { + LOG_INFO("[%s] video inhibition %s", names[i], app_inhibited ? "active" : "released"); + G.outputs[i].video_inhibited = app_inhibited; + } + /* Restart this output's normal idle countdown when playback ends. */ + if (inhibited[i]) G.outputs[i].last_input = now; + last_inputs[i] = G.outputs[i].last_input; + snprintf(states[i], 8, "%s", G.outputs[i].state); + is_focused[i] = (strcmp(names[i], focused_buf) == 0); + if (is_focused[i]) + timeouts[i] = G.config.focused_timeout; + else + timeouts[i] = mgr_get_output_timeout(&G.outputs[i]); + } + pthread_mutex_unlock(&G.lock); + + if (inhibitors) json_object_put(inhibitors); + + for (int i = 0; i < n; i++) { + double idle_secs = now - last_inputs[i]; + + if (inhibited[i]) { + if (strcmp(states[i], "off") == 0) mgr_unblank_output(names[i]); + } else if (A.ambient_fd >= 0) { + /* The ambient controller owns visual idle transitions, not idle clocks. */ + continue; + } else if (G.outputs[i].preview_until > now) { + continue; + } else if (idle_secs >= timeouts[i]) { + mgr_blank_output(names[i]); + } else { + if (strcmp(states[i], "off") == 0 && is_focused[i]) { + mgr_unblank_output(names[i]); + } + } + } + + if (now - last_cursor_check >= CURSOR_POLL_INTERVAL) { + mgr_update_wayfire_cursor(); + last_cursor_check = now; + } + + for (int s = 0; s < G.config.check_interval * 10 && !G.stop; s++) { + api_poll(100); + + double wait_now = mgr_monotime(); + if (wait_now - last_cursor_check >= CURSOR_POLL_INTERVAL) { + mgr_update_wayfire_cursor(); + last_cursor_check = wait_now; + } + + pthread_mutex_lock(&G.lock); + int idle_now = G.seat_idle; + int any_blanked = 0; + if (!idle_now) { + for (int i = 0; i < G.output_count; i++) { + if (strcmp(G.outputs[i].state, "off") == 0) { + any_blanked = 1; + break; + } + } + } + pthread_mutex_unlock(&G.lock); + if (!idle_now && any_blanked) break; + } + if (G.adaptive_update) { + G.adaptive_update = 0; + G.last_adaptive_sample = 0; + mgr_update_adaptive_colors(); + } + } + + /* Clean shutdown */ + LOG_INFO("Shutting down..."); + for (int i = 0; i < G.output_count; i++) + mgr_unblank_output(G.outputs[i].name); + mgr_release_wayfire_cursor(); + for (size_t i = 0; i < 64; ++i) if (mgr_fading[i].pid > 0) kill(mgr_fading[i].pid, SIGTERM); + + pthread_cancel(focus_tid); + pthread_cancel(idle_tid); + + api_close(); + mgr_remove_pidfile(); + if (G.log_fp) fclose(G.log_fp); + pthread_mutex_destroy(&G.lock); + close(instance_lock_fd); + + return 0; +} + +/* =================================================================== + * ENTRY POINT — dispatch to manager or renderer + * =================================================================== */ + +int main(int argc, char **argv) { + if (argc > 1 && !strcmp(argv[1], "--render")) return renderer_main(argc, argv); + if (argc > 1) return api_cli(argc, argv); + return manager_main(); +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain.pam b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain.pam new file mode 100644 index 000000000000..3624630cafc1 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain.pam @@ -0,0 +1,3 @@ +#%PAM-1.0 +auth include system-auth +account include system-auth diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain.toml.default b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain.toml.default new file mode 100644 index 000000000000..ae3c8b5e089a --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/digitalrain.toml.default @@ -0,0 +1,164 @@ +schema_version = 1 +# Digital Rain configuration +# Guide: configuration.md in /usr/share/doc/digitalrain-*/ (may be compressed). +# Manager chooses one config; user settings do not merge with /etc/digitalrain.toml. +# Quote strings and use true/false switches. Inline # comments are supported. +# Timer values are seconds; animation values explicitly say frames. +# Appearance updates live. Surface/background policy applies on next activation. +[global] +# --- Idle detection and logging --- +# Seconds before an unfocused physical monitor starts rain. Use a positive integer. +TIMEOUT = 300 +# Seconds before the focused monitor starts rain while input is idle. Use a positive +# integer. This takes precedence over [output_timeouts]. +FOCUSED_TIMEOUT = 30 +# Seconds between normal manager checks; use a positive integer. Cursor tracking and +# wake checks continue during this wait. +CHECK_INTERVAL = 5 +# true honors visible applications' Wayland and supported portal idle inhibitors on their monitors; false ignores +# them. Requires digitalrain-idle (portal requests also need the GTK backend bridge). This follows application requests, not video +# detection. +INHIBIT_VIDEO = true +# Writable log path; ~/ expands to your home. Reopened and truncated on manager startup. +# Restart the manager after changing this path. +LOG_FILE = "~/.local/state/digitalrain/digitalrain.log" +# Whole-seat inactivity seconds before native session locking. 0 disables automatic +# locking. Independent of monitor saver timers; saver inhibitors do not delay it. +# Requires Wayfire session-lock and the current digitalrain-idle helper. +LOCK_TIMEOUT = 0 +[matrix] +# --- Background, transitions, and monitor power --- +# true starts rain over visible desktop content; false starts with a black background. +TRANSPARENT = false +# Seconds from renderer startup until transparent rain starts fading to a black +# background. 0 keeps it transparent indefinitely. Used only with TRANSPARENT=true. +TRANSPARENT_TIMEOUT = 600 +# Additional seconds after the background is fully black before the rain fades away. 0 +# keeps rain running on black indefinitely; it does not immediately stop rain. +BLACK_RAIN_TIMEOUT = 0 +# false retains a solid-black surface with the monitor powered for instant wake. true powers +# off after rain ends. Requires a positive BLACK_RAIN_TIMEOUT and digitalrain-power; +# unavailable power control falls back to black. +POWER_OFF = false +# false leaves apps and bars visible under transparent rain; the black background covers +# them naturally. true optionally fades them away over the wallpaper. Requires +# TRANSPARENT=true and digitalrain-desktop. +HIDE_DESKTOP = false +# Frames per fade stage, valid range 1–300. Target frame rate is 30 fps: 20 frames ≈ 0.67 +# seconds; 30 ≈ 1 second. A wake can contain both a rain fade and a background fade. +FADE_FRAMES = 30 +# --- Color and adaptive sampling --- +# Fixed: green, red, blue, white, yellow, cyan, magenta, or #RRGGBB. +# Per monitor: random_named (alias random), random_hex. +# Per trail: random_trail_named, random_trail_hex. +# Adaptive: adaptive, adaptive-light, adaptive-dark (requires digitalrain-sampler). +# Named color, six-digit hex, random mode, or adaptive mode; see the color table in +# configuration.md. +COLOR = "green" +# Seconds between desktop contrast samples in adaptive modes; minimum 5. Requires +# digitalrain-sampler. +ADAPTIVE_SAMPLE_INTERVAL = 60 +# Seconds between new random hues in adaptive modes. 0 holds the initial hue; negative +# values are rejected. Independent of contrast sampling. +ADAPTIVE_HUE_INTERVAL = 900 +# --- Glyphs and depth layers --- +# Base glyph size in renderer pixels, valid range 6–72. Depth layers scale the glyph +# grid. +FONT_SIZE = 12 +# Number of rain layers, valid range 1–8. Far layers are smaller, dimmer and slower; near +# layers are larger, brighter and faster. +DEPTH_LAYERS = 4 +# --- Motion and trail lifetime --- +# Minimum fall speed in cells per animation frame before depth scaling. Valid range 0.001–100. +MIN_SPEED = 0.4 +# Maximum fall speed in cells per animation frame before depth scaling. Must be at least +# MIN_SPEED. +MAX_SPEED = 2.0 +# Bias for the random fall-speed distribution. Keep it between MIN_SPEED and MAX_SPEED; +# this is not an exact mean. +AVG_SPEED = 0.8 +# Minimum glyph trail lifetime in animation frames; minimum 1. Visible trail length also +# depends on fall speed. +MIN_TRAIL = 4 +# Maximum glyph trail lifetime in animation frames. Must be at least MIN_TRAIL. +MAX_TRAIL = 28 +# --- Initial rain density --- +# These settings seed the initial animation; they do not enforce an ongoing +# fraction of active columns as trails respawn. +# With DEPTH_LAYERS=1, the midpoint of MIN_DENSITY and MAX_DENSITY seeds active columns. +# Valid range 0–1. Ignored for multiple layers. +MIN_DENSITY = 0.25 +# Upper density value for the single-layer midpoint. Valid between MIN_DENSITY and 1. +MAX_DENSITY = 0.45 +# With DEPTH_LAYERS>1, initial active-column probability in the far layer. Intermediate +# layers interpolate toward LAYER_NEAR_DENSITY. Valid range 0–2; values >=1 seed every +# eligible column. +LAYER_FAR_DENSITY = 0.35 +# With DEPTH_LAYERS>1, initial active-column probability in the near layer. Normal range +# 0–1 (valid range 0–2). Ignored in single-layer mode. +LAYER_NEAR_DENSITY = 0.15 +# --- Leading glyph behavior --- +# Minimum frames before a head glyph may change while it stays in one row. Valid range +# 0–120; entering a new row chooses a new glyph regardless. +HEAD_HOLD_FRAMES = 5 +# Probability per eligible frame of changing the head glyph, valid range 0–1. 0 disables +# these stationary changes. +HEAD_MUTATION_CHANCE = 0.15 +# Minimum white blend for heads in non-adaptive colors, valid range 0–1. 0 uses the trail +# color; 1 uses white. Adaptive modes use their palette instead. +HEAD_WHITE_MIN = 0.75 +# Maximum white blend, valid range 0–1. If the minimum exceeds the maximum, the config +# is rejected. +HEAD_WHITE_MAX = 1.00 +[output_timeouts] +# Unfocused idle delay by physical connector; FOCUSED_TIMEOUT still applies to the focused monitor. Positive seconds; 0 falls back to TIMEOUT. +# Use physical names such as DP-1, even when they share VIRTUAL-1. +#HDMI-A-1 = 600 +#DP-1 = 300 +[output_colors] +# Same color modes as COLOR; a value here takes precedence on this monitor. +# Use physical names such as DP-1, even when they share VIRTUAL-1. +#HDMI-A-1 = "adaptive" +#DP-1 = "random_trail_hex" +[output_power_off] +# Per-monitor override of POWER_OFF: false keeps instant wake, true powers off after rain. +# Use physical names such as DP-1, even when they share VIRTUAL-1. +#HDMI-A-1 = false +#DP-1 = true + +[alerts] +# Temporary status colors submitted by programs or agents; does not activate rain. +ENABLED = true +# Allow colors on native lock surfaces; independent from authentication. +SHOW_WHILE_LOCKED = false + +[alerts.info] +COLOR = "#00bfff" +PRIORITY = 10 +PULSE = 0.0 # Gentle brightness reduction, 0..0.25, with a two-second period. +[alerts.success] +COLOR = "#40ff80" +PRIORITY = 20 +PULSE = 0.0 +[alerts.warning] +COLOR = "#ffb000" +PRIORITY = 30 +PULSE = 0.0 +[alerts.error] +COLOR = "#ff4040" +PRIORITY = 40 +PULSE = 0.0 + +[audio] +# Requires the optional PipeWire helper. Capture runs only with eligible visible rain. +ENABLED = false +SOURCE = "default-output" # Or the PipeWire node.name of a playback sink. +MODE = "energy" # RMS/peak response; spectrum/beat modes are not implemented. +SHOW_WHILE_LOCKED = false +OUTPUTS = [] # Physical connectors; empty means all eligible monitors. +SPEED_AMOUNT = 0.25 # Maximum proportional increase, valid range 0..2. +BRIGHTNESS_AMOUNT = 0.10 # Maximum proportional increase, valid range 0..1. +ATTACK_MS = 50.0 # Envelope rise time, 1..2000 ms. +RELEASE_MS = 250.0 # Envelope fall time, 1..5000 ms. +GAIN = 4.0 # Measurement gain, 0.01..100; normalized output is capped at 1. +NOISE_GATE = 0.005 # RMS below this level produces no modulation; 0..1. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-idle-notify-v1-client-protocol.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-idle-notify-v1-client-protocol.h new file mode 100644 index 000000000000..19f2a6dd7f20 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-idle-notify-v1-client-protocol.h @@ -0,0 +1,323 @@ +/* Generated by wayland-scanner 1.26.0 */ + +#ifndef EXT_IDLE_NOTIFY_V1_CLIENT_PROTOCOL_H +#define EXT_IDLE_NOTIFY_V1_CLIENT_PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_ext_idle_notify_v1 The ext_idle_notify_v1 protocol + * @section page_ifaces_ext_idle_notify_v1 Interfaces + * - @subpage page_iface_ext_idle_notifier_v1 - idle notification manager + * - @subpage page_iface_ext_idle_notification_v1 - idle notification + * @section page_copyright_ext_idle_notify_v1 Copyright + * <pre> + * + * Copyright © 2015 Martin Gräßlin + * Copyright © 2022 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 (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. + * </pre> + */ +struct ext_idle_notification_v1; +struct ext_idle_notifier_v1; +struct wl_seat; + +#ifndef EXT_IDLE_NOTIFIER_V1_INTERFACE +#define EXT_IDLE_NOTIFIER_V1_INTERFACE +/** + * @page page_iface_ext_idle_notifier_v1 ext_idle_notifier_v1 + * @section page_iface_ext_idle_notifier_v1_desc Description + * + * This interface allows clients to monitor user idle status. + * + * After binding to this global, clients can create ext_idle_notification_v1 + * objects to get notified when the user is idle for a given amount of time. + * @section page_iface_ext_idle_notifier_v1_api API + * See @ref iface_ext_idle_notifier_v1. + */ +/** + * @defgroup iface_ext_idle_notifier_v1 The ext_idle_notifier_v1 interface + * + * This interface allows clients to monitor user idle status. + * + * After binding to this global, clients can create ext_idle_notification_v1 + * objects to get notified when the user is idle for a given amount of time. + */ +extern const struct wl_interface ext_idle_notifier_v1_interface; +#endif +#ifndef EXT_IDLE_NOTIFICATION_V1_INTERFACE +#define EXT_IDLE_NOTIFICATION_V1_INTERFACE +/** + * @page page_iface_ext_idle_notification_v1 ext_idle_notification_v1 + * @section page_iface_ext_idle_notification_v1_desc Description + * + * This interface is used by the compositor to send idle notification events + * to clients. + * + * Initially the notification object is not idle. The notification object + * becomes idle when no user activity has happened for at least the timeout + * duration, starting from the creation of the notification object. User + * activity may include input events or a presence sensor, but is + * compositor-specific. + * + * How this notification responds to idle inhibitors depends on how + * it was constructed. If constructed from the + * get_idle_notification request, then if an idle inhibitor is + * active (e.g. another client has created a zwp_idle_inhibitor_v1 + * on a visible surface), the compositor must not make the + * notification object idle. However, if constructed from the + * get_input_idle_notification request, then idle inhibitors are + * ignored, and only input from the user, e.g. from a keyboard or + * mouse, counts as activity. + * + * When the notification object becomes idle, an idled event is sent. When + * user activity starts again, the notification object stops being idle, + * a resumed event is sent and the timeout is restarted. + * @section page_iface_ext_idle_notification_v1_api API + * See @ref iface_ext_idle_notification_v1. + */ +/** + * @defgroup iface_ext_idle_notification_v1 The ext_idle_notification_v1 interface + * + * This interface is used by the compositor to send idle notification events + * to clients. + * + * Initially the notification object is not idle. The notification object + * becomes idle when no user activity has happened for at least the timeout + * duration, starting from the creation of the notification object. User + * activity may include input events or a presence sensor, but is + * compositor-specific. + * + * How this notification responds to idle inhibitors depends on how + * it was constructed. If constructed from the + * get_idle_notification request, then if an idle inhibitor is + * active (e.g. another client has created a zwp_idle_inhibitor_v1 + * on a visible surface), the compositor must not make the + * notification object idle. However, if constructed from the + * get_input_idle_notification request, then idle inhibitors are + * ignored, and only input from the user, e.g. from a keyboard or + * mouse, counts as activity. + * + * When the notification object becomes idle, an idled event is sent. When + * user activity starts again, the notification object stops being idle, + * a resumed event is sent and the timeout is restarted. + */ +extern const struct wl_interface ext_idle_notification_v1_interface; +#endif + +#define EXT_IDLE_NOTIFIER_V1_DESTROY 0 +#define EXT_IDLE_NOTIFIER_V1_GET_IDLE_NOTIFICATION 1 +#define EXT_IDLE_NOTIFIER_V1_GET_INPUT_IDLE_NOTIFICATION 2 + + +/** + * @ingroup iface_ext_idle_notifier_v1 + */ +#define EXT_IDLE_NOTIFIER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_ext_idle_notifier_v1 + */ +#define EXT_IDLE_NOTIFIER_V1_GET_IDLE_NOTIFICATION_SINCE_VERSION 1 +/** + * @ingroup iface_ext_idle_notifier_v1 + */ +#define EXT_IDLE_NOTIFIER_V1_GET_INPUT_IDLE_NOTIFICATION_SINCE_VERSION 2 + +/** @ingroup iface_ext_idle_notifier_v1 */ +static inline void +ext_idle_notifier_v1_set_user_data(struct ext_idle_notifier_v1 *ext_idle_notifier_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) ext_idle_notifier_v1, user_data); +} + +/** @ingroup iface_ext_idle_notifier_v1 */ +static inline void * +ext_idle_notifier_v1_get_user_data(struct ext_idle_notifier_v1 *ext_idle_notifier_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) ext_idle_notifier_v1); +} + +static inline uint32_t +ext_idle_notifier_v1_get_version(struct ext_idle_notifier_v1 *ext_idle_notifier_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) ext_idle_notifier_v1); +} + +/** + * @ingroup iface_ext_idle_notifier_v1 + * + * Destroy the manager object. All objects created via this interface + * remain valid. + */ +static inline void +ext_idle_notifier_v1_destroy(struct ext_idle_notifier_v1 *ext_idle_notifier_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_idle_notifier_v1, + EXT_IDLE_NOTIFIER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_idle_notifier_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_ext_idle_notifier_v1 + * + * Create a new idle notification object. + * + * The notification object has a minimum timeout duration and is tied to a + * seat. The client will be notified if the seat is inactive for at least + * the provided timeout. See ext_idle_notification_v1 for more details. + * + * A zero timeout is valid and means the client wants to be notified as + * soon as possible when the seat is inactive. + */ +static inline struct ext_idle_notification_v1 * +ext_idle_notifier_v1_get_idle_notification(struct ext_idle_notifier_v1 *ext_idle_notifier_v1, uint32_t timeout, struct wl_seat *seat) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) ext_idle_notifier_v1, + EXT_IDLE_NOTIFIER_V1_GET_IDLE_NOTIFICATION, &ext_idle_notification_v1_interface, wl_proxy_get_version((struct wl_proxy *) ext_idle_notifier_v1), 0, NULL, timeout, seat); + + return (struct ext_idle_notification_v1 *) id; +} + +/** + * @ingroup iface_ext_idle_notifier_v1 + * + * Create a new idle notification object to track input from the + * user, such as keyboard and mouse movement. Because this object is + * meant to track user input alone, it ignores idle inhibitors. + * + * The notification object has a minimum timeout duration and is tied to a + * seat. The client will be notified if the seat is inactive for at least + * the provided timeout. See ext_idle_notification_v1 for more details. + * + * A zero timeout is valid and means the client wants to be notified as + * soon as possible when the seat is inactive. + */ +static inline struct ext_idle_notification_v1 * +ext_idle_notifier_v1_get_input_idle_notification(struct ext_idle_notifier_v1 *ext_idle_notifier_v1, uint32_t timeout, struct wl_seat *seat) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) ext_idle_notifier_v1, + EXT_IDLE_NOTIFIER_V1_GET_INPUT_IDLE_NOTIFICATION, &ext_idle_notification_v1_interface, wl_proxy_get_version((struct wl_proxy *) ext_idle_notifier_v1), 0, NULL, timeout, seat); + + return (struct ext_idle_notification_v1 *) id; +} + +/** + * @ingroup iface_ext_idle_notification_v1 + * @struct ext_idle_notification_v1_listener + */ +struct ext_idle_notification_v1_listener { + /** + * notification object is idle + * + * This event is sent when the notification object becomes idle. + * + * It's a compositor protocol error to send this event twice + * without a resumed event in-between. + */ + void (*idled)(void *data, + struct ext_idle_notification_v1 *ext_idle_notification_v1); + /** + * notification object is no longer idle + * + * This event is sent when the notification object stops being + * idle. + * + * It's a compositor protocol error to send this event twice + * without an idled event in-between. It's a compositor protocol + * error to send this event prior to any idled event. + */ + void (*resumed)(void *data, + struct ext_idle_notification_v1 *ext_idle_notification_v1); +}; + +/** + * @ingroup iface_ext_idle_notification_v1 + */ +static inline int +ext_idle_notification_v1_add_listener(struct ext_idle_notification_v1 *ext_idle_notification_v1, + const struct ext_idle_notification_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) ext_idle_notification_v1, + (void (**)(void)) listener, data); +} + +#define EXT_IDLE_NOTIFICATION_V1_DESTROY 0 + +/** + * @ingroup iface_ext_idle_notification_v1 + */ +#define EXT_IDLE_NOTIFICATION_V1_IDLED_SINCE_VERSION 1 +/** + * @ingroup iface_ext_idle_notification_v1 + */ +#define EXT_IDLE_NOTIFICATION_V1_RESUMED_SINCE_VERSION 1 + +/** + * @ingroup iface_ext_idle_notification_v1 + */ +#define EXT_IDLE_NOTIFICATION_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_ext_idle_notification_v1 */ +static inline void +ext_idle_notification_v1_set_user_data(struct ext_idle_notification_v1 *ext_idle_notification_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) ext_idle_notification_v1, user_data); +} + +/** @ingroup iface_ext_idle_notification_v1 */ +static inline void * +ext_idle_notification_v1_get_user_data(struct ext_idle_notification_v1 *ext_idle_notification_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) ext_idle_notification_v1); +} + +static inline uint32_t +ext_idle_notification_v1_get_version(struct ext_idle_notification_v1 *ext_idle_notification_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) ext_idle_notification_v1); +} + +/** + * @ingroup iface_ext_idle_notification_v1 + * + * Destroy the notification object. + */ +static inline void +ext_idle_notification_v1_destroy(struct ext_idle_notification_v1 *ext_idle_notification_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_idle_notification_v1, + EXT_IDLE_NOTIFICATION_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_idle_notification_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-idle-notify-v1-protocol.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-idle-notify-v1-protocol.c new file mode 100644 index 000000000000..56871e977d6b --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-idle-notify-v1-protocol.c @@ -0,0 +1,80 @@ +/* Generated by wayland-scanner 1.26.0 */ + +/* + * Copyright © 2015 Martin Gräßlin + * Copyright © 2022 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 (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. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface ext_idle_notification_v1_interface; +extern const struct wl_interface wl_seat_interface; + +static const struct wl_interface *ext_idle_notify_v1_types[] = { + &ext_idle_notification_v1_interface, + NULL, + &wl_seat_interface, + &ext_idle_notification_v1_interface, + NULL, + &wl_seat_interface, +}; + +static const struct wl_message ext_idle_notifier_v1_requests[] = { + { "destroy", "", ext_idle_notify_v1_types + 0 }, + { "get_idle_notification", "nuo", ext_idle_notify_v1_types + 0 }, + { "get_input_idle_notification", "2nuo", ext_idle_notify_v1_types + 3 }, +}; + +WL_PRIVATE const struct wl_interface ext_idle_notifier_v1_interface = { + "ext_idle_notifier_v1", 2, + 3, ext_idle_notifier_v1_requests, + 0, NULL, +}; + +static const struct wl_message ext_idle_notification_v1_requests[] = { + { "destroy", "", ext_idle_notify_v1_types + 0 }, +}; + +static const struct wl_message ext_idle_notification_v1_events[] = { + { "idled", "", ext_idle_notify_v1_types + 0 }, + { "resumed", "", ext_idle_notify_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface ext_idle_notification_v1_interface = { + "ext_idle_notification_v1", 2, + 1, ext_idle_notification_v1_requests, + 2, ext_idle_notification_v1_events, +}; + diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-session-lock-v1-client-protocol.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-session-lock-v1-client-protocol.h new file mode 100644 index 000000000000..ac2da228101d --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-session-lock-v1-client-protocol.h @@ -0,0 +1,684 @@ +/* Generated by wayland-scanner 1.26.0 */ + +#ifndef EXT_SESSION_LOCK_V1_CLIENT_PROTOCOL_H +#define EXT_SESSION_LOCK_V1_CLIENT_PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_ext_session_lock_v1 The ext_session_lock_v1 protocol + * secure session locking with arbitrary graphics + * + * @section page_desc_ext_session_lock_v1 Description + * + * This protocol allows for a privileged Wayland client to lock the session + * and display arbitrary graphics while the session is locked. + * + * The compositor may choose to restrict this protocol to a special client + * launched by the compositor itself or expose it to all privileged clients, + * this is compositor policy. + * + * The client is responsible for performing authentication and informing the + * compositor when the session should be unlocked. If the client dies while + * the session is locked the session remains locked, possibly permanently + * depending on compositor policy. + * + * The key words "must", "must not", "required", "shall", "shall not", + * "should", "should not", "recommended", "may", and "optional" in this + * document are to be interpreted as described in IETF RFC 2119. + * + * Warning! The protocol described in this file is currently in the + * testing phase. Backward compatible changes may be added together with + * the corresponding interface version bump. Backward incompatible changes + * can only be done by creating a new major version of the extension. + * + * @section page_ifaces_ext_session_lock_v1 Interfaces + * - @subpage page_iface_ext_session_lock_manager_v1 - used to lock the session + * - @subpage page_iface_ext_session_lock_v1 - manage lock state and create lock surfaces + * - @subpage page_iface_ext_session_lock_surface_v1 - a surface displayed while the session is locked + * @section page_copyright_ext_session_lock_v1 Copyright + * <pre> + * + * Copyright 2021 Isaac Freund + * + * 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. + * </pre> + */ +struct ext_session_lock_manager_v1; +struct ext_session_lock_surface_v1; +struct ext_session_lock_v1; +struct wl_output; +struct wl_surface; + +#ifndef EXT_SESSION_LOCK_MANAGER_V1_INTERFACE +#define EXT_SESSION_LOCK_MANAGER_V1_INTERFACE +/** + * @page page_iface_ext_session_lock_manager_v1 ext_session_lock_manager_v1 + * @section page_iface_ext_session_lock_manager_v1_desc Description + * + * This interface is used to request that the session be locked. + * @section page_iface_ext_session_lock_manager_v1_api API + * See @ref iface_ext_session_lock_manager_v1. + */ +/** + * @defgroup iface_ext_session_lock_manager_v1 The ext_session_lock_manager_v1 interface + * + * This interface is used to request that the session be locked. + */ +extern const struct wl_interface ext_session_lock_manager_v1_interface; +#endif +#ifndef EXT_SESSION_LOCK_V1_INTERFACE +#define EXT_SESSION_LOCK_V1_INTERFACE +/** + * @page page_iface_ext_session_lock_v1 ext_session_lock_v1 + * @section page_iface_ext_session_lock_v1_desc Description + * + * In response to the creation of this object the compositor must send + * either the locked or finished event. + * + * The locked event indicates that the session is locked. This means + * that the compositor must stop rendering and providing input to normal + * clients. Instead the compositor must blank all outputs with an opaque + * color such that their normal content is fully hidden. + * + * The only surfaces that should be rendered while the session is locked + * are the lock surfaces created through this interface and optionally, + * at the compositor's discretion, special privileged surfaces such as + * input methods or portions of desktop shell UIs. + * + * The locked event must not be sent until a new "locked" frame (either + * from a session lock surface or the compositor blanking the output) has + * been presented on all outputs and no security sensitive normal/unlocked + * content is possibly visible. + * + * The finished event should be sent immediately on creation of this + * object if the compositor decides that the locked event will not be sent. + * + * The compositor may wait for the client to create and render session lock + * surfaces before sending the locked event to avoid displaying intermediate + * blank frames. However, it must impose a reasonable time limit if + * waiting and send the locked event as soon as the hard requirements + * described above can be met if the time limit expires. Clients should + * immediately create lock surfaces for all outputs on creation of this + * object to make this possible. + * + * This behavior of the locked event is required in order to prevent + * possible race conditions with clients that wish to suspend the system + * or similar after locking the session. Without these semantics, clients + * triggering a suspend after receiving the locked event would race with + * the first "locked" frame being presented and normal/unlocked frames + * might be briefly visible as the system is resumed if the suspend + * operation wins the race. + * + * If the client dies while the session is locked, the compositor must not + * unlock the session in response. It is acceptable for the session to be + * permanently locked if this happens. The compositor may choose to continue + * to display the lock surfaces the client had mapped before it died or + * alternatively fall back to a solid color, this is compositor policy. + * + * Compositors may also allow a secure way to recover the session, the + * details of this are compositor policy. Compositors may allow a new + * client to create a ext_session_lock_v1 object and take responsibility + * for unlocking the session, they may even start a new lock client + * instance automatically. + * @section page_iface_ext_session_lock_v1_api API + * See @ref iface_ext_session_lock_v1. + */ +/** + * @defgroup iface_ext_session_lock_v1 The ext_session_lock_v1 interface + * + * In response to the creation of this object the compositor must send + * either the locked or finished event. + * + * The locked event indicates that the session is locked. This means + * that the compositor must stop rendering and providing input to normal + * clients. Instead the compositor must blank all outputs with an opaque + * color such that their normal content is fully hidden. + * + * The only surfaces that should be rendered while the session is locked + * are the lock surfaces created through this interface and optionally, + * at the compositor's discretion, special privileged surfaces such as + * input methods or portions of desktop shell UIs. + * + * The locked event must not be sent until a new "locked" frame (either + * from a session lock surface or the compositor blanking the output) has + * been presented on all outputs and no security sensitive normal/unlocked + * content is possibly visible. + * + * The finished event should be sent immediately on creation of this + * object if the compositor decides that the locked event will not be sent. + * + * The compositor may wait for the client to create and render session lock + * surfaces before sending the locked event to avoid displaying intermediate + * blank frames. However, it must impose a reasonable time limit if + * waiting and send the locked event as soon as the hard requirements + * described above can be met if the time limit expires. Clients should + * immediately create lock surfaces for all outputs on creation of this + * object to make this possible. + * + * This behavior of the locked event is required in order to prevent + * possible race conditions with clients that wish to suspend the system + * or similar after locking the session. Without these semantics, clients + * triggering a suspend after receiving the locked event would race with + * the first "locked" frame being presented and normal/unlocked frames + * might be briefly visible as the system is resumed if the suspend + * operation wins the race. + * + * If the client dies while the session is locked, the compositor must not + * unlock the session in response. It is acceptable for the session to be + * permanently locked if this happens. The compositor may choose to continue + * to display the lock surfaces the client had mapped before it died or + * alternatively fall back to a solid color, this is compositor policy. + * + * Compositors may also allow a secure way to recover the session, the + * details of this are compositor policy. Compositors may allow a new + * client to create a ext_session_lock_v1 object and take responsibility + * for unlocking the session, they may even start a new lock client + * instance automatically. + */ +extern const struct wl_interface ext_session_lock_v1_interface; +#endif +#ifndef EXT_SESSION_LOCK_SURFACE_V1_INTERFACE +#define EXT_SESSION_LOCK_SURFACE_V1_INTERFACE +/** + * @page page_iface_ext_session_lock_surface_v1 ext_session_lock_surface_v1 + * @section page_iface_ext_session_lock_surface_v1_desc Description + * + * The client may use lock surfaces to display a screensaver, render a + * dialog to enter a password and unlock the session, or however else it + * sees fit. + * + * On binding this interface the compositor will immediately send the + * first configure event. After making the ack_configure request in + * response to this event the client should attach and commit the first + * buffer. Committing the surface before acking the first configure is a + * protocol error. Committing the surface with a null buffer at any time + * is a protocol error. + * + * The compositor is free to handle keyboard/pointer focus for lock + * surfaces however it chooses. A reasonable way to do this would be to + * give the first lock surface created keyboard focus and change keyboard + * focus if the user clicks on other surfaces. + * @section page_iface_ext_session_lock_surface_v1_api API + * See @ref iface_ext_session_lock_surface_v1. + */ +/** + * @defgroup iface_ext_session_lock_surface_v1 The ext_session_lock_surface_v1 interface + * + * The client may use lock surfaces to display a screensaver, render a + * dialog to enter a password and unlock the session, or however else it + * sees fit. + * + * On binding this interface the compositor will immediately send the + * first configure event. After making the ack_configure request in + * response to this event the client should attach and commit the first + * buffer. Committing the surface before acking the first configure is a + * protocol error. Committing the surface with a null buffer at any time + * is a protocol error. + * + * The compositor is free to handle keyboard/pointer focus for lock + * surfaces however it chooses. A reasonable way to do this would be to + * give the first lock surface created keyboard focus and change keyboard + * focus if the user clicks on other surfaces. + */ +extern const struct wl_interface ext_session_lock_surface_v1_interface; +#endif + +#define EXT_SESSION_LOCK_MANAGER_V1_DESTROY 0 +#define EXT_SESSION_LOCK_MANAGER_V1_LOCK 1 + + +/** + * @ingroup iface_ext_session_lock_manager_v1 + */ +#define EXT_SESSION_LOCK_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_ext_session_lock_manager_v1 + */ +#define EXT_SESSION_LOCK_MANAGER_V1_LOCK_SINCE_VERSION 1 + +/** @ingroup iface_ext_session_lock_manager_v1 */ +static inline void +ext_session_lock_manager_v1_set_user_data(struct ext_session_lock_manager_v1 *ext_session_lock_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) ext_session_lock_manager_v1, user_data); +} + +/** @ingroup iface_ext_session_lock_manager_v1 */ +static inline void * +ext_session_lock_manager_v1_get_user_data(struct ext_session_lock_manager_v1 *ext_session_lock_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) ext_session_lock_manager_v1); +} + +static inline uint32_t +ext_session_lock_manager_v1_get_version(struct ext_session_lock_manager_v1 *ext_session_lock_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) ext_session_lock_manager_v1); +} + +/** + * @ingroup iface_ext_session_lock_manager_v1 + * + * This informs the compositor that the session lock manager object will + * no longer be used. Existing objects created through this interface + * remain valid. + */ +static inline void +ext_session_lock_manager_v1_destroy(struct ext_session_lock_manager_v1 *ext_session_lock_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_session_lock_manager_v1, + EXT_SESSION_LOCK_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_session_lock_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_ext_session_lock_manager_v1 + * + * This request creates a session lock and asks the compositor to lock the + * session. The compositor will send either the ext_session_lock_v1.locked + * or ext_session_lock_v1.finished event on the created object in + * response to this request. + */ +static inline struct ext_session_lock_v1 * +ext_session_lock_manager_v1_lock(struct ext_session_lock_manager_v1 *ext_session_lock_manager_v1) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) ext_session_lock_manager_v1, + EXT_SESSION_LOCK_MANAGER_V1_LOCK, &ext_session_lock_v1_interface, wl_proxy_get_version((struct wl_proxy *) ext_session_lock_manager_v1), 0, NULL); + + return (struct ext_session_lock_v1 *) id; +} + +#ifndef EXT_SESSION_LOCK_V1_ERROR_ENUM +#define EXT_SESSION_LOCK_V1_ERROR_ENUM +enum ext_session_lock_v1_error { + /** + * attempted to destroy session lock while locked + */ + EXT_SESSION_LOCK_V1_ERROR_INVALID_DESTROY = 0, + /** + * unlock requested but locked event was never sent + */ + EXT_SESSION_LOCK_V1_ERROR_INVALID_UNLOCK = 1, + /** + * given wl_surface already has a role + */ + EXT_SESSION_LOCK_V1_ERROR_ROLE = 2, + /** + * given output already has a lock surface + */ + EXT_SESSION_LOCK_V1_ERROR_DUPLICATE_OUTPUT = 3, + /** + * given wl_surface has a buffer attached or committed + */ + EXT_SESSION_LOCK_V1_ERROR_ALREADY_CONSTRUCTED = 4, +}; +#endif /* EXT_SESSION_LOCK_V1_ERROR_ENUM */ + +/** + * @ingroup iface_ext_session_lock_v1 + * @struct ext_session_lock_v1_listener + */ +struct ext_session_lock_v1_listener { + /** + * session successfully locked + * + * This client is now responsible for displaying graphics while + * the session is locked and deciding when to unlock the session. + * + * The locked event must not be sent until a new "locked" frame has + * been presented on all outputs and no security sensitive + * normal/unlocked content is possibly visible. + * + * If this event is sent, making the destroy request is a protocol + * error, the lock object must be destroyed using the + * unlock_and_destroy request. + */ + void (*locked)(void *data, + struct ext_session_lock_v1 *ext_session_lock_v1); + /** + * the session lock object should be destroyed + * + * The compositor has decided that the session lock should be + * destroyed as it will no longer be used by the compositor. + * Exactly when this event is sent is compositor policy, but it + * must never be sent more than once for a given session lock + * object. + * + * This might be sent because there is already another + * ext_session_lock_v1 object held by a client, or the compositor + * has decided to deny the request to lock the session for some + * other reason. This might also be sent because the compositor + * implements some alternative, secure way to authenticate and + * unlock the session. + * + * The finished event should be sent immediately on creation of + * this object if the compositor decides that the locked event will + * not be sent. + * + * If the locked event is sent on creation of this object the + * finished event may still be sent at some later time in this + * object's lifetime. This is compositor policy. + * + * Upon receiving this event, the client should make either the + * destroy request or the unlock_and_destroy request, depending on + * whether or not the locked event was received on this object. + */ + void (*finished)(void *data, + struct ext_session_lock_v1 *ext_session_lock_v1); +}; + +/** + * @ingroup iface_ext_session_lock_v1 + */ +static inline int +ext_session_lock_v1_add_listener(struct ext_session_lock_v1 *ext_session_lock_v1, + const struct ext_session_lock_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) ext_session_lock_v1, + (void (**)(void)) listener, data); +} + +#define EXT_SESSION_LOCK_V1_DESTROY 0 +#define EXT_SESSION_LOCK_V1_GET_LOCK_SURFACE 1 +#define EXT_SESSION_LOCK_V1_UNLOCK_AND_DESTROY 2 + +/** + * @ingroup iface_ext_session_lock_v1 + */ +#define EXT_SESSION_LOCK_V1_LOCKED_SINCE_VERSION 1 +/** + * @ingroup iface_ext_session_lock_v1 + */ +#define EXT_SESSION_LOCK_V1_FINISHED_SINCE_VERSION 1 + +/** + * @ingroup iface_ext_session_lock_v1 + */ +#define EXT_SESSION_LOCK_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_ext_session_lock_v1 + */ +#define EXT_SESSION_LOCK_V1_GET_LOCK_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_ext_session_lock_v1 + */ +#define EXT_SESSION_LOCK_V1_UNLOCK_AND_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_ext_session_lock_v1 */ +static inline void +ext_session_lock_v1_set_user_data(struct ext_session_lock_v1 *ext_session_lock_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) ext_session_lock_v1, user_data); +} + +/** @ingroup iface_ext_session_lock_v1 */ +static inline void * +ext_session_lock_v1_get_user_data(struct ext_session_lock_v1 *ext_session_lock_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) ext_session_lock_v1); +} + +static inline uint32_t +ext_session_lock_v1_get_version(struct ext_session_lock_v1 *ext_session_lock_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) ext_session_lock_v1); +} + +/** + * @ingroup iface_ext_session_lock_v1 + * + * This informs the compositor that the lock object will no longer be + * used. Existing objects created through this interface remain valid. + * + * After this request is made, lock surfaces created through this object + * should be destroyed by the client as they will no longer be used by + * the compositor. + * + * It is a protocol error to make this request if the locked event was + * sent, the unlock_and_destroy request must be used instead. + */ +static inline void +ext_session_lock_v1_destroy(struct ext_session_lock_v1 *ext_session_lock_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_session_lock_v1, + EXT_SESSION_LOCK_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_session_lock_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_ext_session_lock_v1 + * + * The client is expected to create lock surfaces for all outputs + * currently present and any new outputs as they are advertised. These + * won't be displayed by the compositor unless the lock is successful + * and the locked event is sent. + * + * Providing a wl_surface which already has a role or already has a buffer + * attached or committed is a protocol error, as is attaching/committing + * a buffer before the first ext_session_lock_surface_v1.configure event. + * + * Attempting to create more than one lock surface for a given output + * is a duplicate_output protocol error. + */ +static inline struct ext_session_lock_surface_v1 * +ext_session_lock_v1_get_lock_surface(struct ext_session_lock_v1 *ext_session_lock_v1, struct wl_surface *surface, struct wl_output *output) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) ext_session_lock_v1, + EXT_SESSION_LOCK_V1_GET_LOCK_SURFACE, &ext_session_lock_surface_v1_interface, wl_proxy_get_version((struct wl_proxy *) ext_session_lock_v1), 0, NULL, surface, output); + + return (struct ext_session_lock_surface_v1 *) id; +} + +/** + * @ingroup iface_ext_session_lock_v1 + * + * This request indicates that the session should be unlocked, for + * example because the user has entered their password and it has been + * verified by the client. + * + * This request also informs the compositor that the lock object will + * no longer be used and should be destroyed. Existing objects created + * through this interface remain valid. + * + * After this request is made, lock surfaces created through this object + * should be destroyed by the client as they will no longer be used by + * the compositor. + * + * It is a protocol error to make this request if the locked event has + * not been sent. In that case, the lock object must be destroyed using + * the destroy request. + * + * Note that a correct client that wishes to exit directly after unlocking + * the session must use the wl_display.sync request to ensure the server + * receives and processes the unlock_and_destroy request. Otherwise + * there is no guarantee that the server has unlocked the session due + * to the asynchronous nature of the Wayland protocol. For example, + * the server might terminate the client with a protocol error before + * it processes the unlock_and_destroy request. + */ +static inline void +ext_session_lock_v1_unlock_and_destroy(struct ext_session_lock_v1 *ext_session_lock_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_session_lock_v1, + EXT_SESSION_LOCK_V1_UNLOCK_AND_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_session_lock_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifndef EXT_SESSION_LOCK_SURFACE_V1_ERROR_ENUM +#define EXT_SESSION_LOCK_SURFACE_V1_ERROR_ENUM +enum ext_session_lock_surface_v1_error { + /** + * surface committed before first ack_configure request + */ + EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK = 0, + /** + * surface committed with a null buffer + */ + EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER = 1, + /** + * failed to match ack'd width/height + */ + EXT_SESSION_LOCK_SURFACE_V1_ERROR_DIMENSIONS_MISMATCH = 2, + /** + * serial provided in ack_configure is invalid + */ + EXT_SESSION_LOCK_SURFACE_V1_ERROR_INVALID_SERIAL = 3, +}; +#endif /* EXT_SESSION_LOCK_SURFACE_V1_ERROR_ENUM */ + +/** + * @ingroup iface_ext_session_lock_surface_v1 + * @struct ext_session_lock_surface_v1_listener + */ +struct ext_session_lock_surface_v1_listener { + /** + * the client should resize its surface + * + * This event is sent once on binding the interface and may be + * sent again at the compositor's discretion, for example if output + * geometry changes. + * + * The width and height are in surface-local coordinates and are + * exact requirements. Failing to match these surface dimensions in + * the next commit after acking a configure is a protocol error. + * @param serial serial for use in ack_configure + */ + void (*configure)(void *data, + struct ext_session_lock_surface_v1 *ext_session_lock_surface_v1, + uint32_t serial, + uint32_t width, + uint32_t height); +}; + +/** + * @ingroup iface_ext_session_lock_surface_v1 + */ +static inline int +ext_session_lock_surface_v1_add_listener(struct ext_session_lock_surface_v1 *ext_session_lock_surface_v1, + const struct ext_session_lock_surface_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) ext_session_lock_surface_v1, + (void (**)(void)) listener, data); +} + +#define EXT_SESSION_LOCK_SURFACE_V1_DESTROY 0 +#define EXT_SESSION_LOCK_SURFACE_V1_ACK_CONFIGURE 1 + +/** + * @ingroup iface_ext_session_lock_surface_v1 + */ +#define EXT_SESSION_LOCK_SURFACE_V1_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_ext_session_lock_surface_v1 + */ +#define EXT_SESSION_LOCK_SURFACE_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_ext_session_lock_surface_v1 + */ +#define EXT_SESSION_LOCK_SURFACE_V1_ACK_CONFIGURE_SINCE_VERSION 1 + +/** @ingroup iface_ext_session_lock_surface_v1 */ +static inline void +ext_session_lock_surface_v1_set_user_data(struct ext_session_lock_surface_v1 *ext_session_lock_surface_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) ext_session_lock_surface_v1, user_data); +} + +/** @ingroup iface_ext_session_lock_surface_v1 */ +static inline void * +ext_session_lock_surface_v1_get_user_data(struct ext_session_lock_surface_v1 *ext_session_lock_surface_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) ext_session_lock_surface_v1); +} + +static inline uint32_t +ext_session_lock_surface_v1_get_version(struct ext_session_lock_surface_v1 *ext_session_lock_surface_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) ext_session_lock_surface_v1); +} + +/** + * @ingroup iface_ext_session_lock_surface_v1 + * + * This informs the compositor that the lock surface object will no + * longer be used. + * + * It is recommended for a lock client to destroy lock surfaces if + * their corresponding wl_output global is removed. + * + * If a lock surface on an active output is destroyed before the + * ext_session_lock_v1.unlock_and_destroy event is sent, the compositor + * must fall back to rendering a solid color. + */ +static inline void +ext_session_lock_surface_v1_destroy(struct ext_session_lock_surface_v1 *ext_session_lock_surface_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_session_lock_surface_v1, + EXT_SESSION_LOCK_SURFACE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_session_lock_surface_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_ext_session_lock_surface_v1 + * + * When a configure event is received, if a client commits the surface + * in response to the configure event, then the client must make an + * ack_configure request sometime before the commit request, passing + * along the serial of the configure event. + * + * If the client receives multiple configure events before it can + * respond to one, it only has to ack the last configure event. + * + * A client is not required to commit immediately after sending an + * ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before committing, + * but only the last request sent before a commit indicates which + * configure event the client really is responding to. + * + * Sending an ack_configure request consumes the configure event + * referenced by the given serial, as well as all older configure events + * sent on this object. + * + * It is a protocol error to issue multiple ack_configure requests + * referencing the same configure event or to issue an ack_configure + * request referencing a configure event older than the last configure + * event acked for a given lock surface. + */ +static inline void +ext_session_lock_surface_v1_ack_configure(struct ext_session_lock_surface_v1 *ext_session_lock_surface_v1, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_session_lock_surface_v1, + EXT_SESSION_LOCK_SURFACE_V1_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) ext_session_lock_surface_v1), 0, serial); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-session-lock-v1-protocol.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-session-lock-v1-protocol.c new file mode 100644 index 000000000000..5f9541520e54 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/ext-session-lock-v1-protocol.c @@ -0,0 +1,97 @@ +/* Generated by wayland-scanner 1.26.0 */ + +/* + * Copyright 2021 Isaac Freund + * + * 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. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface ext_session_lock_surface_v1_interface; +extern const struct wl_interface ext_session_lock_v1_interface; +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_surface_interface; + +static const struct wl_interface *ext_session_lock_v1_types[] = { + NULL, + NULL, + NULL, + &ext_session_lock_v1_interface, + &ext_session_lock_surface_v1_interface, + &wl_surface_interface, + &wl_output_interface, +}; + +static const struct wl_message ext_session_lock_manager_v1_requests[] = { + { "destroy", "", ext_session_lock_v1_types + 0 }, + { "lock", "n", ext_session_lock_v1_types + 3 }, +}; + +WL_PRIVATE const struct wl_interface ext_session_lock_manager_v1_interface = { + "ext_session_lock_manager_v1", 1, + 2, ext_session_lock_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message ext_session_lock_v1_requests[] = { + { "destroy", "", ext_session_lock_v1_types + 0 }, + { "get_lock_surface", "noo", ext_session_lock_v1_types + 4 }, + { "unlock_and_destroy", "", ext_session_lock_v1_types + 0 }, +}; + +static const struct wl_message ext_session_lock_v1_events[] = { + { "locked", "", ext_session_lock_v1_types + 0 }, + { "finished", "", ext_session_lock_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface ext_session_lock_v1_interface = { + "ext_session_lock_v1", 1, + 3, ext_session_lock_v1_requests, + 2, ext_session_lock_v1_events, +}; + +static const struct wl_message ext_session_lock_surface_v1_requests[] = { + { "destroy", "", ext_session_lock_v1_types + 0 }, + { "ack_configure", "u", ext_session_lock_v1_types + 0 }, +}; + +static const struct wl_message ext_session_lock_surface_v1_events[] = { + { "configure", "uuu", ext_session_lock_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface ext_session_lock_surface_v1_interface = { + "ext_session_lock_surface_v1", 1, + 2, ext_session_lock_surface_v1_requests, + 1, ext_session_lock_surface_v1_events, +}; + diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/hide-desktop.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/hide-desktop.md new file mode 100644 index 000000000000..7ed57b66dff0 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/hide-desktop.md @@ -0,0 +1,42 @@ +# Wallpaper-only rain + +This integration is included in Ambient Background. See the configuration guide for current file paths and the WCM controls for continuous live wallpapers; helper and socket identifiers retain their compatible names. + +See [Configuration](configuration.md) for the full option reference, defaults +and reload rules. + +Add these settings to the `[matrix]` section of your digitalrain configuration: + +```toml +[matrix] +TRANSPARENT = true +HIDE_DESKTOP = true +``` + +Enable `digitalrain-desktop` alongside `ipc` in Wayfire's `[core] plugins` list. +The plugin can be loaded by reloading the Wayfire configuration; a compositor +restart is not required. Restart digitalrain after installing a new binary. +The manager reloads changed settings for the next renderer activation. + +Apps, panels, popups and desktop widgets fade to fully transparent while rain +fades in. The wallpaper stays visible until `TRANSPARENT_TIMEOUT` fades the +background to black. Activity fades the desktop back in as the rain/black overlay +fades away. `FADE_FRAMES` controls both directions at 30 frames per +second: 20 frames is about 0.67 seconds, 30 frames is about one second. + +The packaged default is `HIDE_DESKTOP = false`, which keeps desktop content visible. +The option only takes effect with `TRANSPARENT = true`. Renderers can also opt in +with `ambient-idle --render --output NAME --transparent --hide-desktop`. + +The effect applies separately to each sleeping output and includes newly opened +windows. Window opacity, workspace, minimization and stacking settings are not +rewritten. If activity resumes during the fade-out, components fade back in from +their current opacity without jumping. Session-lock surfaces remain visible +above the saver. + +The renderer holds an IPC connection for the effect's lifetime. Closing that +connection, a renderer crash, plugin unload or output removal immediately releases +the effect; normal activity requests a fade-in first. Once a fade finishes, the +plugin stops its animation timer. Without +the plugin, rain continues with the desktop visible and the renderer reports +that the optional effect is unavailable. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/interfaces-and-locking.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/interfaces-and-locking.md new file mode 100644 index 000000000000..29bdbefc598e --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/interfaces-and-locking.md @@ -0,0 +1,209 @@ +# Local control API and native session locking + +This integration is included in Ambient Background. See the configuration guide for current file paths and the WCM controls for continuous live wallpapers; helper and socket identifiers retain their compatible names. + +Digital Rain 1.2.0 provides native Wayland session locking, a local manager API, +and an optional stdio MCP adapter. The normal saver keeps independent physical +monitor timers. Locking protects the whole session across all outputs. + +## Start and control Digital Rain + +Run the manager once through Wayfire autostart. Commands connect to that manager: + +```sh +ambient-idle --status +ambient-idle --explain +ambient-idle --activate DP-1 +ambient-idle --black DP-1 +ambient-idle --wake DP-1 +ambient-idle --lock +ambient-idle --lock-status +ambient-idle --schema +ambient-idle --watch +``` + +Omit the output on activate/black/wake to target all physical monitors. Connector +names are reported by `--status`, including monitors sharing a virtual cube. +`--black` uses the configured monitor power policy. `--wake` resets the selected +monitor's idle countdown; while locked it can show the password prompt, but +cannot unlock. `--help` lists manager controls; `--render --help` lists renderer +arguments. + +## Native lock screen + +Enable `session-lock` and the current `digitalrain-idle` helper in Wayfire's +existing `[core] plugins` list. The former implements `ext-session-lock-v1`; +the latter reports compositor protection after a locker disconnects, allowing +manager restart and crash recovery. These can be loaded without restarting the +compositor. A shortcut can then run: + +```ini +[command] +binding_lock = <super> <alt> KEY_ESC +command_lock = ambient-idle --lock +``` + +`ambient-idle --lock` starts the independent `ambient-lock` process and waits +for confirmed session locking. Failure or lack of confirmation produces a +nonzero exit status. Starting rain or launching a process alone is not reported +as a confirmed lock. `ambient-lock` can also be run directly without the +manager; automatic recovery requires the manager and lock-status helper. + +The locker renders an opaque selected background inside actual session-lock surfaces above the +normal saver layer. Each logical output has a lock surface; on a stitched output +the prompt is repeated in its physical monitor regions. Newly connected outputs +receive lock surfaces, and resized surfaces follow compositor configuration. + +Type your password and press Enter. Backspace removes a character, Ctrl+U clears +input, and Escape clears and hides the prompt. Caps Lock is indicated. UTF-8 +keyboard input and compose sequences use the compositor's keymap. The prompt +hides and clears unfinished input after 15 seconds without activity. + +A separate unprivileged `ambient-auth` worker checks the current user's +password through `/etc/pam.d/digitalrain`, which includes the system authentication +and account policy. Passwords travel only through its private inherited pipe; +there is no password argument, API field, or MCP authentication tool. The UI +remains responsive while PAM checks the password. This interface supports password +authentication, not interactive multi-step PAM/MFA conversations. + +The selected lock effect starts on black, regardless of the normal saver's transparency +setting. It uses the configured rain appearance; adaptive colors use the saved +black-background palette without sampling desktop content during locking. +WCM’s Lock screen settings choose one physical monitor or all monitors for the +clock and unlock controls, while every output keeps its opaque protected +background. A disconnected selection falls back to an available monitor. The +controls start invisible, fade in on input and fade away after the configurable +idle delay, clearing unfinished passwords. Authentication in progress remains +visible. These settings also control effect selection, appearance, clock/date and +fade-to-black independently of Ambient. When Ambient is unavailable, +`BLACK_RAIN_TIMEOUT` controls when locked rain settles to black; zero keeps rain +running. A black lock surface stops animation. Input shows the prompt without +releasing protection. `POWER_OFF=false` keeps monitors powered for instant response; +opt-in global/per-output power settings work through `digitalrain-power`. + +Only successful PAM authentication requests unlock. Saver signals, normal input, +API wake, or restarting the idle manager do not unlock. If the locker crashes, +Wayfire retains protection; the manager restarts the locker with a bounded retry +delay. Without the manager, a replacement `ambient-lock` can recover the prompt. + +Automatic locking is independent of per-monitor saver timers and disabled by +this default: + +```toml +[global] +LOCK_TIMEOUT = 0 +``` + +A positive value requests a session lock after that many seconds of whole-seat +input inactivity, with approximately the idle notifier/manager scheduling delay. +Application and API saver inhibitors do not suppress automatic security locking. +Per-monitor activity on another screen still postpones the session-wide lock. +`LOCK_CMD` has been removed and is rejected in TOML; no external locker is launched. + +## API version 1 + +The manager listens on `$XDG_RUNTIME_DIR/digitalrain/control.sock`. The runtime +directory must belong to the user and be private. Digital Rain's subdirectory is +mode 0700, sockets are mode 0600, and accepted clients must have the same UID. +The transport is Unix `SOCK_SEQPACKET`: one UTF-8 JSON object per packet, at most +16 KiB. There is no network listener. Connections and queued traffic are bounded; +slow readers are disconnected instead of blocking rendering or monitor wake. + +Every request includes `"version":1` and `"method"`. A reply is a JSON object; +failures contain `"error"`. Unexpected fields, unknown outputs, unsupported +versions, and invalid setting types/ranges are rejected. Process commands in +sequence on a connection. `watch` returns an initial snapshot followed by state +changes; it does not emit frame ticks or continuously changing countdown values. + +| Method | Additional fields | Behavior | +| --- | --- | --- | +| `reload` | None | Validate and reload the selected TOML file; keep the old configuration on error. | +| `alert`, `clear_alert`, `list_alerts` | See [alerts and audio](alerts-and-audio.md) | Temporary visual status, with bounded lifetime and priorities. | +| `status`, `explain` | None | Outputs, phases, focus, input-idle timing, inhibitors, power state, and reasons. | +| `activate`, `black`, `wake` | Optional `output` | Control one physical monitor or all monitors. | +| `lock` | None | Request the native session-wide lock; the reply may still be `locking`. | +| `lock_status` | None | Read `state`; use this to observe an API lock request completing. | +| `watch` | None | Subscribe to changes in the manager snapshot. | +| `inhibit` | `seconds`, `reason`, optional `output` | Create a timed saver inhibitor and return its `id`; maximum 24 hours. | +| `uninhibit` | `id` | Release a timed inhibitor early. | +| `schema` | None | Supported API settings, types, defaults, ranges, current values, and application timing. | +| `settings` | `settings` object, optional boolean `save` | Apply validated settings; `save:true` also atomically updates the selected user-owned config. | +| `preview` | `settings` object, optional `output`, optional `seconds` | Temporarily render with settings for 15 seconds by default, up to 600; do not change saved settings. | + +Saver phase and session-lock state are separate. Lock states are `locking`, +`locked`, `locked_unavailable` (compositor protection with no connected locker), +`unlocked`, and `unknown` (no authoritative status available). A missing helper +or disconnected locker is not assumed to mean unlocked. Protection status from +Wayfire and the locker's confirmed protocol event drive these transitions. + +Settings use `section.KEY` names. API schema covers numeric global/matrix controls +and `matrix.COLOR`. Edit paths and per-output configuration sections in the config +file. Runtime settings last until overwritten by a later config reload or manager +restart. Saved TOML writes preserve unrelated entries and comments, validate the result, and reject detected concurrent edits; saving a config +owned by another user, such as a system file, is rejected. + +Examples: + +```sh +ambient-idle --request '{"version":1,"method":"inhibit","seconds":600,"reason":"presentation"}' +ambient-idle --request '{"version":1,"method":"preview","output":"DP-1","seconds":20,"settings":{"matrix.COLOR":"cyan","matrix.FONT_SIZE":18}}' +ambient-idle --request '{"version":1,"method":"settings","save":true,"settings":{"global.LOCK_TIMEOUT":900}}' +``` + +A Python client needs only the standard library: + +```python +import json, os, socket +with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as client: + client.connect(os.environ['XDG_RUNTIME_DIR'] + '/digitalrain/control.sock') + client.sendall(json.dumps({'version': 1, 'method': 'status'}).encode()) + print(json.loads(client.recv(16384))) +``` + +The private `lock.sock` channel between manager and locker accepts only status, +watch, and wake, plus bounded private appearance snapshots. It has no unlock or password operation. Monitor power leases are +released on wake, normal exit, or process death without affecting session locking. + +## Optional MCP adapter + +The Gentoo `mcp` USE flag installs `ambient-background-mcp`. Configure an MCP client to +launch that command as the desktop user with the same `XDG_RUNTIME_DIR`. No MCP +process runs until a client starts it. It translates tools and resources into the +same local API and has no renderer, PAM, shell execution, or HTTP server. + +Resources: `digitalrain://status`, `digitalrain://schema`. Tools: `status`, +`explain`, `activate`, `black`, `wake`, `lock`, `lock_status`, `inhibit`, `uninhibit`, +`preview`, `settings`, `reload`, `alert`, `clear_alert`, and `list_alerts`. `lock` requests locking; check `lock_status` for completion. +There is deliberately no unlock tool. Tool inputs and limits match the API table. + +The adapter uses MCP [stdio transport](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports) +and [initialization negotiation](https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle). +An HTTP adapter remains future work. + +## Performance and validation + +The API uses the manager's existing timed waits rather than adding a polling +thread or timer. Renderers report only phase/power transitions over a private +socket; they do not serialize JSON or serve clients. The locker and PAM worker +run only when needed. Monitor fades are reaped asynchronously so they do not +block API requests or another monitor's wake. + +Historical 1.1.0 baseline: in five-second isolated comparisons on the development machine, the old and new +manager both used less than one CPU accounting tick with no clients and had +17 voluntary context switches per second. The new manager used 92 KiB more RSS; +an idle event subscriber added 40 KiB and about 0.2 switches/second. Both rain +renderers measured 0.4% CPU and 90.2 switches/second. These short samples support +low overhead; they are not a universal zero-cost guarantee. Wayland commit traces +measured median frame intervals of 33.390 ms before and 33.387 ms after, with +95th percentiles of 33.429 ms and 33.426 ms respectively (roughly 30 fps). + +Tests cover incorrect/correct PAM authentication using a separate test-only PAM +policy, locked output coverage, black idle, duplicate lockers, crash recovery, +manager restart, input/API wake isolation, hot-plugging, and opt-in power-off. +The API tests cover validation, save/preview separation, timed inhibitors, +events, oversized packets, and MCP initialization/tools/resources. Existing +stitched-output, application-inhibition, topology, glyph-compositing, and manager +ownership regressions remain part of validation. No integration test locks the +real desktop or needs the user's password. + +See [Configuration](configuration.md) and the [Wayfire session-lock implementation](https://github.com/WayfireWM/wayfire/blob/master/plugins/protocols/session-lock.cpp). diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/live-config-alerts-audio-plan.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/live-config-alerts-audio-plan.md new file mode 100644 index 000000000000..ed974e5fe54f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/live-config-alerts-audio-plan.md @@ -0,0 +1,363 @@ +# Plan: live configuration, automation alerts, and audio-reactive rain + +This integration is included in Ambient Background. See the configuration guide for current file paths and the WCM controls for continuous live wallpapers; helper and socket identifiers retain their compatible names. + +Status: core delivery implemented in 1.2.0. This is the accepted design from +2026-09-27; the design discussion and proposed examples below are retained for +context. TOML/live appearance, alerts, and energy-mode audio are implemented. +Frequency bands, rhythmic-onset detection, displayed alert text, and waking black +screens for alerts remain future work. Actual interfaces and limits are documented +in [configuration.md](configuration.md), [interfaces-and-locking.md](interfaces-and-locking.md), +and [alerts-and-audio.md](alerts-and-audio.md). + +Digital Rain should respond to saved configuration, events from programs or +agents, and playing audio while keeping its independent physical-monitor timers +and inexpensive solid-black state. Build one live appearance-update path and use +it for all three features. + +## What is already available + +- The manager periodically rereads the existing INI-style configuration. Most + appearance settings apply when the next renderer starts; adaptive palettes + already have a limited update path to running renderers. +- The local API supports status, change subscriptions, settings, and temporary + previews. A preview starts a renderer and is unavailable during session locking; + it is not an alert mechanism that preserves the current animation and timers. +- The optional MCP adapter exposes the same API to agents. Ordinary programs can + use the CLI or local socket without an agent. +- Renderer-to-manager reporting currently carries phase/power transitions. + General manager-to-renderer appearance updates still need implementation. +- Native locking uses a separate process and actual Wayland lock surfaces. + Its private channel currently accepts status, watch, and wake only. +- This development desktop runs PipeWire and WirePlumber. Audio analysis is not + currently part of Digital Rain. + +## Intended experience + +Save a color or speed setting and see running rain change smoothly. A failed +backup can temporarily turn rain red; clearing the failure restores the current +normal palette. Playing music can gently increase the rain's speed and brightness +with its energy. Later, bass, midrange, and treble can influence different depth +layers or columns. + +An error color remains recognizable while music plays: alerts own the hue, and +audio can still influence motion within configured limits. The visual controls +affect Digital Rain's pixels; display brightness and panel styling are separate. + +Default behavior: + +| Situation | Planned response | +| --- | --- | +| Transparent rain or rain over black | Apply eligible alerts and audio effects. | +| Solid black or a powered-off monitor | Keep it that way; stop audio sampling when no other rain needs it. | +| A monitor is awake | Store eligible alerts until expiry; do not activate its saver. | +| Input resumes | Preserve normal wake behavior and independent monitor timers. | +| Session is locked | Visual alerts and audio require separate opt-in settings; authentication remains independent. | +| Playback stops or an audio source disappears | Smoothly return to normal rain. | +| Invalid configuration is saved | Keep the last valid configuration and report the error. | + +Keep `POWER_OFF = false`, `LOCK_TIMEOUT = 0`, and `HIDE_DESKTOP = false` for this +desktop. Existing application idle inhibitors retain their behavior; playing audio +does not automatically override them, activate rain, or extend a saver phase. + +## 1. TOML and reliable live reload + +Use a maintained C TOML parser with verified TOML 1.0 support, license, and Gentoo +packaging. Select the library during implementation after checking available +packages and parser tests. TOML defines data syntax; file watching and live +application are Digital Rain features. The [TOML 1.0 specification](https://toml.io/en/v1.0.0) +defines the baseline syntax for this plan. + +Keep familiar section names and setting names. Quote strings and hex colors, +use native booleans, and represent monitor overrides as tables. Introduce +`schema_version = 1` at the top level. Read one selected file, with no implicit +system/user merging: explicit `AMBIENT_BACKGROUND_CONFIG` (or legacy `DIGITALRAIN_CONFIG`), then the user location under +`XDG_CONFIG_HOME` or `~/.config`, then `/etc/ambient-background.toml`. Pin the selected +path for that manager lifetime so a temporary deletion cannot select another file. + +Provide a one-time conversion command that writes a separate `.toml` file and +reports obsolete or unconvertible entries. Preserve the original and compare +effective settings before switching. Preserve the user's actual values, including +`MIN_SPEED = 0.01`; the existing API's 0.1 minimum must be reconciled with the +renderer/config semantics rather than silently changing that value. Update the +system/user config, examples, package messages, tests, and documentation together. +The legacy parser can serve the converter; ongoing parallel config formats are +not required. + +Implement a dedicated configuration structure and one validator shared by file +loads and API updates. Parse each file into defaults plus explicit overrides. +Removing a setting then consistently restores its documented default. Reject +unknown keys, invalid types, non-finite numbers, and invalid relationships such +as minimum speed greater than maximum speed. Preserve the previous snapshot on +any reload error; report source path and line/key where available. An invalid +startup configuration produces a clear startup failure, never a silent fallback +that changes locking or power policy. + +Watch the containing directory with inotify so editor atomic replacement works. +Debounce save bursts, verify file identity/content, and recover watches after +directory replacement or event overflow. Use the existing manager housekeeping +as a low-frequency fallback. Add explicit `reload` and config-check commands. +Bound file size and parsing work. A missing file during replacement retains the +last valid configuration until a valid replacement arrives. + +Publish validated revisions and classify each setting in the API schema: + +| Setting class | Application | +| --- | --- | +| Palette, speed, alert/audio controls | Update running rain without resetting it. | +| Font size and depth/grid resources | Build replacement resources, then swap at a frame boundary; keep old resources on failure. | +| Idle thresholds | Reevaluate against existing activity timestamps on the next manager check. | +| Phase durations | Recalculate from the existing phase start; never move a black screen backward into rain. | +| Surface/background policy and monitor power | Apply on next activation unless a separately documented safe transition exists. | +| Chosen config path and logging destination | Explicit restart-required status. | + +Report desired and applied revisions, deferred settings, and renderer failures. +Do not claim a multi-process update is atomic if an individual renderer has not +applied it. Reloading appearance never restarts a locker or requests an unlock. + +Preserve API version 1's existing field names and integer boolean inputs through +an adapter while TOML uses booleans. Replace the current INI text writer: TOML +forbids duplicate keys/table definitions, so appending repeated sections is not +valid. Use syntax-aware edits that preserve comments, validate the complete result, +detect concurrent edits, and commit with a same-directory atomic rename. Runtime +settings retain the documented lifetime until a successful reload or restart. +Alert and audio values are transient and never written into base configuration. + +## 2. A shared live appearance path + +Extend the inherited renderer socket into a bounded, versioned, bidirectional +channel. Send small configuration/appearance snapshots and acknowledgements. +Retain phase/power reporting. Coalesce pending updates so slow consumers receive +the newest state, and reserve delivery for control transitions rather than letting +audio samples fill the queue. Include revision/sequence IDs and monotonic times. +Integrate the channel into the renderer's Wayland polling, including solid-black +waits, without introducing continuous wakeups in that phase. + +Separate the normal configuration from the effective appearance: + +```text +validated base settings + physical-monitor overrides + | + temporary preview, if any + | + highest-priority eligible alert + | + bounded audio modulation + | + current saver/lock phase policy + | + renderer at its existing frame cadence +``` + +Clamp the final result to brightness, speed, and density limits. An alert owns +hue while active; audio hue modulation is suppressed during that alert. Preview +temporarily suppresses alert/audio visuals on its selected output so the requested +appearance can be inspected; alert expiry continues normally. Clearing an alert +reveals the latest underlying state, not a stale snapshot from when it arrived. + +The separate locker needs its own narrow appearance-update operation. Render +per-physical-monitor effects inside a stitched lock surface using its existing +region mapping. Keep visual updates separate from wake/input, PAM, and unlock. +On manager/helper disconnection, temporary effects decay or expire locally while +the locker continues protecting the session. + +## 3. Automation alerts + +Add `alert`, `clear_alert`, and `list_alerts` to the local API, then expose them +through CLI and MCP tools. Extend status/watch with alert lifecycle and effective +appearance changes. These are events, not frame or audio-sample streams. + +Proposed request: + +```json +{ + "version": 1, + "method": "alert", + "source": "backup", + "id": "nightly", + "severity": "error", + "seconds": 60, + "output": "DP-1" +} +``` + +The same `(source, id)` updates an existing alert and renews its expiry. Clearing +that pair is idempotent. Omit `output` for all eligible physical monitors. Require +a finite duration from 1 to 86400 seconds; use monotonic expiry from acceptance, +including while black, awake, or disconnected. Programs renew ongoing conditions; +a crashed producer cannot leave an indefinite alert. An acknowledged alert survives +the producer closing its socket, but does not persist across manager restart. + +Keep at most 64 active alerts and bounded identifiers/messages; reject excess +requests clearly and limit per-client work so wake events stay responsive. +All producers use the existing same-user socket permissions. Source names are +labels, not additional authentication identities. + +Default priority: error, warning, success, info. A newer update wins a tie; a +lower-priority alert becomes visible if the winner expires and it is still valid. +Map severity to appearance in TOML. Validate a specifically named output at +acceptance; after a disconnect retain its alert until expiry and reapply only if +that physical output returns. Never transfer it to another monitor. + +Start with color and an optional gentle pulse, with transitions rather than +flashing. Message text can be stored as status metadata; rendered text is a later +feature, separately controlled for locked sessions. Black-screen alert presentation +is also a later explicit option. Initial alerts never resume animation from black +or power a monitor on. + +Ship examples for build completion/failure, backup status, and service health. +Programs publish changes through IPC; agents can use the equivalent MCP tools. +No shell hooks or script execution inside the renderer are needed. + +## 4. Audio-reactive rain + +Use an optional `ambient-audio` helper backed by native PipeWire. Capture the +selected playback sink's monitor; microphone input is outside the initial scope. +PipeWire provides `stream.capture.sink` for targeting output audio and +`stream.monitor` for monitor streams. See the official [stream keys](https://docs.pipewire.org/group__pw__keys.html) +and [capture example](https://docs.pipewire.org/audio-capture_8c-example.html). + +Start with one source: the default playback output, or an explicitly configured +sink. This covers audio routed to that sink, not every device simultaneously. +Follow default-output changes when configured to do so; reconnect a named sink by +stable identity after unplug/replug. Missing sources or permissions produce an +observable unavailable state and normal rain, with bounded reconnect backoff. +Never silently substitute a microphone. Verify volume/mute behavior experimentally +and document whether the captured monitor data reflects output volume; honor the +selected sink's mute state in the visual effect. + +Use one analysis stream shared by all participating monitors. Start the helper +only when enabled and an eligible rain surface is visible. Stop capture when all +eligible surfaces become awake, solid black, or powered off. On a locked desktop, +only start it if locked audio effects are explicitly enabled. A standalone locker +without the manager can render ordinary rain without audio integration. + +The first mode measures short-window RMS energy and peaks. Apply a noise gate, +configurable gain, and attack/release smoothing. Silence settles to zero modulation; +source loss or stale measurements also decays to zero. This mode reacts to musical +energy without claiming beat or tempo recognition. + +| Input | Initial visual mapping | Later options | +| --- | --- | --- | +| Overall energy | Gentle speed increase | Density within preallocated capacity | +| Smoothed peaks | Bounded glyph-brightness increase | Soft accent pulses | +| Bass / midrange / treble | Deferred | Separate depth layers or column regions | +| Detected rhythmic onsets | Deferred | Bounded pulses with a minimum interval | + +Keep processing outside GL, Wayland, and PAM paths. The PipeWire callback uses +preallocated storage and bounded work, with no blocking, file/network I/O, JSON, +or allocation. A worker computes measurements and sends small timestamped scalar +snapshots through a private channel, initially capped at 20 updates per second. +Renderers interpolate at the existing 30 FPS; they never wait for audio. Drop old +measurements under backpressure. Extend to three-band analysis only after measuring +the simpler mode. Raw audio is neither stored nor exposed through the public API. + +Audio effects must not create a playback loop, alter routing, change device +volume, or hold audio devices active after the last eligible rain surface stops. +Account for any PipeWire/session-manager idle-inhibition interaction in testing. +MCP/status expose enabled/source/availability state, not a high-rate sample feed. + +## Proposed configuration excerpt + +This is valid TOML syntax illustrating the planned schema. It is not accepted by +Digital Rain 1.1.0 and is not a complete replacement for the user's config. + +```toml +schema_version = 1 + +[global] +LOCK_TIMEOUT = 0 + +[matrix] +COLOR = "adaptive" +POWER_OFF = false +HIDE_DESKTOP = false + +[alerts] +ENABLED = true +SHOW_WHILE_LOCKED = false + +[alerts.error] +COLOR = "#ff4040" +PRIORITY = 40 + +[alerts.warning] +COLOR = "#ffb000" +PRIORITY = 30 + +[audio] +ENABLED = false +SOURCE = "default-output" +MODE = "energy" +SHOW_WHILE_LOCKED = false +OUTPUTS = [] # Empty means all eligible physical monitors. +SPEED_AMOUNT = 0.25 # Maximum proportional increase above base speed. +BRIGHTNESS_AMOUNT = 0.10 # Maximum proportional increase, subject to final cap. +ATTACK_MS = 50 +RELEASE_MS = 250 +``` + +Audio is opt-in. Tune gain and modulation amounts while previewing on one monitor; +the eventual full schema must document defaults, units, bounds, and application +timing for every field. Success/info themes also receive documented defaults. + +## Delivery order and checks + +1. **Configuration foundation:** isolate configuration state, unify validation, + select the TOML parser, implement conversion and validation commands, and make + saves/reloads transactional. Test the actual user config for equivalent values. +2. **Live appearance:** add renderer/locker updates, revision reporting, and + resource replacement. Test without alerts/audio first, including locked reloads. +3. **Alerts:** implement bounded lifecycle/priority rules, IPC/CLI/MCP support, + event subscriptions, themes, and producer examples. +4. **Audio energy mode:** optional helper and Gentoo `pipewire` USE dependency, + source selection, smoothing, visibility-based lifecycle, and per-monitor opt-in. +5. **Measure and extend:** benchmark before considering spectrum bands, rhythmic + onsets, displayed alert text, or alerts on black screens. Publish measured costs. + +Configuration checks cover invalid/partial saves, atomic replacement, deletion, +duplicate TOML keys/tables, unknown settings, removed keys, comments, simultaneous +API saves, and resource-allocation failure. Confirm that applying appearance does +not restart animation, change phase age, or change independent idle deadlines. + +Alert checks cover competing producers, priority ties, updates, clear/expiry, +producer death, manager restart, disconnected outputs, previews, adaptive-color +updates underneath alerts, floods, and slow readers. Lock integration checks must +prove that appearance changes cannot trigger wake or unlock and that protected +surfaces survive visual/helper failures and display hotplug. + +Audio checks use deterministic silence, tones, impulses, and changing levels, +then real playback on a private PipeWire test setup. Cover clipping/non-finite +input, stereo/channel layouts, mute/volume, default-device changes, source loss, +server/helper restarts, stale measurements, and audio while an application inhibits +the saver. Use a virtual playback sink for automated tests; do not capture real +user audio during verification. + +Measure disabled features, an idle API subscriber, active alerts, audio energy +mode, and later spectrum mode separately. Compare CPU, RSS, wakeups, frame-time +median/p95/p99, update latency, and audio xruns over sustained runs with warmup and +repetitions. Test one monitor and stitched monitors at actual desktop resolutions. +Compare equivalent PipeWire playback with and without capture to expose costs in +the audio server as well as Digital Rain. + +Acceptance requires no audio helper/capture when disabled, no continuing audio +work when all eligible surfaces are black, unchanged target frame cadence, bounded +memory/queues, responsive wake, and no measurable disabled-feature regression +beyond repeat-run noise. Enabled audio has a real cost; report the measurements +instead of promising zero overhead. Use isolated compositor sessions for lock and +hotplug tests. A compositor restart is not part of the rollout. + +## Files expected to change during implementation + +| Area | Starting points | +| --- | --- | +| Config parsing and renderer state | `digitalrain.c`; extract reusable config/schema code as needed | +| Public controls, validation, saves, events | `digitalrain-api.h`, `digitalrain-control.h` | +| Protected visual updates and physical regions | `digitalrain-lock.c` | +| Agent tools | `ambient-background-mcp.py` | +| Optional audio capture and analysis | New `digitalrain-audio.c` and focused analysis code | +| Build and packaging | `Makefile`, next-version ebuild, annotated TOML example | +| Validation and user guidance | Existing headless tests, new focused tests, configuration/API/feature guides | + +The PAM worker needs no new responsibilities. Preserve the existing GLES rendering +baseline; an OpenGL version migration or language rewrite is outside this plan. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/monitor-power.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/monitor-power.md new file mode 100644 index 000000000000..087d81d74959 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/monitor-power.md @@ -0,0 +1,50 @@ +# Optional monitor power-off + +This integration is included in Ambient Background. See the configuration guide for current file paths and the WCM controls for continuous live wallpapers; helper and socket identifiers retain their compatible names. + +See [Configuration](configuration.md) for the full option reference, defaults +and reload rules. + +Digital Rain keeps monitors powered on by default. With a positive `BLACK_RAIN_TIMEOUT`, +the rain fades to solid black and the renderer sleeps without redrawing. This +keeps OLED pixels black while avoiding the monitor's power-on delay. + +For LCDs or other monitors where powering down is preferred, enable the +`digitalrain-power` Wayfire plugin and configure: + +```toml +[matrix] +BLACK_RAIN_TIMEOUT = 600 +POWER_OFF = true +``` + +Power-off replaces the **final solid-black state**. It does not skip rain or +change the idle timers. With transparent rain, the sequence is: idle timeout, +transparent rain, black-background rain, monitor power-off. A zero +`BLACK_RAIN_TIMEOUT` means rain continues indefinitely, so power-off is never +reached. Transparent rain also needs a nonzero `TRANSPARENT_TIMEOUT` to reach +the black background. + +Mixed setups can override the global setting by physical connector name: + +```toml +[output_power_off] +HDMI-A-1 = false +DP-1 = true +``` + +The manager still tracks each physical monitor independently, including members +of a stitched desktop. Activity or focus returning to that monitor restores +power before the wake animation. Video inhibition and normal shutdown also +restore it. Power control preserves the monitor's place in the desktop, so +windows are not rearranged. + +The renderer holds a compositor IPC connection while power is off. Disconnect, +renderer crash, or unloading the power plugin restores power. Managed renderers +also receive SIGTERM if their manager dies. An output removed and replaced while +asleep is not treated as the same connector object. Outputs already powered off +by another owner are left alone. If the helper is missing or rejects the request, +Digital Rain stays solid black and reports the failure. + +For a standalone renderer, use `--power-off 1` with a positive +`--black-rain-timeout`; `SIGUSR1` wakes it, as in black-screen mode. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/stitched-outputs.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/stitched-outputs.md new file mode 100644 index 000000000000..2ed008229370 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/stitched-outputs.md @@ -0,0 +1,45 @@ +# Digital Rain on a stitched Wayfire desktop + +This integration is included in Ambient Background. See the configuration guide for current file paths and the WCM controls for continuous live wallpapers; helper and socket identifiers retain their compatible names. + +Digital Rain keeps independent physical-monitor timers when Wayfire exposes +DP-1, DP-2 and DP-3 as regions of VIRTUAL-1. The cube and workspace remain shared. + +The manager discovers member names and rectangles from `window-rules/list-outputs`. +Each member keeps its own unfocused/focused timeout, color overrides, adaptive +palette, renderer, and wake state. Pointer crossings select the physical member; +view-focus events also use Wayfire's `physical-monitor` field. Keep using physical +names in `[output_timeouts]`, `[output_colors]`, and `[output_power_off]`. +See [Configuration](configuration.md) for defaults, units and precedence. + +Renderers bind the parent Wayland output and anchor a surface to the member's +rectangle. With the default `HIDE_DESKTOP=false`, apps and bars remain visible under +transparent rain until the black background covers them. Optional desktop hiding +fades only pixels in that rectangle, including the corresponding slice of a +spanning window or panel, leaving the wallpaper visible during transparent rain. +Rain on black and solid black are also limited to that monitor. Optional monitor +power-off targets the physical connector and preserves the shared desktop layout. + +Video inhibition protects every member intersected by the visible playback +window. Moving the window moves its protection; closing it releases protection. +Adaptive luminance samples are taken separately for each member. + +Output changes are reconciled after events and periodically. Disappearing outputs +(including Wayfire's temporary NOOP-1) are removed and their renderers stopped. +Changing grouping or geometry recreates affected targets with fresh timers. +An IPC outage preserves existing member mappings instead of replacing them with +one combined timer. Ordinary outputs retain the Wayland discovery fallback. + +The desktop, idle, sampler and optional power helpers must match the manager +and the installed Wayfire ABI. The cursor helper reports coordinates used by +the manager; that helper is non-unloadable during a running Wayfire session. + +Validation: `python3 tests/test-stitched-outputs.py` as a regular user starts its +own small three-output headless compositor. It checks exact neighboring pixels, +spanning windows, fades, renderer/black timeout, per-member sampling, actual mpv +idle inhibitors, independent manager timers, wake-up, grouping changes, optional +power-off, crash recovery and removal of a sleeping monitor. `--binary PATH` tests +an installed manager/renderer binary with the built helpers. +`--upgrade-from DIRECTORY` first loads old helper libraries from that directory +and verifies a live upgrade. Tests require the usual build dependencies plus +Wayfire, grim, swaybg, mpv and Pillow. They do not use the real desktop's sockets. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.black-rain-timeout.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.black-rain-timeout.md new file mode 100644 index 000000000000..d1f683b8a400 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.black-rain-timeout.md @@ -0,0 +1,27 @@ +# Black output timer regression + +`test-black-rain-timeout.py` runs the production renderer against an isolated +Wayfire compositor with three headless outputs and `ipc ipc-rules` enabled. +It requires `wlr-randr`, `swaybg`, `grim`, and Python Pillow. The optional +`render-test` fixture's `test/stats` method also verifies that static black +does not submit compositor render passes. + +With the test compositor's `XDG_RUNTIME_DIR`, `WAYLAND_DISPLAY`, and +`WAYFIRE_SOCKET` environment variables set: + +```sh +python3 tests/test-black-rain-timeout.py /path/to/built/digitalrain +``` + +The test refuses a session without exactly HEADLESS-1, HEADLESS-2, HEADLESS-3. +It sets test output modes and backgrounds, uses short timers, and verifies: + +* Transparent rain, rain on black, then pure RGB zero on each pixel. +* Staggered activation gives each output its own countdown. +* Zero disables the rain-stop timer; indefinite transparency does not start it. +* Initially opaque mode starts its timer after the black background appears. +* Renderers remain alive and sleep; palette notifications do not restart rain. +* Resize retains complete black coverage and all outputs stay enabled. +* Activity reveals the desktop without a rain flash; termination wakes sleepers. + +The existing glyph-compositing regression also remains applicable. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.cursor-wakeup.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.cursor-wakeup.md new file mode 100644 index 000000000000..cd18ac7a00ba --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.cursor-wakeup.md @@ -0,0 +1,24 @@ +# Cursor visibility during wake-up + +Version 1.0.18 kept the Wayfire cursor hidden whenever its output was marked +`off`, including after input resumed. Unblanking waited for renderer exit +before changing that state, and cursor polling stopped during the wait. This +left the pointer hidden during the fade and delayed visibility changes while +crossing monitors. + +Version 1.0.19 only hides the cursor on a blanked output while the seat is idle. +Unblanking marks the output awake and updates cursor visibility before waiting +for the renderer. Cursor polling continues during that wait. Stationary cursors +on blanked outputs are still hidden once the seat is idle. + +Run `python3 tests/test-cursor-wakeup.py` from the package directory. The test +compiles the production manager code and supplies a private Wayfire IPC socket; +it never connects to the desktop. It checks idle hiding, resumed input, +unblanking with a deliberately slow renderer, and crossing between blanked and +awake outputs during the fade. `--source /path/to/older/digitalrain.c` permits a +before/after comparison. All three wake-up cases fail against 1.0.18 and pass +against 1.0.19; idle hiding passes against both. + +The existing `test-inhibition.c` and `test-manager-lock.py` regressions also +pass. These checks validate manager visibility requests, not physical GPU +cursor-plane rendering or the appearance of a faint cursor outline. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.glyph-compositing.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.glyph-compositing.md new file mode 100644 index 000000000000..287743777d5c --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.glyph-compositing.md @@ -0,0 +1,28 @@ +# Glyph compositing regression + +Version 1.0.13 multiplied layer depth and trail brightness into stroke alpha. +A dim glyph could therefore leave 95% of the application pixel beneath it +visible. Version 1.0.14 applies those factors to RGB intensity and reserves +stroke alpha for antialias coverage and the overall fade. Empty background +pixels remain transparent; glow is still translucent. + +`test-glyph-compositing.c` runs the actual production `render_frame()` and GLSL +shader in a surfaceless EGL pbuffer. A synthetic atlas gives known coverage. +It checks a dim far-layer trail over a white application pixel, antialiasing, +phase fading, and empty-background transparency. The test fails with 1.0.13 +(RGBA `12 6 3 12`) and passes with 1.0.14 (`12 6 3 255`). + +From the package directory, with generated protocol files available in the module directory: + +```sh +cc -std=c11 -O2 -I. tests/test-glyph-compositing.c tomlc17.c \ + ext-idle-notify-v1-protocol.c \ + wlr-layer-shell-unstable-v1-protocol.c \ + xdg-output-unstable-v1-protocol.c xdg-shell-protocol.c \ + $(pkg-config --cflags --libs json-c wayland-client wayland-egl egl glesv2 freetype2) \ + -lpthread -lm -o /tmp/test-glyph-compositing +/tmp/test-glyph-compositing +``` + +The fixture needs EGL with `EGL_PLATFORM_SURFACELESS_MESA`, GLES2 and an RGBA8 +pbuffer. It does not connect to or modify a desktop session. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.hide-desktop.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.hide-desktop.md new file mode 100644 index 000000000000..904ef1c36579 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.hide-desktop.md @@ -0,0 +1,25 @@ +# Desktop fade regression + +Build with `make`, then run as a regular user: + +```sh +python3 tests/test-hide-desktop.py --existing-tests +``` + +The runner starts its own Wayfire with three headless outputs and loads the +candidate plugin directly from ``. It needs Wayfire development headers, +a C++ compiler, pkg-config, grim, wlr-randr, swaybg and Python Pillow. It does not +connect to the desktop, alter the real configuration, or install its fixture. + +Pixel checks cover app, panel, popup and widget layers, wallpaper and lock +visibility, the intermediate opacity of all six desktop layers, simultaneous +rain fade-in, independent monitors, new windows, and multiple IPC owners. +Wake-up must show intermediate desktop opacity while rain or black fades away, +and reverse partial fades without jumping. Repeated wake requests are idempotent. +Wake during the fade, normal wake, SIGKILL and plugin unload must restore exact +baseline pixels. Missing-plugin fallback and output removal are also exercised. +`--existing-tests` runs the black-rain timeout regressions in the same isolated +compositor after the new tests finish. + +Existing cursor-wakeup, manager-lock, glyph-compositing and inhibition tests +remain applicable. Test artifacts are retained under the printed temporary path. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.live-config-alerts-audio.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.live-config-alerts-audio.md new file mode 100644 index 000000000000..cfd9b85465c1 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.live-config-alerts-audio.md @@ -0,0 +1,109 @@ +# Live configuration, alerts and playback tests + +Build with `make`. The audio helper requires PipeWire development files; +`make PIPEWIRE=0` builds the remaining programs without it. + +Run the production manager without a compositor to check TOML parsing, atomic +replacement, comment-preserving API saves (including inline/dotted/multiline TOML), +invalid-edit recovery, default restoration, explicit reload, alert limits/expiry +and the absence of an audio child when disabled: + +```sh +python3 tests/test-live-config-alerts.py digitalrain +``` + +Run the native integration suite as root, selecting an ordinary local account. +It creates a temporary test-only PAM service and removes it afterward. All input, +locking, screenshots and audio happen in private headless Wayfire/PipeWire sessions: + +```sh +python3 tests/run-native-lock-tests.py --user USER +# Validate an installed build instead: +python3 tests/run-native-lock-tests.py --user USER --binary-dir /usr/bin +``` + +This covers live colors/font/depth without changing renderer PIDs, physical-region +alerts inside a stitched desktop, priority and restoration, black-screen policy, +locked appearance without unlocking, authentication, crash recovery, monitor +hotplug, optional DPMS, MCP/API transport, and audio-helper visibility, crash and +server-loss recovery. A private virtual sink is created; hardware audio monitors +are not loaded by the policy-only WirePlumber profile. + +For deterministic playback levels, silence, mute/unmute, default-source changes, +named-sink removal/recreation and missing-source behavior: + +```sh +runuser -u USER -- python3 tests/test-audio.py digitalrain-audio +``` + +The C harness `test-live-render.c` injects allocation failure into a resource +replacement and checks rollback, phase clocks, vertical monitor regions, color +restoration and invalid packets. `run-c-tests.py` compiles it along with the existing +topology, focus/inhibition and glyph-compositing harnesses: + +```sh +python3 tests/run-c-tests.py +python3 tests/test-cursor-wakeup.py +runuser -u USER -- python3 tests/test-stitched-outputs.py +``` + +For a sustained comparison, retain the previous 1.1 executable and run: + +```sh +runuser -u USER -- python3 tests/benchmark-live.py OLD_BINARY digitalrain 15 +``` + +Each case uses two repetitions, three seconds of warmup, and a configurable sample +window (15 seconds by default). Cases include idle/no clients, idle subscription, +one 4K monitor, three stitched 4K monitors, alerts, and audio. A private virtual +sink plays the same tone throughout, exposing additional PipeWire cost when capture +starts. CPU percentages use one logical core as 100%; context-switch counts are +not hardware wakeup measurements. RSS is sampled per process group. + +A separate direct-renderer pass records Wayland client commit intervals. These are +submission cadence, not GPU execution or presentation latency. Manager-owned renderers silence their logs, so multi-monitor manager cases +report CPU/RSS only. `pw-top` +snapshots retain the private audio graph's error counters. Headless tests cannot +establish hardware speaker latency or real-display presentation behavior. + +`DR_BENCH_REPETITIONS` overrides the repetition count; `DR_BENCH_CASES` selects +comma-separated case names for focused reruns. The release measurements below describe their limits. + +## 1.2.0 release observations (2026-09-27) + +Correctness suites passed against both the source build and installed package. +Measurements are short diagnostic samples on the development desktop, not a +statistically conclusive zero-overhead result. The longer run was interrupted by +an external session restart, so its audio measurements were discarded. The final +focused run used one ten-second sample per case after three seconds of warmup. +Raw results: [benchmark-1.2.0.json](benchmark-1.2.0.json). + +| Three stitched 3840×2160 outputs | Renderer CPU, % of one core | Renderer RSS, KiB | +| --- | ---: | ---: | +| 1.1 baseline | 6.3 | 195780 | +| 1.2, effects inactive | 9.6 | 197020 | +| 1.2, alert active | 7.0 | 196396 | +| 1.2, audio active | 7.0 | 196536 | + +The variation between inactive and active effects means these short, randomized +animation runs do not isolate incremental rendering cost. Do not infer that alerts +make rendering faster, or claim no disabled-feature CPU regression from this data. +Earlier matched one-output samples measured 2.07% versus 2.27% CPU. Further sustained +repetitions with controlled animation state would be needed for a reliable delta. + +The idle manager used under one CPU accounting tick in fifteen seconds in both +versions, about 3.7–4.0 MiB RSS, and roughly 17 context switches/second, including an +idle subscriber. Audio added a 6.1 MiB helper with about 44 switches/second; manager +switches rose to about 28/second. Helper and private PipeWire CPU each stayed below +one 10 ms accounting tick during the final sample. This means below measurement +resolution, not zero cost. The virtual audio graph reported zero errors in its +`pw-top` snapshots, with identical playback running before and during capture. + +| Direct renderer commit interval | 1.1 | 1.2 | +| --- | ---: | ---: | +| Median | 33.383 ms | 33.377 ms | +| p95 | 33.575 ms | 33.591 ms | +| p99 | 33.680 ms | 33.846 ms | + +Both retained the intended 30 FPS submission cadence. These measurements do not +establish hardware presentation latency or speaker latency. Audio remains opt-in. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.native-lock.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.native-lock.md new file mode 100644 index 000000000000..3f09458d737b --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/README.native-lock.md @@ -0,0 +1,28 @@ +# Native lock and control regressions + +Build with `umask 022; make`, then as root run: + +```sh +python3 tests/run-native-lock-tests.py --user YOUR_DESKTOP_USER +``` + +To validate packaged executables, add `--binary-dir /usr/bin`. The wrapper creates +a temporary, uniquely named PAM service and a test worker compiled for that +service. It removes the policy and module when the suite exits. It never changes +`/etc/pam.d/digitalrain`, any account password, or the user's Wayfire config. + +The actual suite runs as the selected unprivileged user in a private headless +Wayfire session. It uses isolated compositor plugins for keyboard input and +output inspection; neither those plugins nor the deterministic test PAM module +are included in the installed package. The production auth worker's service is +fixed at compile time and accepts no password arguments or environment override. + +Coverage includes failed/successful PAM authentication, black idle, lock coverage +on stitched monitors, duplicate/refused locking, saver wake isolation, manager +restart, automatic crash recovery, monitor hot-plugging, and power-off/wake while +locked. API tests cover settings validation, preview/save separation, inhibition, +state events, packet bounds, and MCP negotiation/tools/resources. Logs and screen +captures remain in the printed `/tmp/digitalrain-lock-test-*` artifact directory. + +The other package regressions continue to cover ordinary saver behavior. The +native suite neither locks nor injects input into the real desktop. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/benchmark-1.2.0.json b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/benchmark-1.2.0.json new file mode 100644 index 000000000000..7f9718a8a6ca --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/benchmark-1.2.0.json @@ -0,0 +1,179 @@ +[ + { + "case": "old three 4K", + "repetition": 1, + "seconds": 10.000068796995038, + "cost": { + "manager": { + "cpu_percent": 0.0, + "switches_per_second": 16.8, + "rss_kib": 3836 + }, + "renderers": { + "cpu_percent": 6.3, + "switches_per_second": 273.898, + "rss_kib": 195780 + }, + "wayfire": { + "cpu_percent": 0.9, + "switches_per_second": 1087.893, + "rss_kib": 72724 + }, + "pipewire": { + "cpu_percent": 0.0, + "switches_per_second": 23.4, + "rss_kib": 7312 + }, + "policy": { + "cpu_percent": 0.0, + "switches_per_second": 0.0, + "rss_kib": 1800 + }, + "playback": { + "cpu_percent": 0.0, + "switches_per_second": 23.4, + "rss_kib": 17264 + } + }, + "frames": {} + }, + { + "case": "new three 4K", + "repetition": 1, + "seconds": 10.000072757000453, + "cost": { + "manager": { + "cpu_percent": 0.0, + "switches_per_second": 17.2, + "rss_kib": 3972 + }, + "renderers": { + "cpu_percent": 9.6, + "switches_per_second": 277.398, + "rss_kib": 197020 + }, + "wayfire": { + "cpu_percent": 1.2, + "switches_per_second": 1055.392, + "rss_kib": 71788 + }, + "pipewire": { + "cpu_percent": 0.0, + "switches_per_second": 23.4, + "rss_kib": 7312 + }, + "policy": { + "cpu_percent": 0.0, + "switches_per_second": 0.0, + "rss_kib": 1800 + }, + "playback": { + "cpu_percent": 0.0, + "switches_per_second": 23.9, + "rss_kib": 17264 + } + }, + "frames": {} + }, + { + "case": "new alerts three 4K", + "repetition": 1, + "seconds": 10.00006255399785, + "cost": { + "manager": { + "cpu_percent": 0.0, + "switches_per_second": 16.9, + "rss_kib": 3940 + }, + "renderers": { + "cpu_percent": 7.0, + "switches_per_second": 272.898, + "rss_kib": 196396 + }, + "wayfire": { + "cpu_percent": 0.9, + "switches_per_second": 1170.193, + "rss_kib": 71788 + }, + "pipewire": { + "cpu_percent": 0.1, + "switches_per_second": 23.5, + "rss_kib": 7312 + }, + "policy": { + "cpu_percent": 0.0, + "switches_per_second": 0.0, + "rss_kib": 1800 + }, + "playback": { + "cpu_percent": 0.0, + "switches_per_second": 25.0, + "rss_kib": 17264 + } + }, + "frames": {} + }, + { + "case": "new audio three 4K", + "repetition": 1, + "seconds": 10.0000647880006, + "cost": { + "manager": { + "cpu_percent": 0.0, + "switches_per_second": 27.9, + "rss_kib": 4120 + }, + "renderers": { + "cpu_percent": 7.0, + "switches_per_second": 272.598, + "rss_kib": 196536 + }, + "audio": { + "cpu_percent": 0.0, + "switches_per_second": 43.6, + "rss_kib": 6216 + }, + "wayfire": { + "cpu_percent": 0.9, + "switches_per_second": 1004.093, + "rss_kib": 71788 + }, + "pipewire": { + "cpu_percent": 0.0, + "switches_per_second": 23.4, + "rss_kib": 7560 + }, + "policy": { + "cpu_percent": 0.0, + "switches_per_second": 0.0, + "rss_kib": 1800 + }, + "playback": { + "cpu_percent": 0.0, + "switches_per_second": 24.9, + "rss_kib": 17264 + } + }, + "frames": {} + }, + { + "case": "old frame cadence", + "repetition": 1, + "frames": { + "commits": 300, + "median_ms": 33.383, + "p95_ms": 33.575, + "p99_ms": 33.68 + } + }, + { + "case": "new frame cadence", + "repetition": 1, + "frames": { + "commits": 299, + "median_ms": 33.377, + "p95_ms": 33.591, + "p99_ms": 33.846 + } + } +] diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/benchmark-live.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/benchmark-live.py new file mode 100755 index 000000000000..086b7fb5869f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/benchmark-live.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +"""Compare a 1.1 executable with the current build using private Wayfire/PipeWire. +Run unprivileged: python3 tests/benchmark-live.py OLD_BINARY NEW_BINARY [SECONDS] +Uses two repetitions after three-second warmup; results include frame commit +intervals (not GPU presentation latency). Never connects to desktop sockets. +""" +import array, json, math, os, pathlib, re, socket, statistics, struct, subprocess, shlex, sys, tempfile, time, wave +old,new=map(lambda x:str(pathlib.Path(x).resolve()),sys.argv[1:3]);duration=float(sys.argv[3]) if len(sys.argv)>3 else 15 +repetitions=int(os.environ.get('DR_BENCH_REPETITIONS','2')) +selected=os.environ.get('DR_BENCH_CASES','') +assert os.getuid()!=0 +pkg=pathlib.Path(__file__).resolve().parents[1] +root=pathlib.Path(tempfile.mkdtemp(prefix='digitalrain-live-bench-'));children=[];results=[] +env=os.environ.copy() +for key in ['WAYLAND_DISPLAY','WAYFIRE_SOCKET','_WAYFIRE_SOCKET','DISPLAY','WAYFIRE_CONFIG_FILE','WAYFIRE_DEFAULT_CONFIG_BACKEND','PIPEWIRE_RUNTIME_DIR','PIPEWIRE_CONFIG_DIR','PIPEWIRE_CONFIG_NAME','WIREPLUMBER_CONFIG_DIR','DBUS_SESSION_BUS_ADDRESS']:env.pop(key,None) +env.update(XDG_RUNTIME_DIR=str(root),XDG_CONFIG_HOME=str(root/'config'),XDG_STATE_HOME=str(root/'state'),WLR_BACKENDS='headless',WLR_HEADLESS_OUTPUTS='4',WLR_RENDERER='gles2',WLR_RENDER_DRM_DEVICE='/dev/dri/renderD128',WAYFIRE_PLUGIN_PATH=f'{root}:{pkg}',WAYFIRE_PLUGIN_XML_PATH=f'{root}:{pkg}',GIO_USE_VFS='local',PIPEWIRE_REMOTE='pipewire-0') +(root/'wayfire.ini').write_text('[core]\nplugins = ipc ipc-rules digitalrain-cursor digitalrain-idle desktop-test\nxwayland = false\nstitch_enabled = true\nstitch_outputs = HEADLESS-1 HEADLESS-2 HEADLESS-3\nwindow_scope = virtual\n'+''.join(f'[output:HEADLESS-{i}]\nmode = 3840x2160@120\nposition = {(i-1)*3840},0\n' for i in range(1,4))+'[output:HEADLESS-4]\nmode = 1920x1080@60\nposition = 11520,0\n') +base='[global]\nTIMEOUT=9999\nFOCUSED_TIMEOUT=9999\nCHECK_INTERVAL=3\nINHIBIT_VIDEO={inhibit}\nLOG_FILE={log}\n[matrix]\nCOLOR={color}\nFONT_SIZE=14\nDEPTH_LAYERS=8\nTRANSPARENT_TIMEOUT=600\nBLACK_RAIN_TIMEOUT=600\nMIN_SPEED=0.1\nMAX_SPEED=1.25\nAVG_SPEED=0.18\n' +(root/'rain.conf').write_text(base.format(inhibit=0,log=root/'manager.log',color='green')) +(root/'rain.toml').write_text(base.format(inhibit='false',log=json.dumps(str(root/'manager.log')),color='"green"')) +def spawn(argv,debug=False): + e=env.copy() + if debug:e['WAYLAND_DEBUG']='client' + log=root/f'child-{len(children)}.log' + with log.open('w') as stream:p=subprocess.Popen(argv,env=e,stdout=stream,stderr=stream) + children.append(p);p.log=log;return p +def stop(p): + if p.poll() is None: + p.terminate() + try:p.wait(8) + except subprocess.TimeoutExpired:p.kill();p.wait() +def until(fn,timeout=10): + end=time.monotonic()+timeout + while time.monotonic()<end: + try: + result=fn() + if result:return result + except (ConnectionError,FileNotFoundError):pass + time.sleep(.05) + raise AssertionError('timed out') +def ipc(method,data): + with socket.socket(socket.AF_UNIX) as s: + s.settimeout(4);s.connect(env['WAYFIRE_SOCKET']);body=json.dumps(dict(method=method,data=data)).encode();s.sendall(struct.pack('=I',len(body))+body) + def read(n): + b=b'' + while len(b)<n:b+=s.recv(n-len(b)) + return b + return json.loads(read(struct.unpack('=I',read(4))[0])) +def api(method,**kw): + with socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET) as s: + s.settimeout(10);s.connect(str(root/'digitalrain/control.sock'));s.send(json.dumps(dict(version=1,method=method,**kw)).encode());v=json.loads(s.recv(16384));assert 'error' not in v,v;return v +def descendants(pid): + children=pathlib.Path(f'/proc/{pid}/task/{pid}/children').read_text().split() + return [int(p) for p in children] +def counters(pid): + f=pathlib.Path(f'/proc/{pid}/stat').read_text().split();cpu=(int(f[13])+int(f[14]))/os.sysconf('SC_CLK_TCK');ctx=0 + for task in pathlib.Path(f'/proc/{pid}/task').iterdir(): + for line in (task/'status').read_text().splitlines(): + if line.startswith(('voluntary_ctxt_switches:','nonvoluntary_ctxt_switches:')):ctx+=int(line.split()[1]) + status=pathlib.Path(f'/proc/{pid}/status').read_text().splitlines();rss=int(next(x.split()[1] for x in status if x.startswith('VmRSS:'))) + return cpu,ctx,rss +commit_re=re.compile(r'\[(\d+):(\d+):([0-9.]+)\].*?wl_surface[@#](\d+)\.commit\(') +def frame_stats(text): + groups={} + for line in text.splitlines(): + m=commit_re.search(line) + if m:groups.setdefault(m[4],[]).append((int(m[1])*3600+int(m[2])*60+float(m[3]))*1000) + gaps=sorted(b-a for values in groups.values() for a,b in zip(values,values[1:]) if b>a) + return dict(commits=sum(map(len,groups.values())),median_ms=round(statistics.median(gaps),3),p95_ms=round(gaps[int(len(gaps)*.95)],3),p99_ms=round(gaps[int(len(gaps)*.99)],3)) if gaps else {} +try: + flags=shlex.split(subprocess.check_output(['pkg-config','--cflags','--libs','wayfire','wf-utils'],text=True)) + subprocess.run(['c++','-std=c++17','-fPIC','-shared','-DWAYFIRE_PLUGIN','-DWLR_USE_UNSTABLE',str(pkg/'tests/desktop-test.cpp'),'-o',str(root/'libdesktop-test.so'),*flags],check=True) + (root/'desktop-test.xml').write_text('<wayfire><plugin name="desktop-test"><_short>Test</_short></plugin></wayfire>') + wf=spawn(['wayfire','-c',str(root/'wayfire.ini')]);until(lambda:list(root.glob('wayfire-*.socket'))) + env['WAYFIRE_SOCKET']=str(next(root.glob('wayfire-*.socket')));env['WAYLAND_DISPLAY']=next(x.name for x in root.glob('wayland-*') if not x.name.endswith('.lock')) + time.sleep(1);ipc('test/activity',dict(x=12000,y=500));time.sleep(.2) + server=spawn(['pipewire']);until(lambda:(root/'pipewire-0').exists());policy=spawn(['dbus-run-session','--','wireplumber','--profile','policy']);time.sleep(1) + subprocess.run(['pw-cli','create-node','adapter','{ factory.name = support.null-audio-sink node.name = rain-benchmark media.class = Audio/Sink object.linger = true audio.position = [ FL FR ] }'],env=env,check=True,stdout=subprocess.DEVNULL) + tone=root/'tone.wav' + # 15 minutes, generated as repeated one-second stereo blocks. + samples=array.array('h',(int(.25*32767*math.sin(2*math.pi*440*i/48000)) for i in range(48000) for _ in range(2))) + with wave.open(str(tone),'wb') as w: + w.setnchannels(2);w.setsampwidth(2);w.setframerate(48000) + for _ in range(900):w.writeframesraw(samples.tobytes()) + playback=spawn(['pw-play','--target','rain-benchmark',str(tone)]) + cases=[('old idle',old,0,False,False),('new idle',new,0,False,False),('new subscriber',new,0,False,False),('old one 4K',old,1,False,False),('new one 4K',new,1,False,False),('old three 4K',old,3,False,False),('new three 4K',new,3,False,False),('new alerts three 4K',new,3,True,False),('new audio three 4K',new,3,False,True)] + for repetition in range(repetitions): + for label,exe,count,alert,audio in cases: + if selected and label not in selected.split(','):continue + assert server.poll() is None and playback.poll() is None,'Private audio server/playback exited; discard interrupted run' + env['DIGITALRAIN_CONFIG']=str(root/('rain.conf' if exe==old else 'rain.toml')) + manager=spawn([exe]);until(lambda:api('status').get('outputs')) + if audio:api('settings',settings={'audio.ENABLED':1,'audio.SOURCE':'rain-benchmark'}) + for i in range(count):api('activate',output=f'HEADLESS-{i+1}') + if alert:api('alert',source='benchmark',id='busy',severity='error',seconds=300) + if audio:until(lambda:api('status')['audio']['state']=='monitoring') + watcher=None + if label=='new subscriber': + watcher=socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET);watcher.connect(str(root/'digitalrain/control.sock'));watcher.send(b'{"version":1,"method":"watch"}');watcher.recv(16384) + time.sleep(3) + if count:assert sum(o['saver'] for o in api('status')['outputs'])==count,api('status') + pids=[manager.pid,*descendants(manager.pid),wf.pid,server.pid,policy.pid,playback.pid] + names={pid:('manager' if pid==manager.pid else 'wayfire' if pid==wf.pid else 'pipewire' if pid==server.pid else 'policy' if pid==policy.pid else 'playback' if pid==playback.pid else 'audio' if b'digitalrain-audio' in pathlib.Path(f'/proc/{pid}/cmdline').read_bytes() else 'renderers') for pid in pids} + before={pid:counters(pid) for pid in pids};offset=manager.log.stat().st_size;start=time.monotonic();time.sleep(duration);elapsed=time.monotonic()-start;after={pid:counters(pid) for pid in pids};cost={} + for pid in pids: + v=cost.setdefault(names[pid],dict(cpu_percent=0,switches_per_second=0,rss_kib=0));a=before[pid];b=after[pid];v['cpu_percent']+=100*(b[0]-a[0])/elapsed;v['switches_per_second']+=(b[1]-a[1])/elapsed;v['rss_kib']+=b[2] + for v in cost.values(): + for k in v:v[k]=round(v[k],3) + frames={} + row=dict(case=label,repetition=repetition+1,seconds=elapsed,cost=cost,frames=frames);results.append(row);print(json.dumps(row),flush=True) + if audio: + top=subprocess.run(['pw-top','-b','-n','2'],env=env,text=True,capture_output=True,timeout=8);(root/f'pw-top-{repetition}.txt').write_text(top.stdout) + if watcher:watcher.close() + stop(manager);time.sleep(.4) + for repetition in range(repetitions): + for exe,label in [(old,'old frame cadence'),(new,'new frame cadence')]: + renderer=spawn([exe,'--render','--output','HEADLESS-1','--depth-layers','8','--font-size','14','--min-speed','0.1','--max-speed','1.25','--avg-speed','0.18'],debug=True) + time.sleep(3);offset=renderer.log.stat().st_size;time.sleep(duration) + with renderer.log.open() as stream:stream.seek(offset);frames=frame_stats(stream.read()) + assert frames.get('commits',0)>100,frames + row=dict(case=label,repetition=repetition+1,frames=frames);results.append(row);print(json.dumps(row),flush=True);stop(renderer) + (root/'results.json').write_text(json.dumps(results,indent=2));print('Artifacts:',root,flush=True) +finally: + for child in reversed(children):stop(child) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/desktop-test.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/desktop-test.cpp new file mode 100644 index 000000000000..4fa668daab5e --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/desktop-test.cpp @@ -0,0 +1,98 @@ +/* Solid test surfaces make unwanted desktop pixels measurable in screenshots. */ +#include <map> +#include <wayfire/seat.hpp> +#include <wayfire/compositor-view.hpp> +#include <wayfire/core.hpp> +#include <wayfire/output-layout.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/scene.hpp> +#include <wayfire/workspace-stream.hpp> +#include <wayfire/workspace-set.hpp> +#include <wayfire/scene-operations.hpp> +#include <wayfire/nonstd/wlroots-full.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> + +class desktop_test_t : public wf::plugin_interface_t +{ + wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; + std::map<std::string, std::shared_ptr<wf::color_rect_view_t>> solids; + std::shared_ptr<wf::workspace_stream_node_t> stream; + public: + void init() override + { + repo->register_method("test/workspace-stream", [this] (wf::json_t data) + { + if (stream) wf::scene::remove_child(stream); + stream.reset(); + if (!data.has_member("close")) + { + auto output = wf::get_core().output_layout->find_output(data["output"].as_string()); + auto ws = output->wset()->get_current_workspace(); + if (data.has_member("workspace")) ws = {data["workspace"].as_int(), ws.y}; + stream = std::make_shared<wf::workspace_stream_node_t>(output, ws); + wf::scene::add_front(wf::get_core().scene(), stream); + } + return wf::ipc::json_ok(); + }); + repo->register_method("test/output-power", [] (wf::json_t) + { + wf::json_t result; + for (auto& [head, state] : wf::get_core().output_layout->get_current_configuration()) + result[std::string(head->name)] = head->enabled; + return result; + }); + repo->register_method("test/remove-output", [] (wf::json_t data) + { + for (auto& [head, state] : wf::get_core().output_layout->get_current_configuration()) + if (data["output"].as_string() == head->name) { + wlr_output_destroy(head); + return wf::ipc::json_ok(); + } + return wf::ipc::json_error("Unknown test output"); + }); + repo->register_method("test/activity", [] (wf::json_t data) + { + wf::get_core().warp_cursor({data["x"].as_double(), data["y"].as_double()}); + wf::get_core().seat->notify_activity(); + return wf::ipc::json_ok(); + }); + repo->register_method("test/solid", [this] (wf::json_t data) + { + auto output = wf::get_core().output_layout->find_output(data["output"].as_string()); + auto& view = solids[data["key"].as_string()]; + if (data.has_member("close")) + { + if (view) + { + view->damage(); + wf::scene::set_node_enabled(view->get_root_node(), false); + view->close(); + view.reset(); + } + return wf::ipc::json_ok(); + } + if (!view) view = wf::color_rect_view_t::create(wf::VIEW_ROLE_DESKTOP_ENVIRONMENT, + output, (wf::scene::layer)data["layer"].as_int()); + view->set_geometry({double(data["x"].as_int()), double(data["y"].as_int()), + double(data["w"].as_int()), double(data["h"].as_int())}); + view->set_color({data["r"].as_double(), data["g"].as_double(), + data["b"].as_double(), 1.0}); + view->set_border(0); + return wf::ipc::json_ok(); + }); + } + void fini() override + { + repo->unregister_method("test/workspace-stream"); + if (stream) wf::scene::remove_child(stream); + stream.reset(); + repo->unregister_method("test/output-power"); + repo->unregister_method("test/remove-output"); + repo->unregister_method("test/solid"); + repo->unregister_method("test/activity"); + for (auto& [key, view] : solids) if (view) view->close(); + solids.clear(); + } +}; +DECLARE_WAYFIRE_PLUGIN(desktop_test_t); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/lock-test.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/lock-test.cpp new file mode 100644 index 000000000000..1b1e406069f3 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/lock-test.cpp @@ -0,0 +1,67 @@ +/* Test-only input and compositor inspection. Never installed. */ +#include <wayfire/core.hpp> +#include <wayfire/seat.hpp> +#include <wayfire/output.hpp> +#include <wayfire/output-layout.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/nonstd/wlroots-full.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> +extern "C" { +#include <wlr/interfaces/wlr_keyboard.h> +#include <wlr/backend/headless.h> +#include <wlr/backend/multi.h> +} +class lock_test : public wf::plugin_interface_t { + wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; + wlr_keyboard keyboard{}; + wlr_keyboard_impl impl{.name = "digitalrain-lock-test"}; + + public: + void init() override { + wlr_keyboard_init(&keyboard, &impl, "digitalrain-lock-test"); + auto ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + auto keymap = xkb_keymap_new_from_names(ctx, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS); + wlr_keyboard_set_keymap(&keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(ctx); + wl_signal_emit_mutable(&wf::get_core().backend->events.new_input, &keyboard.base); + repo->register_method("test/key", [this](wf::json_t data) { + wlr_keyboard_key_event ev{.time_msec = 1, + .keycode = (uint32_t)data["code"].as_int(), + .update_state = true, + .state = WL_KEYBOARD_KEY_STATE_PRESSED}; + wlr_keyboard_notify_key(&keyboard, &ev); + ev.state = WL_KEYBOARD_KEY_STATE_RELEASED; + wlr_keyboard_notify_key(&keyboard, &ev); + return wf::ipc::json_ok(); + }); + repo->register_method("test/lock-state", [](wf::json_t) { + wf::json_t result; + for (auto o : wf::get_core().output_layout->get_outputs()) + result[o->to_string()] = o->is_inhibited(); + return result; + }); + repo->register_method("test/add-output", [](wf::json_t) { + wlr_backend *headless = nullptr; + wlr_multi_for_each_backend( + wf::get_core().backend, + [](wlr_backend *b, void *d) { + if (wlr_backend_is_headless(b)) + *static_cast<wlr_backend **>(d) = b; + }, + &headless); + if (!headless) + return wf::ipc::json_error("no headless backend"); + wlr_headless_add_output(headless, 320, 240); + return wf::ipc::json_ok(); + }); + } + void fini() override { + repo->unregister_method("test/key"); + repo->unregister_method("test/lock-state"); + repo->unregister_method("test/add-output"); + wlr_keyboard_finish(&keyboard); + } +}; +DECLARE_WAYFIRE_PLUGIN(lock_test); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/pam-test.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/pam-test.c new file mode 100644 index 000000000000..4b45ee702c57 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/pam-test.c @@ -0,0 +1,27 @@ +/* Deterministic test PAM policy. Compile only for an isolated test service. */ +#include <security/pam_modules.h> +#include <security/pam_ext.h> +#include <string.h> +PAM_EXTERN int pam_sm_authenticate(pam_handle_t *p, int f, int n, const char **v) { + (void)f; + (void)n; + (void)v; + const char *password = NULL; + if (pam_get_authtok(p, PAM_AUTHTOK, &password, NULL) != PAM_SUCCESS) + return PAM_AUTH_ERR; + return password && !strcmp(password, "rain-test-7") ? PAM_SUCCESS : PAM_AUTH_ERR; +} +PAM_EXTERN int pam_sm_setcred(pam_handle_t *p, int f, int n, const char **v) { + (void)p; + (void)f; + (void)n; + (void)v; + return PAM_SUCCESS; +} +PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *p, int f, int n, const char **v) { + (void)p; + (void)f; + (void)n; + (void)v; + return PAM_SUCCESS; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/portal-player.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/portal-player.c new file mode 100644 index 000000000000..96dd4e5f92e7 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/portal-player.c @@ -0,0 +1,108 @@ +/* A real Wayland application using the public portal API, not a broker PID + * stand-in. Commands let the private suite exercise request/window lifetimes. */ +#include <gtk/gtk.h> +#include <gdk/gdkwayland.h> +#include <stdio.h> + +static GDBusConnection *bus; +static GtkWidget *window; +static GPtrArray *requests; +static char *parent; + +static void exported(GdkWindow *w, const char *handle, gpointer data) +{ + g_free(parent); + parent = g_strconcat("wayland:", handle, NULL); + puts("READY"); +} + +static void connect_bus(void) +{ + bus = g_dbus_connection_new_for_address_sync(g_getenv("DBUS_SESSION_BUS_ADDRESS"), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, NULL, NULL); + g_assert(bus); + g_dbus_connection_set_exit_on_close(bus, FALSE); +} + +static void make_window(void) +{ + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), "Portal video fixture"); + gtk_window_set_default_size(GTK_WINDOW(window), 350, 300); + gtk_container_add(GTK_CONTAINER(window), gtk_label_new("Portal playing")); + gtk_widget_show_all(window); + g_assert(gdk_wayland_window_export_handle(gtk_widget_get_window(window), exported, NULL, NULL)); +} + +static gboolean command(GIOChannel *channel, GIOCondition condition, gpointer data) +{ + char *line = NULL; + GError *error = NULL; + if (g_io_channel_read_line(channel, &line, NULL, NULL, NULL) != G_IO_STATUS_NORMAL) + return FALSE; + g_strchomp(line); + gboolean firefox = g_str_has_prefix(line, "INHIBIT_FIREFOX "); + if (firefox || g_str_has_prefix(line, "INHIBIT ")) { + const char *selector = line + (firefox ? 16 : 8); + char *token = g_strdup_printf("test%u", requests->len); + GVariantBuilder options; + g_variant_builder_init(&options, G_VARIANT_TYPE_VARDICT); + if (!firefox) + g_variant_builder_add(&options, "{sv}", "handle_token", g_variant_new_string(token)); + g_variant_builder_add(&options, "{sv}", "reason", g_variant_new_string("Playing private test video")); + const char *handle = strcmp(selector, "parent") == 0 ? parent : + strcmp(selector, "empty") == 0 ? "" : selector; + GVariant *reply = g_dbus_connection_call_sync(bus, "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", "org.freedesktop.portal.Inhibit", "Inhibit", + g_variant_new("(su@a{sv})", handle, 8, g_variant_builder_end(&options)), + G_VARIANT_TYPE("(o)"), 0, 5000, NULL, &error); + if (reply) { + char *path; + g_variant_get(reply, "(o)", &path); + g_ptr_array_add(requests, path); + printf("HANDLE %s\n", path); + g_variant_unref(reply); + } + g_free(token); + } else if (!strcmp(line, "PARENT")) { + printf("PARENT %s\n", parent); + } else if (g_str_has_prefix(line, "CLOSE ")) { + unsigned index = strtoul(line + 6, NULL, 10); + g_assert(index < requests->len); + GVariant *reply = g_dbus_connection_call_sync(bus, "org.freedesktop.portal.Desktop", + g_ptr_array_index(requests, index), "org.freedesktop.portal.Request", "Close", + NULL, NULL, 0, 5000, NULL, &error); + if (reply) { puts("OK"); g_variant_unref(reply); } + } else if (!strcmp(line, "DISCONNECT")) { + g_dbus_connection_close_sync(bus, NULL, NULL); + g_clear_object(&bus); + puts("OK"); + } else if (!strcmp(line, "RECONNECT")) { + connect_bus(); puts("OK"); + } else if (!strcmp(line, "DESTROY")) { + gtk_widget_destroy(window); window = NULL; puts("OK"); + } else if (!strcmp(line, "NEW")) { + make_window(); + } else if (!strcmp(line, "SECOND")) { + GtkWidget *other = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(other), "Second same-process window"); + gtk_widget_show_all(other); puts("OK"); + } + if (error) { printf("ERROR %s\n", error->message); g_error_free(error); } + g_free(line); + return TRUE; +} + +int main(int argc, char **argv) +{ + setvbuf(stdout, NULL, _IOLBF, 0); + gtk_init(&argc, &argv); + connect_bus(); + requests = g_ptr_array_new_with_free_func(g_free); + GIOChannel *input = g_io_channel_unix_new(0); + g_io_add_watch(input, G_IO_IN | G_IO_HUP, command, NULL); + make_window(); + gtk_main(); + return 0; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/run-c-tests.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/run-c-tests.py new file mode 100755 index 000000000000..5428040ecfc6 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/run-c-tests.py @@ -0,0 +1,7 @@ +import pathlib,shlex,subprocess +pkg=pathlib.Path(__file__).resolve().parents[1];files=pkg +flags=shlex.split(subprocess.check_output(['pkg-config','--cflags','--libs','json-c','wayland-client','wayland-egl','egl','glesv2','freetype2'],text=True)) +for name in ['topology','inhibition','glyph-compositing','live-render']: + out=pathlib.Path('/tmp')/f'digitalrain-test-{name}' + subprocess.run(['cc','-std=c11','-O2','-I'+str(files),str(pkg/f'tests/test-{name}.c'),str(files/'tomlc17.c'),*map(str,files.glob('*-protocol.c')),*flags,'-lpthread','-lm',*(['-Wl,--wrap=calloc'] if name=='live-render' else []),'-o',str(out)],check=True) + subprocess.run([str(out)],check=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/run-native-lock-tests.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/run-native-lock-tests.py new file mode 100755 index 000000000000..8274e5892e57 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/run-native-lock-tests.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Provision a temporary test-only PAM service; always remove it afterward. +Requires root for the policy file, --user selects the unprivileged test runner. +All compositor/input activity happens in the suite's private headless session. +""" +import argparse +import os +from pathlib import Path +import pwd +import subprocess +import tempfile + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--user', required=True) + parser.add_argument('--binary-dir', type=Path, help='Use installed binaries instead of files/') + parser.add_argument('--test-script', type=Path, help='Alternative private lock integration suite') + parser.add_argument('--stage', type=Path, help='Private compositor and renderer fixture') + args = parser.parse_args() + if os.getuid() != 0 or pwd.getpwnam(args.user).pw_uid == 0: + parser.error('Run as root and select a non-root user') + package = Path(__file__).resolve().parents[1] + service = f'digitalrain-test-{os.getpid()}' + policy = Path('/etc/pam.d') / service + with tempfile.TemporaryDirectory(prefix='digitalrain-test-pam-') as temporary: + directory = Path(temporary) + directory.chmod(0o755) + module, worker = directory / 'pam-test.so', directory / 'digitalrain-auth' + subprocess.run(['cc', '-shared', '-fPIC', '-o', str(module), str(package / 'tests/pam-test.c'), '-lpam'], check=True) + subprocess.run(['cc', '-O2', f'-DDIGITALRAIN_PAM_SERVICE="{service}"', '-o', str(worker), str(package / 'digitalrain-auth.c'), '-lpam'], check=True) + module.chmod(0o755) + worker.chmod(0o755) + with policy.open('x') as stream: + stream.write(f'auth required {module}\naccount required {module}\n') + try: + policy.chmod(0o644) + command = ['runuser', '-u', args.user, '--', 'python3', str(args.test_script or package / 'tests/test-native-lock.py'), '--auth-helper', str(worker)] + if args.stage: + command += ['--stage', str(args.stage.resolve())] + if args.binary_dir: + command += ['--binary-dir', str(args.binary_dir.resolve())] + return subprocess.run(command).returncode + finally: + policy.unlink() + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-audio.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-audio.py new file mode 100755 index 000000000000..6598f77414f3 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-audio.py @@ -0,0 +1,76 @@ +"""Playback analysis using private PipeWire, policy-only WirePlumber and virtual sinks. +Uses a private D-Bus session; no hardware monitors or desktop sockets are used. +""" +import array, json, math, os, pathlib, socket, struct, subprocess, sys, tempfile, time, wave +binary=str(pathlib.Path(sys.argv[1]).resolve()) +root=pathlib.Path(tempfile.mkdtemp(prefix='digitalrain-audio-test-'));children=[] +env=os.environ.copy();env.update(XDG_RUNTIME_DIR=str(root),XDG_CONFIG_HOME=str(root/'config'),XDG_STATE_HOME=str(root/'state'),PIPEWIRE_REMOTE='pipewire-0') +for k in ['PIPEWIRE_CONFIG_DIR','PIPEWIRE_CONFIG_NAME','WIREPLUMBER_CONFIG_DIR','PIPEWIRE_RUNTIME_DIR']:env.pop(k,None) +def spawn(args,**kwargs): + with (root/f'child-{len(children)}.log').open('w') as out: p=subprocess.Popen(args,env=env,stdout=out,stderr=out,**kwargs) + children.append(p);return p +def command(*args):return subprocess.check_output(args,env=env,stderr=subprocess.STDOUT,text=True,timeout=5) +def until(fn,timeout=8): + end=time.monotonic()+timeout + while time.monotonic()<end: + try: + value=fn() + if value:return value + except (subprocess.CalledProcessError,ConnectionError):pass + time.sleep(.05) + raise AssertionError('timed out') +def sinks(): + return {n['info']['props']['node.name']:n['id'] for n in json.loads(command('pw-dump')) if n['type']=='PipeWire:Interface:Node' and n.get('info',{}).get('props',{}).get('media.class')=='Audio/Sink'} +def create(name): + command('pw-cli','create-node','adapter',f'{{ factory.name = support.null-audio-sink node.name = {name} media.class = Audio/Sink object.linger = true audio.position = [ FL FR ] }}') + return until(lambda:sinks().get(name)) +def default(name):command('pw-metadata','-n','default','0','default.audio.sink',json.dumps({'name':name}),'Spa:String:JSON') +connections=[] +def helper(source): + left,right=socket.socketpair(socket.AF_UNIX,socket.SOCK_SEQPACKET);left.settimeout(3) + p=spawn([binary,str(right.fileno()),source],pass_fds=[right.fileno()]);right.close();connections.append(left);return p,left +def collect(s,seconds): + end=time.monotonic()+seconds;values=[] + while time.monotonic()<end: + raw=s.recv(1024);assert len(raw)==24,raw + magic,available,timestamp,rms,peak=struct.unpack('=IIdff',raw) + assert magic==0x44524132 and 0<=rms<=1 and 0<=peak<=1 + values.append((timestamp,available,rms,peak)) + return values +def tone(name, amplitude=.25, seconds=2): + path=root/f'{name}-{amplitude}.wav' + samples=array.array('h',(int(amplitude*32767*math.sin(2*math.pi*440*i/48000)) for i in range(int(seconds*48000)) for _ in range(2))) + with wave.open(str(path),'wb') as w:w.setnchannels(2);w.setsampwidth(2);w.setframerate(48000);w.writeframes(samples.tobytes()) + return spawn(['pw-play','--target',name,str(path)]) +try: + server=spawn(['pipewire']);until(lambda:(root/'pipewire-0').exists()) + policy=spawn(['dbus-run-session','--','wireplumber','--profile','policy']);until(lambda:'default' in command('pw-metadata','-l')) + first=create('rain-test-one');second=create('rain-test-two');assert set(sinks())=={'rain-test-one','rain-test-two'},sinks() + default('rain-test-one') + process,s=helper('default-output');until(lambda:any(v[1] for v in collect(s,.15))) + playback=tone('rain-test-one');values=collect(s,1.6) + assert max(v[2] for v in values)>.12 and max(v[3] for v in values)>.23,values + assert max(v[2] for v in values)<.20,values + playback.wait(5);values=collect(s,.6);assert values[-1][2]==0,values + print('Virtual playback RMS/peak and silence: PASS',flush=True) + playback=tone('rain-test-one',seconds=3);collect(s,.4) + command('pw-cli','set-param',str(first),'Props','{ mute = true }');values=collect(s,.7);assert all(v[2]==0 for v in values[-5:]),values + command('pw-cli','set-param',str(first),'Props','{ mute = false }');values=collect(s,.5);assert max(v[2] for v in values)>.12 + playback.wait(5) + default('rain-test-two');time.sleep(.3);playback=tone('rain-test-two');values=collect(s,1.4);assert max(v[2] for v in values)>.12,values;playback.wait(5) + print('Sink mute and default playback-device switch: PASS',flush=True) + named,n=helper('rain-test-one');playback=tone('rain-test-one');values=collect(n,1.4);assert max(v[2] for v in values)>.12;playback.wait(5) + command('pw-cli','destroy',str(first));values=collect(n,.6);assert values[-1][1]==0 and values[-1][2]==0 + first=create('rain-test-one');time.sleep(.3);playback=tone('rain-test-one');values=collect(n,1.4);assert max(v[2] for v in values)>.12;playback.wait(5) + print('Named playback sink unplug/reconnect: PASS',flush=True) + absent,a=helper('missing-output');values=collect(a,.4);assert all(v[1]==0 and v[2]==0 for v in values) + assert set(sinks())=={'rain-test-one','rain-test-two'} + print('Missing sink remains unavailable without input fallback: PASS',flush=True) +finally: + for s in connections:s.close() + for p in reversed(children): + if p.poll() is None: + p.terminate() + try:p.wait(4) + except subprocess.TimeoutExpired:p.kill();p.wait() + print('Artifacts:',root,flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-black-rain-timeout.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-black-rain-timeout.py new file mode 100755 index 000000000000..334c9a1f806b --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-black-rain-timeout.py @@ -0,0 +1,178 @@ +"""Run ONLY in an isolated Wayfire session with three HEADLESS outputs. + +Usage: python3 test-black-rain-timeout.py /path/to/digitalrain +Needs Wayfire ipc/ipc-rules, wlr-randr, swaybg, grim, and Pillow. +Uses seconds-long timers; does not change the user's config or power outputs off. +""" +import io +import json +import os +from pathlib import Path +import signal +import socket +import struct +import subprocess +import sys +import tempfile +import time + +from PIL import Image + + +def ipc(method, data=None): + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: + sock.settimeout(5) + sock.connect(os.environ['WAYFIRE_SOCKET']) + payload = json.dumps({'method': method, 'data': data or {}}).encode() + sock.sendall(struct.pack('<I', len(payload)) + payload) + + def read(n): + result = b'' + while len(result) < n: + part = sock.recv(n - len(result)) + if not part: + raise RuntimeError('IPC disconnected') + result += part + return result + + return json.loads(read(struct.unpack('<I', read(4))[0])) + + +outputs = ipc('window-rules/list-outputs') +assert {o['name'] for o in outputs} == {'HEADLESS-1', 'HEADLESS-2', 'HEADLESS-3'}, \ + 'This test must run in its own three-output headless compositor' +binary = str(Path(sys.argv[1]).resolve()) +children = [] +logs = tempfile.TemporaryDirectory(prefix='digitalrain-black-test-') + + +def spawn(command): + log = open(Path(logs.name) / f'{len(children)}.log', 'w') + process = subprocess.Popen(command, stdout=log, stderr=subprocess.STDOUT) + log.close() + children.append(process) + return process + + +def rain(output, timeout, transparent=True, transparent_timeout=1, fade_frames=6): + command = [binary, '--render', '--output', output, '--color', 'adaptive', + '--fade-frames', str(fade_frames), '--min-density', '0.8', + '--max-density', '0.8', '--black-rain-timeout', str(timeout), + '--transparent-timeout', str(transparent_timeout)] + if transparent: + command.append('--transparent') + return spawn(command) + + +def shot(output): + png = subprocess.check_output(['grim', '-o', output, '-']) + return Image.open(io.BytesIO(png)).convert('RGB') + + +def black(output): + return shot(output).getbbox() is None + + +def desktop_pixels(output): + return sum(r > 150 and g < 20 and b < 20 + for r, g, b in shot(output).get_flattened_data()) + + +def until(check, timeout=6): + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if check(): + return + time.sleep(.1) + raise AssertionError('Timed out waiting for expected state') + + +def cpu_ticks(process): + fields = Path(f'/proc/{process.pid}/stat').read_text().split() + return int(fields[13]) + int(fields[14]) + + +def dismiss(process): + process.send_signal(signal.SIGUSR1) + assert process.wait(timeout=3) == 0 + + +try: + command = ['wlr-randr'] + for i in range(1, 4): + command += ['--output', f'HEADLESS-{i}', '--custom-mode', '640x480', + '--scale', '1', '--transform', 'normal', '--pos', f'{(i-1)*640},0'] + subprocess.run(command, check=True) + spawn(['swaybg', '-c', 'cc0000']) + until(lambda: desktop_pixels('HEADLESS-1') > 100000) + + a = rain('HEADLESS-1', 2) + time.sleep(.7) + assert desktop_pixels('HEADLESS-1') > 100000, 'transparent stage missing' + time.sleep(1) + assert desktop_pixels('HEADLESS-1') == 0 + assert not black('HEADLESS-1'), 'rain must continue after background blackens' + b = rain('HEADLESS-2', 2) + c = rain('HEADLESS-3', 0, transparent=False) + until(lambda: black('HEADLESS-1')) + assert not black('HEADLESS-2'), 'later output must keep its own timer' + assert not black('HEADLESS-3'), 'zero must disable the rain-stop timer' + until(lambda: black('HEADLESS-2')) + assert all(p.poll() is None for p in (a, b, c)), 'black must retain the renderer' + assert not black('HEADLESS-3') + print('Transparent -> rain on black -> solid black; independent timers; zero disables: PASS', flush=True) + dismiss(c) + + # Palette notifications must not restart animation or redraw a black output. + time.sleep(.4) + ticks = [cpu_ticks(p) for p in (a, b)] + stats = ipc('test/stats') # Optional fixture measuring compositor render passes. + a.send_signal(signal.SIGRTMIN) + time.sleep(1) + delta = [cpu_ticks(p) - ticks[i] for i, p in enumerate((a, b))] + assert max(delta) <= 1, delta + after = ipc('test/stats') + if 'passes' in stats: + assert after['passes'] == 0, after + assert black('HEADLESS-1') and black('HEADLESS-2') + print(f'Static black sleeps; palette update keeps it black (CPU ticks {delta}): PASS', flush=True) + + subprocess.run(['wlr-randr', '--output', 'HEADLESS-1', '--custom-mode', '800x600'], check=True) + time.sleep(.4) + resized = shot('HEADLESS-1') + assert resized.size == (800, 600) and resized.getbbox() is None + assert len(ipc('window-rules/list-outputs')) == 3 + print('Resize remains fully black; outputs stay enabled: PASS', flush=True) + + # Capture the reveal: its pixels may only be black or the red desktop, not rain. + a.send_signal(signal.SIGUSR1) + while a.poll() is None: + reveal = shot('HEADLESS-1') + assert all(g == 0 and b == 0 for r, g, b in reveal.get_flattened_data()) + assert a.returncode == 0 + assert desktop_pixels('HEADLESS-1') > 100000 + dismiss(b) + print('Activity reveals desktop without bringing rain back: PASS', flush=True) + + c = rain('HEADLESS-3', .5, transparent=True, transparent_timeout=0) + time.sleep(1.4) + assert desktop_pixels('HEADLESS-3') > 100000 + dismiss(c) + c = rain('HEADLESS-3', .8, transparent=False) + time.sleep(.7) + assert not black('HEADLESS-3') + until(lambda: black('HEADLESS-3')) + assert c.poll() is None + c.terminate() + assert c.wait(timeout=2) == 0 + print('Timer waits for black; initially opaque mode and termination work: PASS', flush=True) +finally: + for process in reversed(children): + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=5) + except subprocess.TimeoutExpired: + process.kill() + process.wait() + logs.cleanup() diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-cursor-wakeup.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-cursor-wakeup.py new file mode 100755 index 000000000000..f86209b1adb3 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-cursor-wakeup.py @@ -0,0 +1,176 @@ +"""Exercise the production manager against private, simulated Wayfire IPC. + +Run from any directory with Python 3; --source accepts an older digitalrain.c +for before/after checks. No desktop sockets or configuration are used. +""" +import argparse +import json +import os +from pathlib import Path +import shlex +import socket +import struct +import subprocess +import tempfile +import threading +import time + + +package = Path(__file__).resolve().parents[1] +parser = argparse.ArgumentParser() +parser.add_argument('--source', type=Path, default=package / 'digitalrain.c') +args = parser.parse_args() + +HARNESS = r''' +#define main digitalrain_program_main +#include DIGITALRAIN_SOURCE +#undef main +#include <assert.h> + +int main(int argc, char **argv) { + assert(argc == 2); + pthread_mutex_init(&G.lock, NULL); + G.output_count = 3; + mgr_init_output(&G.outputs[0], "DP-1", mgr_monotime()); + mgr_init_output(&G.outputs[1], "DP-2", mgr_monotime()); + mgr_init_output(&G.outputs[2], "DP-3", mgr_monotime()); + strcpy(G.outputs[0].state, "off"); + strcpy(G.outputs[1].state, "off"); + G.seat_idle = strcmp(argv[1], "active") != 0; + if (!strncmp(argv[1], "fade", 4)) { + /* A renderer that deliberately needs 1.2 seconds to finish its fade. */ + int ready[2]; + assert(pipe(ready) == 0); + pid_t child = fork(); + assert(child >= 0); + if (child == 0) { + close(ready[0]); + signal(SIGUSR1, SIG_IGN); + assert(write(ready[1], "r", 1) == 1); + usleep(1200000); + _exit(0); + } + close(ready[1]); + char byte; + assert(read(ready[0], &byte, 1) == 1); + close(ready[0]); + G.wayfire_cursor_hidden = 1; + G.outputs[0].pids[0] = child; + G.outputs[0].pid_count = 1; + mgr_unblank_output("DP-1"); + assert(!strcmp(G.outputs[0].state, "on")); + assert(!strcmp(G.outputs[1].state, "off")); + assert(G.outputs[0].pid_count == 0); + /* The production loop now reaps fades asynchronously. Keep polling + * cursor state while this detached renderer is still fading. */ + double end = mgr_monotime() + 1.3; + while (mgr_monotime() < end) { + mgr_update_wayfire_cursor(); mgr_reap_fading(); usleep(100000); + } + mgr_reap_fading(); + } else { + mgr_update_wayfire_cursor(); + mgr_update_wayfire_cursor(); /* repeated polls must be idempotent */ + } + pthread_mutex_destroy(&G.lock); + return 0; +} +''' + + +def read(sock, count): + data = b'' + while len(data) < count: + part = sock.recv(count - len(data)) + if not part: + raise RuntimeError('IPC disconnected') + data += part + return data + + +def check(binary, runtime, case): + path = runtime / 'ipc.socket' + hidden = case != 'idle' + changes = [] + errors = [] + started = time.monotonic() + done = threading.Event() + with socket.socket(socket.AF_UNIX) as server: + server.bind(str(path)) + server.listen() + server.settimeout(.05) + + def serve(): + nonlocal hidden + try: + while not done.is_set(): + try: + connection, _ = server.accept() + except socket.timeout: + continue + with connection: + connection.settimeout(2) + request = json.loads(read(connection, struct.unpack('=I', read(connection, 4))[0])) + method = request['method'] + elapsed = time.monotonic() - started + output = 'DP-1' + if case == 'fade-crossing': + if elapsed >= .6: + output = 'DP-3' + elif elapsed >= .25: + output = 'DP-2' + if method == 'digitalrain/cursor_status': + response = dict(valid=True, hidden=hidden, output=output) + else: + assert method in ('digitalrain/hide_cursor', 'digitalrain/unhide_cursor'), method + hidden = method == 'digitalrain/hide_cursor' + changes.append((hidden, elapsed)) + response = dict(result='ok') + payload = json.dumps(response).encode() + connection.sendall(struct.pack('=I', len(payload)) + payload) + except Exception as error: + errors.append(error) + + thread = threading.Thread(target=serve) + thread.start() + try: + env = dict(os.environ, WAYFIRE_SOCKET=str(path)) + result = subprocess.run([str(binary), case], env=env, + capture_output=True, text=True, timeout=5) + assert result.returncode == 0, result.stdout + result.stderr + finally: + done.set() + thread.join(timeout=3) + path.unlink() + assert not errors, errors + expected = [True] if case == 'idle' else [False] + if case == 'fade-crossing': + expected = [False, True, False] + assert [value for value, _ in changes] == expected, changes + if case.startswith('fade'): + assert changes[0][1] < .5, f'cursor was hidden during fade: {changes}' + assert changes[-1][1] < 1.1, f'cursor tracking waited for renderer exit: {changes}' + print(f'PASS {case}: {changes}') + + +with tempfile.TemporaryDirectory(prefix='digitalrain-cursor-test-') as directory: + runtime = Path(directory) + harness = runtime / 'test.c' + harness.write_text(HARNESS) + binary = runtime / 'test' + flags = shlex.split(subprocess.check_output( + ['pkg-config', '--cflags', '--libs', 'json-c', 'wayland-client', + 'wayland-egl', 'egl', 'glesv2', 'freetype2'], text=True)) + subprocess.run(['cc', '-std=c11', '-O2', '-I' + str(package), + '-DDIGITALRAIN_SOURCE="' + str(args.source.resolve()) + '"', + str(harness), str(package / 'tomlc17.c'), *map(str, sorted((package).glob('*-protocol.c'))), + *flags, '-lpthread', '-lm', '-o', str(binary)], check=True) + failures = [] + for case in ['idle', 'active', 'fade', 'fade-crossing']: + try: + check(binary, runtime, case) + except AssertionError as error: + failures.append(case) + print(f'FAIL {case}: {error}') + if failures: + raise SystemExit('Failed: ' + ', '.join(failures)) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-glyph-compositing.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-glyph-compositing.c new file mode 100644 index 000000000000..f8b1e27c7f79 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-glyph-compositing.c @@ -0,0 +1,76 @@ +/* Exercise the production renderer with a known solid/antialiased glyph. + * No Wayland connection is needed: an EGL pbuffer provides the framebuffer. */ +#define main digitalrain_program_main +#ifndef DIGITALRAIN_SOURCE +#define DIGITALRAIN_SOURCE "../digitalrain.c" +#endif +#include DIGITALRAIN_SOURCE +#undef main +#include <assert.h> + +static void draw_glyph(unsigned char coverage, float fade, unsigned char pixel[4]) { + glBindTexture(GL_TEXTURE_2D, S.atlas.texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, &coverage); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + S.fade_opacity = fade; + render_frame(); + glReadPixels(8, 8, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + assert(glGetError() == GL_NO_ERROR); +} + +int main(void) { + EGLDisplay display = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, + EGL_DEFAULT_DISPLAY, NULL); + assert(display != EGL_NO_DISPLAY && eglInitialize(display, NULL, NULL)); + const EGLint config_attrs[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE}; + EGLConfig config; + EGLint count; + assert(eglChooseConfig(display, config_attrs, &config, 1, &count) && count == 1); + const EGLint context_attrs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attrs); + const EGLint surface_attrs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + EGLSurface surface = eglCreatePbufferSurface(display, config, surface_attrs); + assert(eglMakeCurrent(display, surface, surface, context)); + assert(render_init_shaders()); + S.surface_width = S.surface_height = 16; + render_set_projection(16, 16); + S.num_depth_layers = 1; + render_cell_t cell = {.codepoint = 'A', .age = 5}; + render_column_t column = {.active = true, .trail_len = 10, .color = {1, .5, .25}}; + S.layers[0] = (depth_layer_t){.cols = 1, .rows = 1, .cell_w = 16, + .cell_h = 16, .font_scale = 1, .opacity = .2, + .cells = &cell, .columns = &column}; + S.atlas.glyph_count = 1; + S.atlas.glyphs[0] = (glyph_info_t){.codepoint = 'A', .u1 = 1, .v1 = 1, + .width = 16, .height = 16, .bearing_y = 16}; + glGenTextures(1, &S.atlas.texture); + glBindTexture(GL_TEXTURE_2D, S.atlas.texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + unsigned char pixel[4]; + draw_glyph(255, 1, pixel); + printf("Dim far-layer trail RGBA: %u %u %u %u\n", pixel[0], pixel[1], pixel[2], pixel[3]); + fflush(stdout); + assert(pixel[3] == 255 && pixel[0] > 0 && pixel[0] < 20); + /* The dim stroke must cover a white application pixel, not blend with it. */ + glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); + render_frame(); glReadPixels(8, 8, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + assert(pixel[0] < 20 && pixel[1] < 20 && pixel[2] < 20); + draw_glyph(128, 1, pixel); + assert(pixel[3] >= 127 && pixel[3] <= 129); /* antialiasing remains */ + draw_glyph(255, .5, pixel); + assert(pixel[3] >= 127 && pixel[3] <= 129); /* phase fades remain */ + draw_glyph(0, 1, pixel); + assert(pixel[3] == 0); /* empty background remains transparent */ + glDeleteTextures(1, &S.atlas.texture); + glDeleteBuffers(1, &S.vbo); glDeleteProgram(S.shader_program); free(S.vertex_buf); + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(display, surface); eglDestroyContext(display, context); eglTerminate(display); + puts("Glyph coverage, dimming, antialiasing, fade and transparent background: PASS"); + return 0; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-hide-desktop.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-hide-desktop.py new file mode 100755 index 000000000000..179c19855c53 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-hide-desktop.py @@ -0,0 +1,305 @@ +"""Pixel/lifecycle regression in a private three-output Wayfire session. + +Run as a regular user: python3 tests/test-hide-desktop.py [--existing-tests] +Needs built files/, Wayfire, grim, wlr-randr, swaybg, pkg-config, C++, and Pillow. +No installed plugins, desktop settings or real display sockets are modified. +""" +import argparse +import io +import json +import os +from pathlib import Path +import shlex +import shutil +import signal +import socket +import struct +import subprocess +import tempfile +import time + +from PIL import Image + +parser = argparse.ArgumentParser() +parser.add_argument('--existing-tests', action='store_true') +parser.add_argument('--upgrade-from', type=Path, help='Old helper library to load before the candidate') +args = parser.parse_args() +assert os.geteuid() != 0, 'Run as a regular user' +package = Path(__file__).resolve().parents[1] +runtime = Path(tempfile.mkdtemp(prefix='digitalrain-desktop-test-')) +children = [] +connections = [] +env = os.environ.copy() +for key in ['WAYLAND_DISPLAY', 'WAYFIRE_SOCKET', '_WAYFIRE_SOCKET', 'DISPLAY', + 'WAYFIRE_DEFAULT_CONFIG_BACKEND', 'WAYFIRE_CONFIG_FILE', 'LD_LIBRARY_PATH']: + env.pop(key, None) +env.update(XDG_RUNTIME_DIR=str(runtime), WLR_BACKENDS='headless', + WLR_HEADLESS_OUTPUTS='3', WLR_RENDERER='gles2', + WLR_RENDER_DRM_DEVICE=env.get('WLR_RENDER_DRM_DEVICE', '/dev/dri/renderD128'), + WAYFIRE_PLUGIN_PATH=f'{runtime}:{package}', + WAYFIRE_PLUGIN_XML_PATH=f'{runtime}:{package}') + + +def spawn(command): + with (runtime / f'child-{len(children)}.log').open('w') as log: + process = subprocess.Popen(command, env=env, stdout=log, stderr=log) + children.append(process) + return process + + +def until(check, timeout=6): + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if check(): + return + time.sleep(.05) + raise AssertionError('Timed out waiting for expected state') + + +def read(sock, count): + result = b'' + while len(result) < count: + part = sock.recv(count - len(result)) + if not part: + raise RuntimeError('IPC disconnected') + result += part + return result + + +def request(sock, method, data=None): + payload = json.dumps(dict(method=method, data=data or {})).encode() + sock.sendall(struct.pack('=I', len(payload)) + payload) + return json.loads(read(sock, struct.unpack('=I', read(sock, 4))[0])) + + +def connect(): + sock = socket.socket(socket.AF_UNIX) + sock.settimeout(3) + sock.connect(env['WAYFIRE_SOCKET']) + connections.append(sock) + return sock + + +def ipc(method, data=None): + with connect() as sock: + return request(sock, method, data) + + +def hold(output, fade_ms=0): + sock = connect() + assert request(sock, 'digitalrain/hide_desktop', dict(output=output, fade_ms=fade_ms)) == {'result': 'ok'} + return sock + + +def shot(output='HEADLESS-1'): + png = subprocess.check_output(['grim', '-o', output, '-'], env=env) + return Image.open(io.BytesIO(png)).convert('RGB') + + +def pixels(color, output='HEADLESS-1'): + return sum(p == color for p in shot(output).get_flattened_data()) + + +def solid(key, layer, output='HEADLESS-1', close=False, x=0, y=0, w=100, h=80, + r=0.0, g=0.0, b=1.0): + data = dict(key=key, output=output, layer=layer, x=x, y=y, w=w, h=h, r=r, g=g, b=b) + if close: + data['close'] = True + assert ipc('test/solid', data) == {'result': 'ok'} + + +def rain(output='HEADLESS-1', hide=True, transparent=True, timeout=0, black_timeout=0, fade_frames=6): + cmd = [str(package / 'digitalrain'), '--render', '--output', output, + '--color', 'green', '--fade-frames', str(fade_frames), '--transparent-timeout', str(timeout), + '--black-rain-timeout', str(black_timeout)] + if transparent: + cmd += ['--transparent'] + if hide: + cmd += ['--hide-desktop'] + return spawn(cmd) + + +def dismiss(process): + process.send_signal(signal.SIGUSR1) + assert process.wait(timeout=4) == 0 + + +try: + flags = shlex.split(subprocess.check_output( + ['pkg-config', '--cflags', '--libs', 'wayfire', 'wf-utils'], text=True)) + subprocess.run(['c++', '-std=c++17', '-fPIC', '-shared', '-DWAYFIRE_PLUGIN', + '-DWLR_USE_UNSTABLE', str(package / 'tests/desktop-test.cpp'), + '-o', str(runtime / 'libdesktop-test.so'), *flags], check=True) + (runtime / 'desktop-test.xml').write_text( + '<wayfire><plugin name="desktop-test"><_short>Test</_short></plugin></wayfire>') + if args.upgrade_from: + shutil.copyfile(args.upgrade_from, runtime / 'libdigitalrain-desktop.so') + config = runtime / 'wayfire.ini' + plugins = 'ipc ipc-rules digitalrain-desktop desktop-test' + + def configure(enabled=True): + config.write_text('[core]\nplugins = ' + (plugins if enabled else plugins.replace('digitalrain-desktop ', '')) + + '\nxwayland = false\nvwidth = 1\nvheight = 1\n' + '[workarounds]\nenable_so_unloading = false\n') + + configure() + compositor = spawn(['wayfire', '-c', str(config)]) + until(lambda: list(runtime.glob('wayfire-*.socket')) and + [p for p in runtime.glob('wayland-*') if not p.name.endswith('.lock')]) + env['WAYFIRE_SOCKET'] = str(next(runtime.glob('wayfire-*.socket'))) + env['WAYLAND_DISPLAY'] = next(p.name for p in runtime.glob('wayland-*') if not p.name.endswith('.lock')) + if args.upgrade_from: + assert 'digitalrain/show_desktop' not in ipc('list-methods')['methods'] + (runtime / 'libdigitalrain-desktop.so').unlink() + ipc('wayfire/reload-plugins') + until(lambda: 'digitalrain/show_desktop' in ipc('list-methods')['methods']) + print('Upgrade from resident old helper without restarting Wayfire: PASS', flush=True) + assert {o['name'] for o in ipc('window-rules/list-outputs')} == { + 'HEADLESS-1', 'HEADLESS-2', 'HEADLESS-3'} + cmd = ['wlr-randr'] + for i in range(1, 4): + cmd += ['--output', f'HEADLESS-{i}', '--custom-mode', '640x480', '--pos', f'{(i-1)*640},0'] + subprocess.run(cmd, env=env, check=True) + background = spawn(['swaybg', '-c', 'cc0000']) + red, blue = (204, 0, 0), (0, 0, 255) + until(lambda: pixels(red) > 300000) + for index, layer in enumerate([1, 2, 3, 4, 5, 8]): + solid(str(layer), layer, x=index * 100, y=100) + solid('second', 2, output='HEADLESS-2', y=100) + until(lambda: pixels(blue) == 48000) + baseline = shot().tobytes() + + a = hold('HEADLESS-1') + until(lambda: pixels(blue) == 0) + assert pixels(red) == 640 * 480, 'wallpaper must remain completely visible' + assert pixels(blue, 'HEADLESS-2') == 8000, 'other output changed' + assert request(a, 'digitalrain/hide_desktop', {'output': 'HEADLESS-1'}) == {'result': 'ok'} + b = hold('HEADLESS-1') + a.close() + time.sleep(.1) + assert pixels(blue) == 0, 'one renderer must not release another renderer' + solid('new', 3, y=300) + assert pixels(blue) == 0, 'new windows must also stay hidden' + solid('lock', 7, y=250, r=1.0, b=1.0) + until(lambda: pixels((255, 0, 255)) == 8000) + solid('lock', 7, close=True) + b.close() + until(lambda: pixels(blue) == 56000) + solid('new', 3, close=True) + until(lambda: shot().tobytes() == baseline) + print('All app/panel layers hidden; wallpaper/lock retained; outputs isolated; balanced lifetime: PASS', flush=True) + + a = hold('HEADLESS-1', fade_ms=2000) + time.sleep(.6) + fading = shot() + for index in range(6): + r, g, b = fading.getpixel((index * 100 + 50, 140)) + assert 20 < r < 180 and g == 0 and 30 < b < 240, (index, (r, g, b)) + assert fading.getpixel((600, 400)) == red, 'wallpaper must not fade' + # Resume reverses from the current opacity without an abrupt jump. + before = fading.getpixel((50, 140)) + assert request(a, 'digitalrain/show_desktop') == {'result': 'ok'} + immediate = shot().getpixel((50, 140)) + assert abs(immediate[2] - before[2]) < 20, (before, immediate) + time.sleep(.4) + recovering = shot().getpixel((50, 140)) + assert before[2] < recovering[2] < 250, (before, recovering) + until(lambda: shot().tobytes() == baseline) + a.close() + print('All six desktop layers fade smoothly; wake reverses a partial fade without jumping: PASS', flush=True) + + a = hold('HEADLESS-1', fade_ms=1000) + until(lambda: pixels(red) == 640 * 480) + assert request(a, 'digitalrain/show_desktop') == {'result': 'ok'} + time.sleep(.35) + fading = shot() + for index in range(6): + r, g, b = fading.getpixel((index * 100 + 50, 140)) + assert 20 < r < 180 and g == 0 and 30 < b < 240, (index, (r, g, b)) + assert pixels(blue, 'HEADLESS-2') == 8000 + # A repeated wake must not restart the animation. + assert request(a, 'digitalrain/show_desktop') == {'result': 'ok'} + until(lambda: shot().tobytes() == baseline) + a.close() + print('All six hidden desktop layers fade back in; repeated wake is idempotent: PASS', flush=True) + + p = rain(hide=False) + time.sleep(.7) + assert pixels(blue) > 20000, 'default behavior must preserve apps' + dismiss(p) + until(lambda: shot().tobytes() == baseline) + p = rain(fade_frames=30) + time.sleep(.4) + fading = list(shot().get_flattened_data()) + assert sum(20 < r < 180 and b > g + 30 for r, g, b in fading) > 10000 + assert any(g > 0 for r, g, b in fading), 'rain must fade in at the same time' + time.sleep(.8) + until(lambda: pixels(blue) == 0) + assert pixels(red) > 150000, 'transparent stage must show wallpaper' + assert any(g > 0 for r, g, b in shot().get_flattened_data()), 'rain missing' + p.send_signal(signal.SIGUSR1) + time.sleep(.3) + recovering = list(shot().get_flattened_data()) + assert sum(20 < r < 180 and b > g + 30 for r, g, b in recovering) > 10000 + assert sum(pixel == blue for pixel in recovering) < 1000, 'resume must fade, not jump' + assert p.wait(timeout=4) == 0 + until(lambda: shot().tobytes() == baseline) + p = rain() + until(lambda: pixels(blue) == 0) + p.kill() + p.wait(timeout=3) + until(lambda: shot().tobytes() == baseline) + print('Default off; wallpaper-only rain; desktop fade-in with rain fade-out; SIGKILL recovery: PASS', flush=True) + + p = rain(timeout=1, black_timeout=.6, fade_frames=30) + until(lambda: pixels(blue) == 0) + until(lambda: shot().getbbox() is None) + assert p.poll() is None + p.send_signal(signal.SIGUSR1) + time.sleep(.4) + recovering = list(shot().get_flattened_data()) + assert any(r > 10 and b > 10 and g == 0 for r, g, b in recovering), 'desktop must fade in from black' + assert p.wait(timeout=4) == 0 + until(lambda: shot().tobytes() == baseline) + print('Wallpaper rain -> black rain -> solid black -> desktop fade-in: PASS', flush=True) + + a = hold('HEADLESS-1') + until(lambda: pixels(blue) == 0) + configure(False) + until(lambda: 'digitalrain/hide_desktop' not in ipc('list-methods')['methods']) + until(lambda: shot().tobytes() == baseline) + a.close() + p = rain() + time.sleep(.7) + assert p.poll() is None and pixels(blue) > 20000, 'missing plugin must fall back' + dismiss(p) + until(lambda: shot().tobytes() == baseline) + configure() + until(lambda: 'digitalrain/hide_desktop' in ipc('list-methods')['methods']) + a = hold('HEADLESS-2') + subprocess.run(['wlr-randr', '--output', 'HEADLESS-2', '--off'], env=env, check=True) + a.close() + subprocess.run(['wlr-randr', '--output', 'HEADLESS-2', '--on'], env=env, check=True) + assert compositor.poll() is None + assert shot().tobytes() == baseline + print('Plugin unload/missing plugin and output removal recover safely: PASS', flush=True) + + for key in ['1', '2', '3', '4', '5', '8', 'second']: + solid(key, 2, close=True) + background.terminate() + background.wait(timeout=3) + if args.existing_tests: + subprocess.run(['python3', str(package / 'tests/test-black-rain-timeout.py'), + str(package / 'digitalrain')], env=env, check=True) +finally: + for sock in connections: + sock.close() + for child in reversed(children): + if child.poll() is None: + child.terminate() + try: + child.wait(timeout=5) + except subprocess.TimeoutExpired: + child.kill() + child.wait() + print(f'Test artifacts: {runtime}', flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-inhibition.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-inhibition.c new file mode 100644 index 000000000000..d9985245d7db --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-inhibition.c @@ -0,0 +1,69 @@ +#define main digitalrain_program_main +#ifndef DIGITALRAIN_SOURCE +#define DIGITALRAIN_SOURCE "../digitalrain.c" +#endif +#include DIGITALRAIN_SOURCE +#undef main +#include <assert.h> + +int main(void) { + struct json_object *response = json_tokener_parse("{\"outputs\":[\"DP-2\",\"HDMI-A-1\"]}"); + assert(mgr_response_inhibits_output(response, "DP-2")); + assert(mgr_response_inhibits_output(response, "HDMI-A-1")); + assert(!mgr_response_inhibits_output(response, "DP-1")); + assert(!mgr_response_inhibits_output(response, "DP")); + json_object_put(response); + const char *invalid[] = {"{}", "{\"error\":\"unavailable\"}", + "{\"outputs\":null}", "{\"outputs\":\"DP-2\"}", + "{\"outputs\":[false,12,null,{}]}", "{\"outputs\":[]}"}; + for (size_t i = 0; i < sizeof(invalid) / sizeof(invalid[0]); i++) { + response = json_tokener_parse(invalid[i]); + assert(!mgr_response_inhibits_output(response, "DP-2")); + json_object_put(response); + } + assert(!mgr_response_inhibits_output(NULL, "DP-2")); + pthread_mutex_init(&G.lock, NULL); + /* Typing on DP-3 after brief pauses must not postpone idle DP-1/DP-2. + * Their 300-second countdowns have already expired in this scenario. */ + double idle_since = mgr_monotime() - 310.0; + G.output_count = 3; + mgr_init_output(&G.outputs[0], "DP-1", idle_since); + mgr_init_output(&G.outputs[1], "DP-2", idle_since); + mgr_init_output(&G.outputs[2], "DP-3", idle_since); + snprintf(G.focused_output, sizeof(G.focused_output), "DP-3"); + for (int i = 0; i < 5; ++i) { + mgr_idle_handle_idled(NULL, NULL); + assert(G.seat_idle); + double resumed_at = mgr_monotime(); + mgr_idle_handle_resumed(NULL, NULL); + assert(!G.seat_idle); + assert(G.outputs[0].last_input == idle_since); + assert(G.outputs[1].last_input == idle_since); + assert(G.outputs[2].last_input >= resumed_at); + assert(mgr_monotime() - G.outputs[0].last_input >= 300.0); + } + mgr_update_focused_output("DP-2"); + double left_dp3 = G.outputs[2].last_input; + mgr_idle_handle_idled(NULL, NULL); + mgr_idle_handle_resumed(NULL, NULL); + assert(G.outputs[0].last_input == idle_since); + assert(G.outputs[2].last_input == left_dp3); + assert(G.outputs[1].last_input > idle_since); + + /* A temporarily unknown focus cannot justify resetting all outputs. */ + G.focused_output[0] = '\0'; + mgr_idle_handle_resumed(NULL, NULL); + assert(G.outputs[0].last_input == idle_since); + assert(G.outputs[2].last_input == left_dp3); + + G.manual_activation = 1; + G.last_blank_time = mgr_monotime(); + mgr_idle_handle_resumed(NULL, NULL); + assert(G.manual_activation); /* spurious early resume must not undo explicit activation */ + G.last_blank_time -= 4; + mgr_idle_handle_resumed(NULL, NULL); + assert(!G.manual_activation); + pthread_mutex_destroy(&G.lock); + puts("Passed independent output idle timers, focus changes, inhibition filtering, and explicit activation resume checks."); + return 0; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-live-config-alerts.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-live-config-alerts.py new file mode 100755 index 000000000000..77878d5f113c --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-live-config-alerts.py @@ -0,0 +1,80 @@ +"""Production manager config/alert regression in a private, compositor-free runtime.""" +import json, os, pathlib, signal, socket, subprocess, sys, tempfile, time, tomllib +binary = str(pathlib.Path(sys.argv[1]).resolve()) +def until(fn, timeout=5): + deadline = time.monotonic()+timeout + while time.monotonic()<deadline: + if fn(): return + time.sleep(.04) + raise AssertionError('timed out') +with tempfile.TemporaryDirectory(prefix='digitalrain-live-') as directory: + root=pathlib.Path(directory); cfg=root/'config.toml' + base=f'''schema_version = 1 +[global] +TIMEOUT = 3600 +FOCUSED_TIMEOUT = 3600 +CHECK_INTERVAL = 1 +LOG_FILE = "{root}/manager.log" +[matrix] +COLOR = '#00ff00' # retain this comment +MIN_SPEED = 0.01 +[audio] +ENABLED = false +''' + cfg.write_text(base) + env=os.environ.copy();env.update(XDG_RUNTIME_DIR=directory, DIGITALRAIN_CONFIG=str(cfg), WAYLAND_DISPLAY='absent-test-display', WAYFIRE_SOCKET=str(root/'absent-ipc'), _WAYFIRE_SOCKET=str(root/'absent-ipc')) + with (root/'launch.log').open('w') as log: + manager=subprocess.Popen([binary], env=env, stdout=log, stderr=log) + def api(method, **arguments): + with socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET) as conn: + conn.settimeout(4);conn.connect(str(root/'digitalrain/control.sock')) + conn.send(json.dumps(dict(version=1,method=method,**arguments)).encode()) + raw=conn.recv(16384);assert raw;return json.loads(raw) + try: + until(lambda:(root/'digitalrain/control.sock').exists()) + assert api('schema')['matrix.MIN_SPEED']['value']==.01 + assert api('settings',settings={'matrix.COLOR':'#123456','matrix.FONT_SIZE':18},save=True).get('error') is None + parsed=tomllib.loads(cfg.read_text());assert parsed['matrix']['COLOR']=='#123456' and parsed['matrix']['FONT_SIZE']==18 + assert '# retain this comment' in cfg.read_text() + assert 'error' not in api('settings',settings={'audio.ENABLED':0,'audio.SPEED_AMOUNT':.5},save=True) + parsed=tomllib.loads(cfg.read_text());assert parsed['audio']['ENABLED'] is False and parsed['audio']['SPEED_AMOUNT']==.5 + assert 'error' not in api('settings', settings={'matrix.FONT_SIZE':22}) + assert 'error' not in api('reload') + assert api('schema')['matrix.FONT_SIZE']['value']==18 + revision=api('status')['settings_revision'];good=cfg.read_text() + cfg.write_text('[matrix]\nFONT_SIZE = "broken"\n');until(lambda:bool(api('status')['config_error'])) + assert api('schema')['matrix.FONT_SIZE']['value']==18 + cfg.write_text(good);until(lambda:not api('status')['config_error']) + replacement=root/'next';replacement.write_text(base.replace("'#00ff00'", '"cyan"'));replacement.replace(cfg) + until(lambda:api('schema')['matrix.COLOR']['value']=='cyan') + assert api('schema')['matrix.FONT_SIZE']['value']==12 + # Missing-parent, dotted keys, inline tables, escaped/multiline string source spans. + for document in [f'global.LOG_FILE="{root}/manager.log"\nmatrix.COLOR="green"\n', f'[global]\nLOG_FILE="{root}/manager.log"\nmatrix_dummy=1\n']: + if 'dummy' in document: continue + cfg.write_text(document);assert 'error' not in api('reload') + assert 'error' not in api('settings',settings={'matrix.FONT_SIZE':16},save=True), cfg.read_text() + assert tomllib.loads(cfg.read_text())['matrix']['FONT_SIZE']==16 + cfg.write_text(f'global = {{ LOG_FILE="{root}/manager.log" }}\nmatrix = {{ COLOR = """green""" }}\n') + assert 'error' not in api('reload') + assert 'error' not in api('settings',settings={'matrix.COLOR':'blue','matrix.FONT_SIZE':20},save=True), cfg.read_text() + assert tomllib.loads(cfg.read_text())['matrix']=={'COLOR':'blue','FONT_SIZE':20} + assert 'error' in api('settings',settings={'matrix.MIN_SPEED':-1}) + assert 'error' in api('settings',settings={'matrix.MIN_SPEED':10,'matrix.MAX_SPEED':1}) + assert 'error' in api('alert',source='test',id='x',severity='error',seconds=0) + assert api('alert',source='test',id='x',severity='error',seconds=1)['accepted'] + assert api('status')['alert_count']==1 + api('alert',source='test',id='x',severity='warning',seconds=1) + assert api('list_alerts')['total']==1 + until(lambda:api('list_alerts')['total']==0) + for i in range(64): assert api('alert',source='test',id=str(i),severity='info',seconds=60)['accepted'] + assert 'error' in api('alert',source='test',id='overflow',severity='error',seconds=60) + assert len(api('list_alerts')['alerts'])==16 and api('list_alerts')['next_offset']==16 + assert len(api('list_alerts',offset=48)['alerts'])==16 + api('clear_alert',source='test',id='0');api('clear_alert',source='test',id='0') + assert api('list_alerts')['total']==63 + assert api('status')['audio']['state']=='disabled' + children=pathlib.Path(f'/proc/{manager.pid}/task/{manager.pid}/children').read_text() + assert not children.strip(), children + print('TOML validation, save preservation, atomic replacement, reset defaults, alert limits/expiry, disabled audio: PASS') + finally: + manager.terminate();manager.wait(8) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-live-render.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-live-render.c new file mode 100644 index 000000000000..2909fc4501be --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-live-render.c @@ -0,0 +1,68 @@ +#define main digitalrain_program_main +#include "../digitalrain.c" +#undef main +#include <assert.h> +static bool fail_allocation; +void *__real_calloc(size_t n, size_t size); +void *__wrap_calloc(size_t n, size_t size) { + if (fail_allocation) { fail_allocation = false; return NULL; } + return __real_calloc(n, size); +} +int main(void) { + EGLDisplay display = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, + EGL_DEFAULT_DISPLAY, NULL); + assert(display != EGL_NO_DISPLAY && eglInitialize(display, NULL, NULL)); + const EGLint config_attrs[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE}; + EGLConfig config; + EGLint count; + assert(eglChooseConfig(display, config_attrs, &config, 1, &count) && count == 1); + const EGLint context_attrs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attrs); + const EGLint surface_attrs[] = {EGL_WIDTH, 128, EGL_HEIGHT, 128, EGL_NONE}; + EGLSurface surface = eglCreatePbufferSurface(display, config, surface_attrs); + assert(eglMakeCurrent(display, surface, surface, context)); + assert(render_init_shaders()); + S.surface_width = S.surface_height = 128; + render_set_projection(128, 128); + + assert(render_init_freetype(16)); + dr_config_packet p = {.magic=DR_LIVE_MAGIC, .kind=DR_CONFIG, .revision=1}; + cf_defaults(&p.config); p.config.matrix_depth_layers=2; + S.phase=PHASE_RUNNING; S.phase_frame=17; S.fade_frames=30; S.render_start_time=42; S.black_start_time=43; + assert(render_apply_config(&p, false)); + GLuint old_texture=S.atlas.texture; render_column_t *old_columns=S.layers[0].columns; + p.revision++; p.config.matrix_font_size=20; + fail_allocation=true; + assert(!render_apply_config(&p, false)); + assert(S.atlas.texture==old_texture && S.layers[0].columns==old_columns); + assert(S.applied_revision==1 && S.live_error && S.phase==PHASE_RUNNING && S.phase_frame==17); + assert(S.render_start_time==42 && S.black_start_time==43); + assert(render_apply_config(&p, false)); + assert(S.font_size==20 && S.applied_revision==2 && !S.live_error); + assert(S.phase==PHASE_RUNNING && S.render_start_time==42 && S.black_start_time==43); + dr_effect_packet effects={.magic=DR_LIVE_MAGIC,.kind=DR_EFFECTS,.count=2}; + snprintf(effects.effects[0].output,64,"top"); + snprintf(effects.effects[1].output,64,"bottom"); + effects.effects[0].width=effects.effects[1].width=128; + effects.effects[0].height=effects.effects[1].height=64; + effects.effects[1].y=64; + effects.effects[0].until=render_monotime()+60;effects.effects[0].color[0]=1; + effects.effects[1].until=render_monotime()+60;effects.effects[1].color[2]=1; + assert(dr_effect_valid(&effects)); render_apply_effects(&effects); + S.effect_mix[0]=S.effect_mix[1]=1; + float brightness; rgb_t top=render_column_color(32,32,(rgb_t){0,1,0},&brightness); + rgb_t bottom=render_column_color(32,96,(rgb_t){0,1,0},&brightness); + assert(top.r==1 && top.b==0 && bottom.r==0 && bottom.b==1); + dr_effect_packet clear={.magic=DR_LIVE_MAGIC,.kind=DR_EFFECTS};render_apply_effects(&clear); + assert(S.effects.effects[0].color[0]==1 && S.effects.effects[0].until==0); + effects.effects[0].energy=NAN; assert(!dr_effect_valid(&effects)); + render_layers_free(S.layers);glDeleteTextures(1,&S.atlas.texture); + FT_Done_Face(S.ft_face);FT_Done_FreeType(S.ft_library); + glDeleteBuffers(1,&S.vbo);glDeleteProgram(S.shader_program);free(S.vertex_buf); + eglMakeCurrent(display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT); + eglDestroySurface(display,surface);eglDestroyContext(display,context);eglTerminate(display); + puts("Live resource rollback, retry, phase clocks and vertical monitor effects: PASS"); + return 0; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-manager-lock.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-manager-lock.py new file mode 100755 index 000000000000..60fbca18b80c --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-manager-lock.py @@ -0,0 +1,150 @@ +"""Regression for manager ownership. Run with /path/to/built/digitalrain. + +Uses private runtime directories and deliberately absent Wayland/IPC sockets; +it never connects to the real desktop. Exercises the production binary. +""" +import os +from pathlib import Path +import signal +import subprocess +import sys +import tempfile +import time + + +binary = str(Path(sys.argv[1]).resolve()) +processes = [] + + +def until(check, timeout=5): + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if check(): + return + time.sleep(.025) + raise AssertionError('Timed out waiting for expected state') + + +def setup(base): + base.mkdir(mode=0o700) + config = base / 'config' + config.write_text('[global]\nTIMEOUT=3600\nFOCUSED_TIMEOUT=3600\n' + 'CHECK_INTERVAL=1\nINHIBIT_VIDEO=false\n' + f'LOG_FILE="{base}/manager.log"\n') + env = os.environ.copy() + env.update(XDG_RUNTIME_DIR=str(base), WAYLAND_DISPLAY='absent-test-display', + WAYFIRE_SOCKET=str(base / 'absent-ipc'), + _WAYFIRE_SOCKET=str(base / 'absent-ipc'), DIGITALRAIN_CONFIG=str(config)) + return env + + +def launch(env): + log = Path(env['XDG_RUNTIME_DIR']) / f'launch-{len(processes)}.log' + with log.open('w') as stream: + process = subprocess.Popen([binary], env=env, stdout=stream, stderr=stream) + processes.append(process) + return process + + +def start(env): + process = launch(env) + pidfile = Path(env['XDG_RUNTIME_DIR']) / 'digitalrain/pid' + until(lambda: pidfile.exists() and pidfile.read_text().strip() == str(process.pid)) + logfile = Path(env['XDG_RUNTIME_DIR']) / 'manager.log' + until(lambda: 'Started' in logfile.read_text()) + assert process.poll() is None + return process + + +def stop(process): + process.terminate() + assert process.wait(timeout=5) == 0 + + +def command(env, *args): + return subprocess.run([binary, *args], env=env, capture_output=True, + text=True, timeout=12) + + +def lock_descriptors(pid): + paths = Path(f'/proc/{pid}/fd').iterdir() + result = [] + for path in paths: + try: + if os.readlink(path).endswith('/manager.lock'): + result.append(path) + except FileNotFoundError: + pass + return result + + +with tempfile.TemporaryDirectory(prefix='digitalrain-lock-test-') as temporary: + base = Path(temporary) + env = setup(base / 'first') + state = base / 'first/digitalrain' + logfile = base / 'first/manager.log' + try: + manager = start(env) + inode = (state / 'manager.lock').stat().st_ino + assert len(lock_descriptors(manager.pid)) == 1 + marker = b'KEEP EXISTING MANAGER LOG\n' + with logfile.open('r+b') as stream: + stream.write(marker) + duplicate = command(env) + assert duplicate.returncode == 0 and 'already running' in duplicate.stderr, duplicate + assert (state / 'pid').read_text().strip() == str(manager.pid) + assert logfile.read_bytes().startswith(marker), 'duplicate truncated the log' + assert manager.poll() is None + assert (state / 'manager.lock').stat().st_ino == inode + print('Duplicate exits cleanly without overwriting PID/log or disturbing owner: PASS', flush=True) + + help_result = command(env, '--render', '--help') + assert help_result.returncode == 0 and 'already running' not in help_result.stderr + assert command(env, '--activate').returncode == 0 + manager.send_signal(signal.SIGUSR1) + until(lambda: 'All outputs activated' in logfile.read_text()) + assert command(env, '--lock').returncode != 0, 'missing compositor must not report a lock' + assert (state / 'control.sock').stat().st_mode & 0o777 == 0o600 + assert command(env, '--status').returncode == 0 + print('Renderer/IPC bypass ownership check; missing compositor cannot confirm lock: PASS', flush=True) + + manager.kill() + assert manager.wait(timeout=5) == -signal.SIGKILL + assert (state / 'pid').read_text().strip() == str(manager.pid) + manager = start(env) + assert (state / 'manager.lock').stat().st_ino == inode + stop(manager) + assert not (state / 'pid').exists() + assert (state / 'manager.lock').stat().st_ino == inode + print('Crash recovery with stale PID/socket and clean shutdown: PASS', flush=True) + + contenders = [launch(env) for _ in range(8)] + until(lambda: sum(p.poll() is None for p in contenders) == 1) + winner = next(p for p in contenders if p.poll() is None) + until(lambda: (state / 'pid').exists() and + (state / 'pid').read_text().strip() == str(winner.pid)) + assert all(p.poll() == 0 for p in contenders if p is not winner) + assert command(env).returncode == 0 + assert (state / 'pid').read_text().strip() == str(winner.pid) + second = start(setup(base / 'second')) + assert winner.poll() is None + stop(second) + stop(winner) + print('Eight simultaneous starts elect one owner; separate runtime directories coexist: PASS', flush=True) + + invalid = setup(base / 'invalid') + (base / 'invalid/digitalrain/manager.lock').mkdir(parents=True) + result = command(invalid) + assert result.returncode != 0 and 'cannot open manager lock' in result.stderr + assert not (base / 'invalid/digitalrain/pid').exists() + assert not (base / 'invalid/manager.log').exists() + print('Lock errors prevent unlocked startup before touching PID/log: PASS', flush=True) + finally: + for process in reversed(processes): + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=5) + except subprocess.TimeoutExpired: + process.kill() + process.wait() diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-native-lock.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-native-lock.py new file mode 100755 index 000000000000..2c874584d611 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-native-lock.py @@ -0,0 +1,225 @@ +"""Native locker integration in a fresh headless Wayfire, never the desktop. +Run unprivileged with --auth-helper pointing to a test-service PAM worker. +Production binaries contain no test password or authentication bypass. +""" +import argparse, io, json, os, pathlib, shlex, shutil, signal, socket, struct, subprocess, tempfile, time +from PIL import Image +p=argparse.ArgumentParser();p.add_argument('--auth-helper',required=True);p.add_argument('--binary-dir',type=pathlib.Path);a=p.parse_args() +assert os.getuid()!=0 +pkg=pathlib.Path(__file__).resolve().parents[1] +binaries=a.binary_dir or pkg +runtime=pathlib.Path(tempfile.mkdtemp(prefix='digitalrain-lock-test-')) +children=[] +(runtime/'bin').mkdir() +env=os.environ.copy() +for k in ['WAYLAND_DISPLAY','WAYFIRE_SOCKET','_WAYFIRE_SOCKET','DISPLAY','WAYFIRE_CONFIG_FILE','WAYFIRE_DEFAULT_CONFIG_BACKEND','LD_LIBRARY_PATH']:env.pop(k,None) +env.update(XDG_CONFIG_HOME=str(runtime/'config'),XDG_STATE_HOME=str(runtime/'state'),XDG_RUNTIME_DIR=str(runtime),WLR_BACKENDS='headless',WLR_HEADLESS_OUTPUTS='3',WLR_RENDERER='gles2',WLR_RENDER_DRM_DEVICE='/dev/dri/renderD128',WAYFIRE_PLUGIN_PATH=f'{runtime}:{pkg}',WAYFIRE_PLUGIN_XML_PATH=f'{runtime}:{pkg}') +def spawn(argv): + with (runtime/f'child-{len(children)}.log').open('w') as f:q=subprocess.Popen(argv,env=env,stdout=f,stderr=f) + children.append(q);return q +def until(fn,timeout=8): + end=time.monotonic()+timeout + while time.monotonic()<end: + try: + r=fn() + if r:return r + except (ConnectionError,FileNotFoundError):pass + time.sleep(.05) + raise AssertionError(f'timed out: {fn}') +def read(s,n): + b=b'' + while len(b)<n: + v=s.recv(n-len(b)) + if not v:raise ConnectionError() + b+=v + return b +def ipc(method,data=None): + with socket.socket(socket.AF_UNIX) as s: + s.settimeout(4);s.connect(env['WAYFIRE_SOCKET']);b=json.dumps(dict(method=method,data=data or {})).encode();s.sendall(struct.pack('=I',len(b))+b) + return json.loads(read(s,struct.unpack('=I',read(s,4))[0])) +def locker(method='status'): + with socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET) as s: + s.settimeout(4);s.connect(str(runtime/'digitalrain/lock.sock'));s.send(json.dumps(dict(method=method)).encode());return json.loads(s.recv(16384)) +def shot(output='VIRTUAL-1'): + return Image.open(io.BytesIO(subprocess.check_output(['grim','-o',output,'-'],env=env,timeout=4))).convert('RGB') +def type_password(text): + codes={'r':19,'a':30,'i':23,'n':49,'-':12,'t':20,'e':18,'s':31,'7':8,'x':45} + for c in text:ipc('test/key',dict(code=codes[c]));time.sleep(.02) + ipc('test/key',dict(code=28)) +try: + flags=shlex.split(subprocess.check_output(['pkg-config','--cflags','--libs','wayfire','wf-utils'],text=True)) + for name in ['lock-test','desktop-test']: + subprocess.run(['c++','-std=c++17','-fPIC','-shared','-DWAYFIRE_PLUGIN','-DWLR_USE_UNSTABLE',str(pkg/f'tests/{name}.cpp'),'-o',str(runtime/f'lib{name}.so'),*flags],check=True) + (runtime/f'{name}.xml').write_text(f'<wayfire><plugin name="{name}"><_short>Test</_short></plugin></wayfire>') + for name,src in [('digitalrain',binaries/'digitalrain'),('digitalrain-lock',binaries/'digitalrain-lock'),('digitalrain-auth',pathlib.Path(a.auth_helper))]:shutil.copy2(src,runtime/'bin'/name) + if (binaries/'digitalrain-audio').exists():shutil.copy2(binaries/'digitalrain-audio',runtime/'bin/digitalrain-audio') + conf=runtime/'wayfire.ini';conf.write_text('[core]\nplugins = ipc ipc-rules session-lock digitalrain-idle digitalrain-cursor digitalrain-power lock-test desktop-test\nxwayland = false\nstitch_enabled = true\nstitch_outputs = HEADLESS-1 HEADLESS-2 HEADLESS-3\nwindow_scope = virtual\n'+''.join(f'[output:HEADLESS-{i}]\nmode = 320x240@60\nposition = {(i-1)*320},0\n' for i in range(1,4))) + cfg=runtime/'rain.toml';cfg.write_text('[global]\nLOG_FILE = "'+str(runtime/'manager.log')+'"\nTIMEOUT = 999\nFOCUSED_TIMEOUT = 999\nCHECK_INTERVAL = 1\nINHIBIT_VIDEO = false\n[matrix]\nBLACK_RAIN_TIMEOUT = 2\nFONT_SIZE = 12\nDEPTH_LAYERS = 2\n') + env['DIGITALRAIN_CONFIG']=str(cfg) + compositor=spawn(['wayfire','-c',str(conf)]) + until(lambda:list(runtime.glob('wayfire-*.socket'))) + env['WAYFIRE_SOCKET']=str(next(runtime.glob('wayfire-*.socket'))) + env['WAYLAND_DISPLAY']=next(x.name for x in runtime.glob('wayland-*') if not x.name.endswith('.lock')) + until(lambda:len(ipc('window-rules/list-outputs'))==1 and ipc('window-rules/list-outputs')[0]['name']=='VIRTUAL-1') + ipc('digitalrain/hide_cursor') + bg=spawn(['swaybg','-c','cc0000']);until(lambda:(204,0,0) in shot().get_flattened_data()) + lock=spawn([str(runtime/'bin/digitalrain-lock')]) + until(lambda:locker().get('state')=='locked') + until(lambda:all(ipc('test/lock-state').values())) + time.sleep(.3);img=shot();img.save(runtime/'locked.png');assert (204,0,0) not in img.get_flattened_data() + duplicate=spawn([str(runtime/'bin/digitalrain-lock')]);assert duplicate.wait(4)!=0 + until(lambda:locker().get('phase')=='black');time.sleep(.2) + black=shot();black.save(runtime/'black.png');assert set(black.get_flattened_data())=={(0,0,0)} + before=pathlib.Path(f'/proc/{lock.pid}/stat').read_text().split();time.sleep(.7);after=pathlib.Path(f'/proc/{lock.pid}/stat').read_text().split() + assert sum(map(int,after[13:15]))-sum(map(int,before[13:15]))<=2 + locker('wake');lock.send_signal(signal.SIGUSR1);assert lock.poll() is None + type_password('x');time.sleep(.7);assert lock.poll() is None and locker()['state']=='locked' + print('Coverage, black idle, duplicate rejection, wake isolation and bad password: PASS',flush=True) + time.sleep(2);type_password('rain-test-7');assert lock.wait(8)==0 + until(lambda:not any(ipc('test/lock-state').values())) + print('Authenticated PAM unlock: PASS',flush=True) + def api(method, **arguments): + with socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET) as s: + s.settimeout(10);s.connect(str(runtime/'digitalrain/control.sock'));s.send(json.dumps(dict(version=1,method=method,**arguments)).encode());return json.loads(s.recv(16384)) + manager=spawn([str(runtime/'bin/digitalrain')]);until(lambda:(runtime/'digitalrain/control.sock').exists()) + assert len(api('status')['outputs'])==3 + assert api('schema')['matrix.FONT_SIZE']['minimum']==6 + assert 'error' in api('settings',settings={'matrix.FONT_SIZE':0}) + assert 'error' in api('settings',settings={'global.LOCK_CMD':'bad'}) + original=cfg.read_text();api('settings',settings={'matrix.FONT_SIZE':16});assert cfg.read_text()==original + api('settings',settings={'matrix.FONT_SIZE':14},save=True);assert 'FONT_SIZE = 14' in cfg.read_text() + inhibited=api('inhibit',output='HEADLESS-1',seconds=2,reason='test') + assert api('status')['inhibitors'][0]['id']==inhibited['id'] + api('uninhibit',id=inhibited['id']);assert not api('status')['inhibitors'] + api('black',output='HEADLESS-1');until(lambda:api('status')['outputs'][0]['phase']=='black') + api('wake',output='HEADLESS-1');until(lambda:all(not o['saver'] for o in api('status')['outputs'])) + before=cfg.read_text();api('preview',output='HEADLESS-2',seconds=1,settings={'matrix.COLOR':'blue'}) + until(lambda:all(not o['saver'] for o in api('status')['outputs']));assert cfg.read_text()==before + assert 'error' in api('unlock') + # Runtime changes retain renderer processes and per-output phase clocks. + api('settings',settings={'matrix.COLOR':'green','matrix.FADE_FRAMES':1,'matrix.BLACK_RAIN_TIMEOUT':0,'matrix.HEAD_WHITE_MIN':0,'matrix.HEAD_WHITE_MAX':0}) + api('activate',output='HEADLESS-1');api('activate',output='HEADLESS-3') + until(lambda:all(o['applied_revision']==api('status')['settings_revision'] for o in [o for o in api('status')['outputs'] if o['name']!='HEADLESS-2'])) + renderer_pids=pathlib.Path(f'/proc/{manager.pid}/task/{manager.pid}/children').read_text().split() + def dominant(region, channel): + image=shot().crop((region*320,0,(region+1)*320,240)) + sums=[sum(pixel[j] for pixel in image.get_flattened_data()) for j in range(3)] + return sums[channel]>max(sums[(channel+1)%3],sums[(channel+2)%3])*1.3 and sums[channel]>500 + until(lambda:dominant(0,1) and dominant(2,1)) + api('alert',source='build',id='failed',severity='error',seconds=20,output='HEADLESS-1') + until(lambda:dominant(0,0));assert dominant(2,1) + api('settings',settings={'matrix.COLOR':'blue','matrix.FONT_SIZE':20,'matrix.DEPTH_LAYERS':3}) + until(lambda:all(o['font_size']==20 and o['depth_layers']==3 for o in [o for o in api('status')['outputs'] if o['name']!='HEADLESS-2'])) + assert pathlib.Path(f'/proc/{manager.pid}/task/{manager.pid}/children').read_text().split()==renderer_pids + until(lambda:dominant(0,0) and dominant(2,2)) + api('alert',source='backup',id='warning',severity='warning',seconds=20,output='HEADLESS-1') + assert api('status')['alerts_by_output']['HEADLESS-1']['severity']=='error' + api('clear_alert',source='backup',id='warning');api('clear_alert',source='build',id='failed') + until(lambda:dominant(0,2)) + api('black',output='HEADLESS-1');until(lambda:api('status')['outputs'][0]['phase']=='black') + api('alert',source='build',id='black',severity='error',seconds=1,output='HEADLESS-1') + time.sleep(.3);assert set(shot().crop((0,0,320,240)).get_flattened_data())=={(0,0,0)} + api('wake');until(lambda:all(not o['saver'] for o in api('status')['outputs'])) + api('reload');until(lambda:api('status')['alert_count']==0) + print('Live colors/resources retain renderer PIDs; per-monitor alerts, priority, restoration and black policy: PASS',flush=True) + # A real private PipeWire helper starts/stops according to visible rain. + if (runtime/'bin/digitalrain-audio').exists(): + audio_server=spawn(['pipewire']);until(lambda:(runtime/'pipewire-0').exists()) + audio_policy=spawn(['dbus-run-session','--','wireplumber','--profile','policy']) + time.sleep(.8) + subprocess.run(['pw-cli','create-node','adapter','{ factory.name = support.null-audio-sink node.name = rain-test-source media.class = Audio/Sink object.linger = true audio.position = [ FL FR ] }'],env=env,check=True,stdout=subprocess.DEVNULL) + api('settings',settings={'audio.SOURCE':'rain-test-source','audio.ENABLED':1,'matrix.BLACK_RAIN_TIMEOUT':0}) + assert api('status')['audio']['state']=='idle' + api('activate',output='HEADLESS-1');until(lambda:api('status')['audio']['state']=='monitoring') + def audio_pid(): + for pid in pathlib.Path(f'/proc/{manager.pid}/task/{manager.pid}/children').read_text().split(): + try: + if pathlib.Path(f'/proc/{pid}/comm').read_text().strip()=='digitalrain-aud':return int(pid) + if b'digitalrain-audio' in pathlib.Path(f'/proc/{pid}/cmdline').read_bytes():return int(pid) + except FileNotFoundError:pass + return None + assert audio_pid() + api('black',output='HEADLESS-1');until(lambda:api('status')['outputs'][0]['phase']=='black') + until(lambda:api('status')['audio']['state']=='idle' and not audio_pid()) + api('wake');api('activate',output='HEADLESS-1');until(lambda:api('status')['audio']['state']=='monitoring') + old_audio=audio_pid();os.kill(old_audio,signal.SIGKILL) + until(lambda:audio_pid() not in (None,old_audio) and api('status')['audio']['state']=='monitoring',timeout=10) + # Server loss must back off instead of spawning a helper each tick. + audio_server.terminate();audio_server.wait(5) + until(lambda:api('status')['audio']['state']!='monitoring') + seen=set();end=time.monotonic()+4 + while time.monotonic()<end: + pid=audio_pid() + if pid:seen.add(pid) + time.sleep(.02) + assert len(seen)<=4,seen + audio_server=spawn(['pipewire']);until(lambda:(runtime/'pipewire-0').exists()) + # Reconnect policy and recreate only the private virtual source. + audio_policy.terminate();audio_policy.wait(5) + audio_policy=spawn(['dbus-run-session','--','wireplumber','--profile','policy']);time.sleep(.8) + subprocess.run(['pw-cli','create-node','adapter','{ factory.name = support.null-audio-sink node.name = rain-test-source media.class = Audio/Sink object.linger = true audio.position = [ FL FR ] }'],env=env,check=True,stdout=subprocess.DEVNULL) + until(lambda:api('status')['audio']['state']=='monitoring',timeout=35) + api('settings',settings={'audio.ENABLED':0});until(lambda:api('status')['audio']['state']=='disabled' and not audio_pid()) + api('wake');api('reload');audio_policy.terminate();audio_policy.wait(5);audio_server.terminate();audio_server.wait(5) + print('Real audio helper visibility lifecycle, black shutdown, crash recovery and disable: PASS',flush=True) + # Event clients receive state changes, not per-frame or countdown traffic. + with socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET) as watch: + watch.settimeout(3);watch.connect(str(runtime/'digitalrain/control.sock')) + watch.send(json.dumps(dict(version=1,method='watch')).encode());watch.recv(16384) + time.sleep(.4);watch.settimeout(.1) + try: + while watch.recv(16384):pass + except TimeoutError:pass + watch.settimeout(.4) + try:watch.recv(16384);raise AssertionError('unchanged state produced an event') + except TimeoutError:pass + api('inhibit',seconds=1,reason='event test');watch.settimeout(3);assert json.loads(watch.recv(16384))['inhibitors'] + with socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET) as bad: + bad.settimeout(2);bad.connect(str(runtime/'digitalrain/control.sock'));bad.send(b'{'*17000);assert bad.recv(16384)==b'' + assert api('status')['version']==1 + messages=[dict(jsonrpc='2.0',id=1,method='initialize',params=dict(protocolVersion='2025-11-25')), + dict(jsonrpc='2.0',method='notifications/initialized'),dict(jsonrpc='2.0',id=2,method='tools/list'), + dict(jsonrpc='2.0',id=3,method='tools/call',params=dict(name='status')), + dict(jsonrpc='2.0',id=4,method='resources/read',params=dict(uri='digitalrain://schema'))] + mcp=subprocess.run(['python3',str(pkg / 'digitalrain-mcp.py')],input=''.join(json.dumps(m)+'\n' for m in messages),text=True,capture_output=True,env=env,check=True) + replies=[json.loads(x) for x in mcp.stdout.splitlines()];assert len(replies)==4 and all('result' in x for x in replies),replies + assert not replies[2]['result']['isError'] + print('IPC settings/preview/inhibition, state events, packet limits and MCP: PASS',flush=True) + command=spawn([str(runtime/'bin/digitalrain'),'--lock']);assert command.wait(10)==0 + until(lambda:api('lock_status')['state']=='locked') + lock_pid=locker()['pid'] + api('settings',settings={'matrix.BLACK_RAIN_TIMEOUT':0,'matrix.COLOR':'green','matrix.FONT_SIZE':16,'matrix.HEAD_WHITE_MIN':0,'matrix.HEAD_WHITE_MAX':0,'alerts.SHOW_WHILE_LOCKED':1}) + api('alert',source='lock-test',id='regional',severity='error',seconds=20,output='HEADLESS-1') + until(lambda:dominant(0,0) and dominant(1,1)) + assert locker()['pid']==lock_pid and all(ipc('test/lock-state').values()) + api('clear_alert',source='lock-test',id='regional');until(lambda:dominant(0,1)) + print('Locked appearance reload and physical-region alerts preserve authentication and PID: PASS',flush=True) + manager.terminate();assert manager.wait(6)==0;assert locker()['state']=='locked' + manager=spawn([str(runtime/'bin/digitalrain')]);until(lambda:(runtime/'digitalrain/control.sock').exists()) + until(lambda:api('lock_status')['state']=='locked');api('wake');assert locker()['state']=='locked' + old_pid=locker()['pid'];os.kill(old_pid,signal.SIGKILL) + until(lambda:locker()['pid']!=old_pid and locker()['state']=='locked') + assert all(ipc('test/lock-state').values()) + type_password('rain-test-7');until(lambda:api('lock_status')['state']=='unlocked') + manager.terminate();manager.wait(6) + print('CLI lock confirmation and manager restart/wake preserve session lock: PASS',flush=True) + lock=spawn([str(runtime/'bin/digitalrain-lock')]);until(lambda:locker()['state']=='locked') + lock.kill();lock.wait(4);assert all(ipc('test/lock-state').values()) + recovered=spawn([str(runtime/'bin/digitalrain-lock')]);until(lambda:locker()['state']=='locked') + ipc('test/add-output');time.sleep(.6);assert all(ipc('test/lock-state').values()) + ipc('test/remove-output',dict(output='HEADLESS-2'));time.sleep(.6);assert recovered.poll() is None + type_password('rain-test-7');assert recovered.wait(8)==0 + print('Crash remains locked, replacement recovery and hot-plug: PASS',flush=True) + cfg.write_text(cfg.read_text().replace('BLACK_RAIN_TIMEOUT = 2', 'BLACK_RAIN_TIMEOUT = 1')+'\nPOWER_OFF = true\n') + powered=spawn([str(runtime/'bin/digitalrain-lock')]);until(lambda:locker()['state']=='locked') + until(lambda:all(not enabled for enabled in ipc('test/output-power').values())) + locker('wake');until(lambda:all(ipc('test/output-power').values()));assert all(ipc('test/lock-state').values()) + type_password('rain-test-7');assert powered.wait(8)==0 + print('Opt-in locked monitor power-off and wake retain protection: PASS',flush=True) +finally: + for q in reversed(children): + if q.poll() is None: + q.terminate() + try:q.wait(4) + except subprocess.TimeoutExpired:q.kill();q.wait() + print('Artifacts:',runtime,flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-stitched-outputs.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-stitched-outputs.py new file mode 100755 index 000000000000..ff27cad9f197 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-stitched-outputs.py @@ -0,0 +1,356 @@ +"""Physical monitor regressions in an isolated stitched Wayfire session. + +Run as a regular user. Requires built files/, Wayfire, grim, swaybg, mpv, +pkg-config, C++, and Pillow. No real desktop sockets or settings are used. +""" +import argparse +import io +import json +import os +from pathlib import Path +import shlex +import shutil +import signal +import socket +import struct +import subprocess +import tempfile +import time + +from PIL import Image + +parser = argparse.ArgumentParser() +parser.add_argument('--upgrade-from', type=Path, help='Directory of old helper libraries for live upgrade validation') +parser.add_argument('--binary', type=Path, help='Test an installed renderer/manager binary') +args = parser.parse_args() +assert os.geteuid() != 0, 'Run as a regular user' +package = Path(__file__).resolve().parents[1] +binary = str(args.binary or package / 'digitalrain') +runtime = Path(tempfile.mkdtemp(prefix='digitalrain-stitched-test-')) +children = [] +connections = [] +env = os.environ.copy() +for key in ['WAYLAND_DISPLAY', 'WAYFIRE_SOCKET', '_WAYFIRE_SOCKET', 'DISPLAY', + 'WAYFIRE_DEFAULT_CONFIG_BACKEND', 'WAYFIRE_CONFIG_FILE', 'LD_LIBRARY_PATH']: + env.pop(key, None) +env.update(XDG_RUNTIME_DIR=str(runtime), WLR_BACKENDS='headless', + WLR_HEADLESS_OUTPUTS='3', WLR_RENDERER='gles2', + WLR_RENDER_DRM_DEVICE=env.get('WLR_RENDER_DRM_DEVICE', '/dev/dri/renderD128'), + WAYFIRE_PLUGIN_PATH=f'{runtime}:{package}', + WAYFIRE_PLUGIN_XML_PATH=f'{runtime}:{package}') + + +def spawn(command): + with (runtime / f'child-{len(children)}.log').open('w') as log: + process = subprocess.Popen(command, env=env, stdout=log, stderr=log) + children.append(process) + return process + + +def until(check, timeout=6): + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if check(): + return + time.sleep(.05) + raise AssertionError('Timed out waiting for expected state') + + +def read(sock, count): + result = b'' + while len(result) < count: + part = sock.recv(count - len(result)) + if not part: + raise RuntimeError('IPC disconnected') + result += part + return result + + +def request(sock, method, data=None): + payload = json.dumps(dict(method=method, data=data or {})).encode() + sock.sendall(struct.pack('=I', len(payload)) + payload) + return json.loads(read(sock, struct.unpack('=I', read(sock, 4))[0])) + + +def connect(): + sock = socket.socket(socket.AF_UNIX) + sock.settimeout(3) + sock.connect(env['WAYFIRE_SOCKET']) + connections.append(sock) + return sock + + +def ipc(method, data=None): + with connect() as sock: + return request(sock, method, data) + + +def hold(output, fade_ms=0): + sock = connect() + assert request(sock, 'digitalrain/hide_desktop', dict(output=output, fade_ms=fade_ms)) == {'result': 'ok'} + return sock + + +def shot(output='VIRTUAL-1'): + png = subprocess.check_output(['grim', '-o', output, '-'], env=env) + return Image.open(io.BytesIO(png)).convert('RGB') + + +def pixels(color, output='VIRTUAL-1'): + return sum(p == color for p in shot(output).get_flattened_data()) + + +def solid(key, layer, output='VIRTUAL-1', close=False, x=0, y=0, w=100, h=80, + r=0.0, g=0.0, b=1.0): + data = dict(key=key, output=output, layer=layer, x=x, y=y, w=w, h=h, r=r, g=g, b=b) + if close: + data['close'] = True + assert ipc('test/solid', data) == {'result': 'ok'} + + +def rain(output='VIRTUAL-1', hide=True, transparent=True, timeout=0, black_timeout=0, fade_frames=6, power=False): + cmd = [binary, '--render', '--output', output, + '--color', 'green', '--fade-frames', str(fade_frames), '--transparent-timeout', str(timeout), + '--black-rain-timeout', str(black_timeout), '--power-off', str(int(power))] + if transparent: + cmd += ['--transparent'] + if hide: + cmd += ['--hide-desktop'] + return spawn(cmd) + + +def dismiss(process): + process.send_signal(signal.SIGUSR1) + assert process.wait(timeout=4) == 0 + + +try: + flags = shlex.split(subprocess.check_output( + ['pkg-config', '--cflags', '--libs', 'wayfire', 'wf-utils'], text=True)) + subprocess.run(['c++', '-std=c++17', '-fPIC', '-shared', '-DWAYFIRE_PLUGIN', + '-DWLR_USE_UNSTABLE', str(package / 'tests/desktop-test.cpp'), + '-o', str(runtime / 'libdesktop-test.so'), *flags], check=True) + (runtime / 'desktop-test.xml').write_text( + '<wayfire><plugin name="desktop-test"><_short>Test</_short></plugin></wayfire>') + if args.upgrade_from: + for helper in ('desktop', 'idle', 'sampler'): + shutil.copyfile(args.upgrade_from / f'libdigitalrain-{helper}.so', runtime / f'libdigitalrain-{helper}.so') + config = runtime / 'wayfire.ini' + def configure(stitched=True, power=True): + power_plugin = ' digitalrain-power' if power else '' + config.write_text(f'[core]\nplugins = ipc ipc-rules digitalrain-desktop digitalrain-idle digitalrain-sampler digitalrain-cursor desktop-test{power_plugin}\n' + 'xwayland = false\nvwidth = 1\nvheight = 1\nwindow_scope = virtual\n' + f'stitch_enabled = {str(stitched).lower()}\nstitch_outputs = HEADLESS-1 HEADLESS-2 HEADLESS-3\n' + '[workarounds]\nenable_so_unloading = false\n' + ''.join( + f'[output:HEADLESS-{i}]\nmode = 256x192@60\nposition = {(i-1)*256},0\n' for i in range(1,4))) + configure() + compositor = spawn(['wayfire', '-c', str(config)]) + until(lambda: list(runtime.glob('wayfire-*.socket'))) + env['WAYFIRE_SOCKET'] = str(next(runtime.glob('wayfire-*.socket'))) + env['WAYLAND_DISPLAY'] = next(p.name for p in runtime.glob('wayland-*') if not p.name.endswith('.lock')) + until(lambda: [o['name'] for o in ipc('window-rules/list-outputs')] == ['VIRTUAL-1']) + if args.upgrade_from: + assert set(ipc('digitalrain/sample_desktop')['outputs']) == {'VIRTUAL-1'} + for helper in ('desktop', 'idle', 'sampler'): + (runtime / f'libdigitalrain-{helper}.so').unlink() + ipc('wayfire/reload-plugins') + upgraded = ipc('digitalrain/sample_desktop') + assert set(upgraded['outputs']) == {'HEADLESS-1', 'HEADLESS-2', 'HEADLESS-3'}, upgraded + print('Resident old helpers upgraded without a compositor restart: PASS', flush=True) + assert ipc('digitalrain/hide_cursor') == {'result': 'ok'} # includes software cursor + background = spawn(['swaybg', '-c', 'cc0000']) + red, blue = (204, 0, 0), (0, 0, 255) + try: + until(lambda: pixels(red) == 768 * 192) + except AssertionError: + frame = shot(); frame.save(runtime/'background.png') + from collections import Counter + print('Background:', frame.size, Counter(frame.get_flattened_data()).most_common(5), flush=True) + raise + solid('span', 2, x=200, y=50, w=400, h=80) + until(lambda: pixels(blue) == 32000) + baseline = shot() + a = hold('HEADLESS-2') + until(lambda: pixels(blue) == (400-256)*80) + hidden = shot() + assert hidden.crop((0,0,256,192)).tobytes() == baseline.crop((0,0,256,192)).tobytes() + assert hidden.crop((512,0,768,192)).tobytes() == baseline.crop((512,0,768,192)).tobytes() + assert set(hidden.crop((256,0,512,192)).get_flattened_data()) == {red} + solid('new', 3, x=300, y=140, w=60, h=30) + time.sleep(.15) + assert set(shot().crop((256,0,512,192)).get_flattened_data()) == {red} + solid('new', 3, close=True) + b = hold('HEADLESS-1') + until(lambda: pixels(blue) == 88*80) + a.close() + until(lambda: pixels(blue) == (400-56)*80) + b.close() + until(lambda: shot().tobytes() == baseline.tobytes()) + print('Seam-spanning windows, new views, independent hide owners and disconnect recovery: PASS', flush=True) + a = hold('HEADLESS-2', 600) + time.sleep(.25) + intermediate = shot() + assert intermediate.getpixel((300,70)) not in (red,blue) + assert intermediate.getpixel((210,70)) == blue + assert intermediate.getpixel((550,70)) == blue + until(lambda: shot().getpixel((300,70)) == red) + request(a, 'digitalrain/show_desktop') + time.sleep(.25) + assert shot().getpixel((300,70)) not in (red,blue) + until(lambda: shot().tobytes() == baseline.tobytes()) + a.close() + print('Physical-region fade out and wake fade preserve neighboring pixels: PASS', flush=True) + r = rain('HEADLESS-2', timeout=1, black_timeout=1) + until(lambda: set(shot().crop((256,0,512,192)).get_flattened_data()) == {(0,0,0)}, timeout=6) + image = shot() + assert ipc('test/output-power')['HEADLESS-2'], 'default black must keep the monitor powered' + assert image.crop((0,0,256,192)).tobytes() == baseline.crop((0,0,256,192)).tobytes() + assert image.crop((512,0,768,192)).tobytes() == baseline.crop((512,0,768,192)).tobytes() + dismiss(r) + until(lambda: shot().tobytes() == baseline.tobytes()) + samples = ipc('digitalrain/sample_desktop')['outputs'] + assert set(samples) == {'HEADLESS-1','HEADLESS-2','HEADLESS-3'}, samples + assert all('luminance' in s for s in samples.values()), samples + assert samples['HEADLESS-1']['luminance'] != samples['HEADLESS-2']['luminance'] + print('Region-sized renderer, black timeout, wake and per-monitor sampling: PASS', flush=True) + + topology = ipc('window-rules/list-outputs') + r = rain('HEADLESS-2', hide=False, timeout=1, black_timeout=1, power=True) + until(lambda: not ipc('test/output-power')['HEADLESS-2']) + state = ipc('test/output-power') + assert state['HEADLESS-1'] and state['HEADLESS-3'] and state['VIRTUAL-1'], state + assert ipc('window-rules/list-outputs') == topology + dismiss(r) + until(lambda: ipc('test/output-power')['HEADLESS-2']) + until(lambda: shot().tobytes() == baseline.tobytes()) + r = rain('HEADLESS-2', hide=False, timeout=1, black_timeout=1, power=True) + until(lambda: not ipc('test/output-power')['HEADLESS-2']) + r.kill(); r.wait(timeout=3) + until(lambda: ipc('test/output-power')['HEADLESS-2']) + print('Opt-in physical monitor power-off, unchanged topology, wake and crash recovery: PASS', flush=True) + + a, b = connect(), connect() + for client in (a, b): + assert request(client, 'digitalrain/power_off', dict(output='HEADLESS-2')) == {'result':'ok'} + a.close() + time.sleep(.1) + assert not ipc('test/output-power')['HEADLESS-2'] + b.close() + until(lambda: ipc('test/output-power')['HEADLESS-2']) + assert 'error' in ipc('digitalrain/power_off', dict(output='VIRTUAL-1')) + assert ipc('window-rules/set-output-power', dict(output_name='HEADLESS-2', enabled=False)) == {'result':'ok'} + assert 'error' in ipc('digitalrain/power_off', dict(output='HEADLESS-2')) + assert not ipc('test/output-power')['HEADLESS-2'] + ipc('window-rules/set-output-power', dict(output_name='HEADLESS-2', enabled=True)) + a = connect() + assert request(a, 'digitalrain/power_off', dict(output='HEADLESS-2')) == {'result':'ok'} + configure(power=False) + until(lambda: ipc('test/output-power')['HEADLESS-2']) + a.close() + r = rain('HEADLESS-2', hide=False, timeout=1, black_timeout=1, power=True) + until(lambda: set(shot().crop((256,0,512,192)).get_flattened_data()) == {(0,0,0)}) + time.sleep(.3) + assert ipc('test/output-power')['HEADLESS-2'] and r.poll() is None + dismiss(r) + configure() + until(lambda: 'digitalrain/power_off' in ipc('list-methods')['methods']) + print('Power ownership, external-off preservation, plugin unload and missing-helper fallback: PASS', flush=True) + + video = spawn(['mpv', '--no-config', '--vo=gpu', '--gpu-context=wayland', + '--no-audio', '--stop-screensaver=yes', '--title=digitalrain-inhibitor-test', + 'av://lavfi:color=c=white:s=64x64:r=5']) + def video_view(): + return next((v for v in ipc('window-rules/list-views') if v.get('title') == 'digitalrain-inhibitor-test'), None) + until(video_view) + vid = video_view()['id'] + def place_video(x, width): + result = ipc('window-rules/configure-view', dict(id=vid, output_name='VIRTUAL-1', + geometry=dict(x=x,y=20,width=width,height=100))) + assert result == {'result':'ok'}, result + place_video(280, 180) + until(lambda: ipc('digitalrain/idle_inhibitors')['outputs'] == ['HEADLESS-2']) + place_video(200, 400) + until(lambda: set(ipc('digitalrain/idle_inhibitors')['outputs']) == {'HEADLESS-1','HEADLESS-2','HEADLESS-3'}) + video.terminate(); video.wait(timeout=5) + until(lambda: ipc('digitalrain/idle_inhibitors')['outputs'] == []) + print('Real video inhibitor follows physical placement, spans members, and releases on exit: PASS', flush=True) + + saver_config = runtime / 'digitalrain.toml' + saver_config.write_text('[global]\nTIMEOUT=2\nFOCUSED_TIMEOUT=4\nCHECK_INTERVAL=1\n' + f'INHIBIT_VIDEO=false\nLOG_FILE="{runtime}/manager.log"\n' + '[matrix]\nCOLOR="green"\nTRANSPARENT=true\nHIDE_DESKTOP=true\nFADE_FRAMES=2\n') + env['DIGITALRAIN_CONFIG'] = str(saver_config) + ipc('test/activity', dict(x=600.0,y=120.0)) + manager = spawn([binary]) + def running(): + pids = Path(f'/proc/{manager.pid}/task/{manager.pid}/children').read_text().split() + result = set() + for pid in pids: + try: args = Path(f'/proc/{pid}/cmdline').read_bytes().split(b'\0') + except FileNotFoundError: continue + if b'--output' in args: result.add(args[args.index(b'--output')+1].decode()) + return result + def active_for(x, seconds): + deadline = time.monotonic()+seconds + while time.monotonic()<deadline: + ipc('test/activity', dict(x=float(x),y=120.0)) + time.sleep(.15) + active_for(600, 5) + assert running() == {'HEADLESS-1','HEADLESS-2'}, running() + active_for(100, 4) + assert running() == {'HEADLESS-2','HEADLESS-3'}, running() + print('Real manager timers: active member stays awake; crossing wakes only that member: PASS', flush=True) + configure(False) + until(lambda: len(ipc('window-rules/list-outputs')) == 3) + active_for(100, 2) + configure(True) + until(lambda: [o['name'] for o in ipc('window-rules/list-outputs')] == ['VIRTUAL-1']) + active_for(100, 3) + assert manager.poll() is None + log = (runtime/'manager.log').read_text() + assert 'renderer exited immediately' not in log, log + assert 'Removed or reconfigured output: HEADLESS-' in log + manager.terminate() + assert manager.wait(timeout=6) == 0 + print('Group/ungroup while running removes stale topology and preserves manager: PASS', flush=True) + + saver_config.write_text('[global]\nTIMEOUT=1\nFOCUSED_TIMEOUT=1\nCHECK_INTERVAL=1\n' + f'INHIBIT_VIDEO=false\nLOG_FILE="{runtime}/power-manager.log"\n' + '[matrix]\nCOLOR="green"\nTRANSPARENT=true\nFADE_FRAMES=2\n' + 'TRANSPARENT_TIMEOUT=1\nBLACK_RAIN_TIMEOUT=1\nPOWER_OFF=true\n' + '[output_power_off]\nHEADLESS-3=false\n') + manager = spawn([binary]) + until(lambda: not ipc('test/output-power')['HEADLESS-1'] and + not ipc('test/output-power')['HEADLESS-2'], timeout=12) + assert ipc('test/output-power')['HEADLESS-3'] + assert running() == {'HEADLESS-1','HEADLESS-2','HEADLESS-3'} + manager.kill(); manager.wait(timeout=3) + until(lambda: all(ipc('test/output-power').values())) + print('Manager passes power policy and per-output overrides; manager death restores monitors: PASS', flush=True) + + configure(False) + until(lambda: len(ipc('window-rules/list-outputs')) == 3) + standalone_topology = ipc('window-rules/list-outputs') + r = rain('HEADLESS-2', hide=False, transparent=False, black_timeout=1, power=True) + until(lambda: not ipc('test/output-power')['HEADLESS-2']) + assert ipc('window-rules/list-outputs') == standalone_topology + assert ipc('test/output-power')['HEADLESS-1'] and ipc('test/output-power')['HEADLESS-3'] + dismiss(r) + until(lambda: ipc('test/output-power')['HEADLESS-2']) + a = connect() + assert request(a, 'digitalrain/power_off', dict(output='HEADLESS-2')) == {'result':'ok'} + assert ipc('test/remove-output', dict(output='HEADLESS-2')) == {'result':'ok'} + a.close() + until(lambda: 'HEADLESS-2' not in ipc('test/output-power')) + assert compositor.poll() is None + print('Standalone monitor power/wake and unplugging a sleeping monitor: PASS', flush=True) +finally: + for connection in connections: + connection.close() + for child in reversed(children): + if child.poll() is None: + child.terminate() + try: child.wait(timeout=5) + except subprocess.TimeoutExpired: + child.kill(); child.wait() + print(f'Test artifacts: {runtime}', flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-topology.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-topology.c new file mode 100644 index 000000000000..9466b6a7d251 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tests/test-topology.c @@ -0,0 +1,47 @@ +#define main digitalrain_program_main +#include "../digitalrain.c" +#undef main +#include <assert.h> + +int main(void) { + const char *topology = "[{\"name\":\"VIRTUAL-1\",\"geometry\":{\"x\":100,\"y\":20,\"width\":512,\"height\":192}," + "\"members\":[{\"name\":\"DP-1\",\"geometry\":{\"x\":0,\"y\":0,\"width\":256,\"height\":192}}," + "{\"name\":\"DP-2\",\"geometry\":{\"x\":256,\"y\":0,\"width\":256,\"height\":192}}]}," + "{\"name\":\"HDMI-A-1\",\"geometry\":{\"x\":612,\"y\":20,\"width\":192,\"height\":108}}]"; + saver_target_t targets[MAX_OUTPUTS]; + struct json_object *outputs = json_tokener_parse(topology); + int n = parse_targets(outputs, targets); + assert(n == 3); + assert(targets[1].region && targets[1].x == 256 && targets[1].layout_x == 100); + assert(!strcmp(targets[1].desktop, "VIRTUAL-1")); + assert(!targets[2].region && targets[2].x == 0 && targets[2].layout_x == 612); + pthread_mutex_init(&G.lock, NULL); + mgr_reconcile_targets(targets, n); + G.outputs[0].last_input = 42; + G.outputs[0].timeout_override = 17; + mgr_reconcile_targets(targets, n); + assert(G.outputs[0].last_input == 42 && G.outputs[0].timeout_override == 17); + pid_t child = fork(); + assert(child >= 0); + if (!child) { for (;;) pause(); } + G.outputs[1].pids[0] = child; + G.outputs[1].pid_count = 1; + strcpy(G.outputs[1].state, "off"); + mgr_reconcile_targets(targets + 2, 1); + int status; + assert(waitpid(child, &status, 0) == child && WIFSIGNALED(status)); + assert(G.output_count == 1 && !strcmp(G.outputs[0].name, "HDMI-A-1")); + assert(!mgr_find_output("DP-2")); + json_object_put(outputs); + outputs = json_tokener_parse("[]"); + assert(parse_targets(outputs, targets) == 0); + json_object_put(outputs); + assert(parse_targets(NULL, targets) == -1); + outputs = json_tokener_parse("[{\"name\":\"bad\"}]"); + assert(parse_targets(outputs, targets) == -1); + json_object_put(outputs); + mgr_reconcile_targets(targets, 0); + assert(G.output_count == 0); + pthread_mutex_destroy(&G.lock); + puts("Topology parsing, stable idle state, disappearing outputs and renderer cleanup: PASS"); +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.LICENSE b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.LICENSE new file mode 100644 index 000000000000..173779e2145a --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024-2026, CK Tan + +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. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.UPSTREAM b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.UPSTREAM new file mode 100644 index 000000000000..7c4dc6cca1f7 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.UPSTREAM @@ -0,0 +1,3 @@ +https://github.com/cktan/tomlc17 +Commit: 8d3766ddafa6515417f4885ae6878ef69fac401e +Unmodified source/header; MIT license in tomlc17.LICENSE. Supports TOML 1.0 and 1.1. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.c new file mode 100644 index 000000000000..6afa46945591 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.c @@ -0,0 +1,3291 @@ +/* Copyright (c) 2024-2026, CK Tan. + * https://github.com/cktan/tomlc17/blob/main/LICENSE + */ +#include "tomlc17.h" +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <math.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +const toml_datum_t DATUM_ZERO = {0}; + +static toml_option_t toml_option = {0, realloc, free}; + +#define MALLOC(n) toml_option.mem_realloc(0, n) +#define REALLOC(p, n) toml_option.mem_realloc(p, n) +#define FREE(p) toml_option.mem_free(p) + +#define DO(x) \ + if (x) \ + return -1; \ + else \ + (void)0 + +// Copy string src to dst where dst is limited to dstsz that includes +// NUL. Return 0 on success, -1 otherwise (because src[] is longer than dst[]). +static inline int copystring(char *dst, int dstsz, const char *src) { + int srcsz = strlen(src) + 1; + if (srcsz > dstsz) { + return -1; + } + memcpy(dst, src, srcsz); + return 0; +} + +/* + * Error buffer + */ +typedef struct ebuf_t ebuf_t; +struct ebuf_t { + char *ptr; + int len; +}; + +/* + * Format an error into ebuf[]. Always return -1. + */ +static int SETERROR(ebuf_t ebuf, int lineno, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + char *p = ebuf.ptr; + char *q = p + ebuf.len; + if (lineno) { + snprintf(p, p < q ? q - p : 0, "(line %d) ", lineno); + p += strlen(p); + } + vsnprintf(p, p < q ? q - p : 0, fmt, args); + va_end(args); + return -1; +} + +/* + * Memory pool. A pool is a pair of page_t lists: small allocations + * (<= PAGE_LARGE_THRESHOLD bytes) are bump-allocated out of + * PAGE_SMALL_SIZE pages, handed out piecemeal until a page fills up + * and a fresh one is linked in front. Large allocations get their + * own exactly-sized page, used once and never shared. This avoids + * ever mallocing one giant block sized for the whole document. + */ +#define PAGE_SMALL_SIZE 4096 +#define PAGE_LARGE_THRESHOLD 1024 +// Small allocations must always fit in a fresh small page. +static_assert(PAGE_SMALL_SIZE > PAGE_LARGE_THRESHOLD, + "page/threshold invariant"); + +#define PAGE_MAGIC 0x50414745u // "PAGE" + +typedef struct page_t page_t; +struct page_t { + uint32_t magic; // PAGE_MAGIC, checked to catch corruption/use-after-free + int top; // offset of first free byte in data[] + int max; // size of data[] + page_t *next; // link to next page in list + char data[1]; // first byte starts here +}; + +#define POOL_MAGIC 0x504f4f4cu // "POOL" + +typedef struct pool_t pool_t; +struct pool_t { + uint32_t magic; // POOL_MAGIC, checked to catch corruption/use-after-free + page_t *small; // most recent small page, bump-allocated piecemeal + page_t *large; // most recent large page, each used for one alloc only +}; + +/** + * Create a page of `size` bytes. Return the page on success, or + * NULL if out of memory. + */ +static page_t *page_create(int size) { + if (!(0 <= size && size <= (1 << 30))) { // [0..1GB] + return NULL; + } + size_t totalsz = (size_t)&((page_t *)0)->data[size]; + page_t *page = MALLOC(totalsz); + if (!page) { + return NULL; + } + page->magic = PAGE_MAGIC; + page->top = 0; + page->max = size; + page->next = NULL; + return page; +} + +/** + * Create an empty memory pool. Return the memory pool on success, + * or NULL if out of memory. pool->small is always non-NULL: a + * pool holds at least one small page for its whole lifetime. + */ +static pool_t *pool_create(void) { + pool_t *pool = MALLOC(sizeof(pool_t)); + if (!pool) { + return NULL; + } + pool->magic = POOL_MAGIC; + pool->small = page_create(PAGE_SMALL_SIZE); + if (!pool->small) { + FREE(pool); + return NULL; + } + pool->large = NULL; + return pool; +} + +/** + * Destroy a memory pool, freeing every page in it. + */ +static void pool_destroy(pool_t *pool) { + if (!pool) { + return; + } + assert(pool->magic == POOL_MAGIC); + pool->magic = 0; // catch use-after-free/double-destroy + for (page_t *p = pool->small; p;) { + page_t *next = p->next; + assert(p->magic == PAGE_MAGIC); + p->magic = 0; // catch use-after-free/double-destroy + FREE(p); + p = next; + } + for (page_t *p = pool->large; p;) { + page_t *next = p->next; + assert(p->magic == PAGE_MAGIC); + p->magic = 0; // catch use-after-free/double-destroy + FREE(p); + p = next; + } + FREE(pool); +} + +/** + * Allocate n bytes from pool. Return the memory allocated on + * success, or NULL if out of memory. + */ +static char *pool_alloc(pool_t *pool, int n) { + assert(pool->magic == POOL_MAGIC); + if (n > PAGE_LARGE_THRESHOLD) { + // Large alloc: own exactly-sized page, used once. + page_t *page = page_create(n); + if (!page) { + return NULL; + } + page->next = pool->large; + pool->large = page; + page->top = page->max; + return page->data; + } + + assert(pool->small->magic == PAGE_MAGIC); + if (pool->small->top + n > pool->small->max) { + // Current small page doesn't have room: abandon its leftover + // space and start a fresh one. + page_t *page = page_create(PAGE_SMALL_SIZE); + if (!page) { + return NULL; + } + page->next = pool->small; + pool->small = page; + } + char *ret = pool->small->data + pool->small->top; + pool->small->top += n; + return ret; +} + +// Cell: expandable buffer that behaves like realloc +typedef struct cell_t cell_t; +struct cell_t { + uint32_t top, max; + // first byte of data starts here (i.e. &(*this)[1]) +}; + +static char *cell_realloc(char *p, int size) { + assert(size >= 0); + if (p == NULL) { + // first malloc + cell_t *tmp = REALLOC(NULL, size + sizeof(cell_t)); + if (!tmp) { + return 0; // out of memory + } + cell_t *cp = tmp; + cp->max = size; + cp->top = size; + return (char *)&cp[1]; + } + + // obtain a handle to cell info + cell_t *cp = (cell_t *)(p - sizeof(cell_t)); + if ((uint32_t)size <= cp->max) { + // cell is big enough for the new size. DONE. + cp->top = size; + return p; + } + + // need to expand. add 30% margin. + int newmax = size * 1.3 + 100; + cell_t *tmp = REALLOC(cp, newmax + sizeof(cell_t)); + if (!tmp) { + return 0; // out of memory + } + cp = tmp; + cp->max = newmax; + cp->top = size; + return (char *)&cp[1]; +} + +static void cell_free(char *p) { + if (p) { + cell_t *cp = (cell_t *)(p - sizeof(cell_t)); + FREE(cp); + } +} + +/* This is a string view. */ +typedef struct span_t span_t; +struct span_t { + const char *ptr; + int len; +}; + +/* Represents a multi-part key */ +#define KEYPARTMAX 10 +typedef struct keypart_t keypart_t; +struct keypart_t { + int nspan; + span_t span[KEYPARTMAX]; +}; + +static int utf8_to_ucs(const char *s, int len, uint32_t *ret); +static int ucs_to_utf8(uint32_t code, char buf[4]); + +// Maximum levels of brackets and braces to prevent +// stack overflow during recursive descent of the parser. +#define BRACKET_LEVEL_MAX 30 +#define BRACE_LEVEL_MAX 30 +#define TABLE_MAX (1 << 14) // 16k +#define ARRAY_MAX (1 << 14) // 16k + +// Buffer size for scanning a number or a timestamp. TOML does not limit the +// length of either literal; tomlc17 does, like the limits above: a literal that +// fills the buffer is rejected as too long, so LITBUF_SIZE - 2 bytes is the +// longest one accepted. That accommodates "9_007_199_254_740_991.0", +// picosecond-precision timestamps, and every other spelling of ordinary length. +// The checks test the length of the literal itself, not of the copy, because +// scan_copystr() also copies whatever follows the literal. +#define LITBUF_SIZE 120 + +enum toktyp_t { + TOK_DOT = 1, + TOK_EQUAL, + TOK_COMMA, + TOK_LBRACK, // [ + TOK_LLBRACK, // [[ + TOK_RBRACK, // ] + TOK_RRBRACK, // ]] + TOK_LBRACE, // { + TOK_RBRACE, // } + TOK_LIT, + TOK_STRING, // "string" + TOK_MLSTRING, // """multi-line-string""" + TOK_LITSTRING, // 'lit-string' + TOK_MLLITSTRING, // '''multi-line-lit-string''' + TOK_TIME, + TOK_DATE, + TOK_DATETIME, + TOK_DATETIMETZ, + TOK_INTEGER, + TOK_FLOAT, + TOK_BOOL, + TOK_ENDL, + TOK_FIN = -5000, // EOF +}; +typedef enum toktyp_t toktyp_t; +typedef struct scanner_t scanner_t; + +/* Remember the current state of a scanner */ +typedef struct scanner_state_t scanner_state_t; +struct scanner_state_t { + scanner_t *sp; + const char *cur; // points into scanner_t::src[] + int lineno; // current line number + const char *line_start; +}; + +// A scan token +typedef struct token_t token_t; +struct token_t { + toktyp_t toktyp; + int lineno; + int colno; + span_t str; + + // values represented by str + union { + const char *escp; // point to an esc char in str + int64_t int64; + double fp64; + bool b1; + struct { + // validity depends on toktyp for TIME, DATE, DATETIME, DATETIMETZ + int year, month, day, hour, minute, sec, usec; + int tz; // +- minutes + } tsval; + } u; +}; + +// Scanner object +struct scanner_t { + const char *src; // src[] + const char *endp; // end of src[], i.e. src + len + const char *cur; // current char in src[] + int lineno; // line number of current char + const char *line_start; + char *errmsg; // set to ebuf.ptr if there was an error + ebuf_t ebuf; // buffer to store error message + + int bracket_level; // count depth of [ ] + int brace_level; // count depth of { } +}; +static void scan_init(scanner_t *sp, const char *src, int len, char *errbuf, + int errbufsz); +static int scan_key(scanner_t *sp, token_t *tok); +static int scan_value(scanner_t *sp, token_t *tok); +// restore scanner to state before tok was returned +static scanner_state_t scan_mark(scanner_t *sp); +static void scan_restore(scanner_t *sp, scanner_state_t state); + +#ifndef min +static inline int min(int a, int b) { return a < b ? a : b; } +#endif + +// Copy up to dstsz - 1 chars from the current position of the scanner +// to dst, and always terminate dst[] with a NUL if dstsz > 0. +static void scan_copystr(scanner_t *sp, char *dst, int dstsz) { + assert(dstsz > 0); + int len = min(sp->endp - sp->cur, dstsz - 1); // account for NUL + if (len > 0) { + memcpy(dst, sp->cur, len); + dst[len] = '\0'; + } +} + +// Parser object +typedef struct parser_t parser_t; +struct parser_t { + scanner_t scanner; + toml_datum_t toptab; // top table + toml_datum_t *curtab; // current table + pool_t *pool; // memory pool for strings + const char *srcname; // source name copied into the pool, or NULL + ebuf_t ebuf; // buffer to store last error message +}; + +// Find key in tab and return its index. If not found, return -1. +static int tab_find(toml_datum_t *tab, span_t key) { + assert(tab->type == TOML_TABLE); + for (int i = 0, top = tab->u.tab.size; i < top; i++) { + if (tab->u.tab.len[i] == key.len && + 0 == memcmp(tab->u.tab.key[i], key.ptr, key.len)) { + return i; + } + } + return -1; +} + +// Put key into tab dictionary. Return a pointer to the datum for the key. +// If the key already exists, returns a pointer to its existing datum (the +// datum is NOT zeroed — callers must check datum->type to detect duplicates). +// If the key is new, appends it with a zero datum and returns a pointer to it. +// Returns NULL on allocation failure. +// +// Dual-use: tab_add() uses the non-zero type to detect and reject duplicates; +// datum_copy/datum_merge() use it to locate or create a slot for writing. +static toml_datum_t *tab_emplace(toml_datum_t *tab, span_t key, + const char **reason) { + assert(tab->type == TOML_TABLE); + int i = tab_find(tab, key); + if (i >= 0) { + return &tab->u.tab.value[i]; + } + + // Expand pkey[], plen[] and value[]. + int N = tab->u.tab.size; + if (N >= TABLE_MAX) { + *reason = "table too large"; + return NULL; + } + + { + char **pkey = + (char **)cell_realloc((char *)tab->u.tab.key, sizeof(*pkey) * (N + 1)); + int *plen = + (int *)cell_realloc((char *)tab->u.tab.len, sizeof(*plen) * (N + 1)); + toml_datum_t *value = (toml_datum_t *)cell_realloc( + (char *)tab->u.tab.value, sizeof(*value) * (N + 1)); + + // on success, must save new pointers in tab->u.tab because the + // old memory areas are gone. + if (pkey) { + tab->u.tab.key = (const char **)pkey; + } + if (plen) { + tab->u.tab.len = plen; + } + if (value) { + tab->u.tab.value = value; + } + + // if any fail, it is safe to bail out. + if (!pkey || !plen || !value) { + *reason = "out of memory"; + return NULL; + } + } + + // There is sufficient space in all the arrays for one more element. + // Append the new key. The value is set to DATUM_ZERO. Caller will + // overwrite with a valid datum. + tab->u.tab.size = N + 1; + tab->u.tab.key[N] = (char *)key.ptr; + tab->u.tab.len[N] = key.len; + tab->u.tab.value[N] = DATUM_ZERO; + return &tab->u.tab.value[N]; +} + +// Add a new key in tab. Return 0 on success, -1 otherwise. +// On error, *reason will point to an error message. +static int tab_add(toml_datum_t *tab, span_t newkey, toml_datum_t newvalue, + const char **reason) { + assert(tab->type == TOML_TABLE); + toml_datum_t *pvalue = tab_emplace(tab, newkey, reason); + if (!pvalue) { + return -1; + } + if (pvalue->type) { + *reason = "duplicate key"; + return -1; + } + *pvalue = newvalue; + return 0; +} + +// Add a new element into an array. Return 0 on success, -1 otherwise. +// On error, *reason will point to an error message. +static toml_datum_t *arr_emplace(toml_datum_t *arr, const char **reason) { + assert(arr->type == TOML_ARRAY); + int n = arr->u.arr.size; + if (n >= ARRAY_MAX) { + *reason = "array too large"; + return NULL; + } + toml_datum_t *elem = (toml_datum_t *)cell_realloc((char *)arr->u.arr.elem, + sizeof(*elem) * (n + 1)); + if (!elem) { + *reason = "out of memory"; + return NULL; + } + arr->u.arr.elem = elem; + arr->u.arr.size = n + 1; + elem[n] = DATUM_ZERO; + return &elem[n]; +} + +// ------------------- parser section +static int parse_norm(parser_t *pp, token_t tok, span_t *ret_span); +static int parse_val(parser_t *pp, token_t tok, toml_datum_t *ret); +static int parse_keyvalue_expr(parser_t *pp, token_t tok); +static int parse_std_table_expr(parser_t *pp, token_t tok); +static int parse_array_table_expr(parser_t *pp, token_t tok); + +static toml_datum_t mkdatum(toml_type_t ty) { + toml_datum_t ret = {0}; + ret.type = ty; + if (ty == TOML_DATE || ty == TOML_TIME || ty == TOML_DATETIME || + ty == TOML_DATETIMETZ) { + ret.u.ts.year = -1; + ret.u.ts.month = -1; + ret.u.ts.day = -1; + ret.u.ts.hour = -1; + ret.u.ts.minute = -1; + ret.u.ts.second = -1; + ret.u.ts.usec = -1; + ret.u.ts.tz = -1; + } + return ret; +} + +static toml_datum_t mkdatum_at(toml_type_t ty, int lineno, int colno) { + toml_datum_t ret = mkdatum(ty); + ret.lineno = lineno; + ret.colno = colno; + return ret; +} + +// Recursively free any dynamically allocated memory in the datum tree +static void datum_free(toml_datum_t *datum) { + if (datum->type == TOML_TABLE) { + for (int i = 0, top = datum->u.tab.size; i < top; i++) { + datum_free(&datum->u.tab.value[i]); + } + cell_free((char *)(void *)datum->u.tab.key); + cell_free((char *)datum->u.tab.len); + cell_free((char *)datum->u.tab.value); + } else if (datum->type == TOML_ARRAY) { + for (int i = 0, top = datum->u.arr.size; i < top; i++) { + datum_free(&datum->u.arr.elem[i]); + } + cell_free((char *)datum->u.arr.elem); + } + // other types do not allocate memory + *datum = DATUM_ZERO; +} + +// Maps each distinct source-name pointer to a single copy in the destination +// pool (deduplicated), so the merged pool holds at most one copy per distinct +// source pointer. Dedup is by pointer identity, not string content: every +// datum from one parse shares one source pointer (set_source_recursive), so +// dedup is exact within a tree, but two trees with content-identical source +// names (e.g. the same filename parsed twice) still get separate copies. +typedef struct srcmap_t srcmap_t; +struct srcmap_t { + pool_t *pool; + const char **olds; // cell buffer of old pointers + const char **news; // cell buffer of new (copied) pointers + int n, cap; +}; + +// Returns the deduplicated copy of src, or NULL for a NULL input or on +// out-of-memory. Callers distinguish the two: a non-NULL src that yields NULL +// is a hard failure. +static const char *dedup_source(srcmap_t *m, const char *src) { + if (!src) { + return NULL; + } + // Pointer-identity match (see srcmap_t above). The scan is O(n) in the + // number of distinct source pointers seen so far, not in the (much + // larger) number of datums, since repeats short-circuit here. + for (int i = 0; i < m->n; i++) { + if (m->olds[i] == src) { + return m->news[i]; + } + } + // Grow the memo first, so an allocation failure aborts cleanly. + if (m->n == m->cap) { + int newcap = m->cap ? m->cap * 2 : 4; + const char **no = + (const char **)cell_realloc((char *)m->olds, sizeof(*no) * newcap); + if (!no) { + return NULL; // out of memory + } + m->olds = no; + const char **nn = + (const char **)cell_realloc((char *)m->news, sizeof(*nn) * newcap); + if (!nn) { + return NULL; // out of memory + } + m->news = nn; + m->cap = newcap; + } + int len = (int)strlen(src) + 1; + char *p = pool_alloc(m->pool, len); + if (!p) { + return NULL; // out of memory + } + memcpy(p, src, len); + m->olds[m->n] = src; + m->news[m->n] = p; + m->n++; + return p; +} + +// Make a deep copy of src to dst. +// Return 0 on success, -1 otherwise. +static int datum_copy(toml_datum_t *dst, toml_datum_t src, srcmap_t *sm, + const char **reason) { + *dst = mkdatum_at(src.type, src.lineno, src.colno); + dst->flag = src.flag; + switch (src.type) { + case TOML_STRING: + // String bytes live in src's pool; copy them into dst's pool so dst + // stays independently owned. + dst->u.str.ptr = pool_alloc(sm->pool, src.u.str.len + 1); + if (!dst->u.str.ptr) { + *reason = "out of memory"; + goto bail; + } + dst->u.str.len = src.u.str.len; + memcpy((char *)dst->u.str.ptr, src.u.str.ptr, src.u.str.len + 1); + break; + case TOML_TABLE: + // Recreate each entry in dst: the key bytes are pool-copied (like a + // string value), and the value is deep-copied recursively. + for (int i = 0; i < src.u.tab.size; i++) { + char *keycopy = pool_alloc(sm->pool, src.u.tab.len[i] + 1); + if (!keycopy) { + *reason = "out of memory"; + goto bail; + } + memcpy(keycopy, src.u.tab.key[i], src.u.tab.len[i] + 1); + span_t newkey = {keycopy, src.u.tab.len[i]}; + toml_datum_t *pvalue = tab_emplace(dst, newkey, reason); + if (!pvalue) { + goto bail; + } + if (datum_copy(pvalue, src.u.tab.value[i], sm, reason)) { + goto bail; + } + } + break; + case TOML_ARRAY: + // Deep-copy each element into a freshly emplaced slot in dst. + for (int i = 0; i < src.u.arr.size; i++) { + toml_datum_t *pelem = arr_emplace(dst, reason); + if (!pelem) { + goto bail; + } + if (datum_copy(pelem, src.u.arr.elem[i], sm, reason)) { + goto bail; + } + } + break; + default: + // Every other type (int, float, bool, timestamp, invalid) is a plain + // value with no pool-owned pointers, so a struct copy is a full copy. + *dst = src; + break; + } + + dst->source = dedup_source(sm, src.source); + if (src.source && !dst->source) { + *reason = "out of memory"; + goto bail; + } + return 0; + +bail: + datum_free(dst); + return -1; +} + +// Check if datum is an array of tables. An empty array is deliberately +// excluded: it has no elements to type-check, so treating it as an array +// of tables here would be true only vacuously, and datum_merge's caller +// relies on this to distinguish "override with an empty array" from +// "append zero table elements" (see below). +static inline bool is_array_of_tables(toml_datum_t datum) { + bool ret = (datum.type == TOML_ARRAY) && (datum.u.arr.size > 0); + for (int i = 0; ret && i < datum.u.arr.size; i++) { + ret = (datum.u.arr.elem[i].type == TOML_TABLE); + } + return ret; +} + +// Merge src into dst. Return 0 on success, -1 otherwise. +static int datum_merge(toml_datum_t *dst, toml_datum_t src, srcmap_t *sm, + const char **reason) { + if (dst->type != src.type) { + datum_free(dst); + return datum_copy(dst, src, sm, reason); + } + switch (src.type) { + case TOML_TABLE: + // for key-value in src: + // override key-value in dst. + for (int i = 0; i < src.u.tab.size; i++) { + char *keycopy = pool_alloc(sm->pool, src.u.tab.len[i] + 1); + if (!keycopy) { + *reason = "out of memory"; + return -1; + } + memcpy(keycopy, src.u.tab.key[i], src.u.tab.len[i] + 1); + span_t key; + key.ptr = keycopy; + key.len = src.u.tab.len[i]; + toml_datum_t *pvalue = tab_emplace(dst, key, reason); + if (!pvalue) { + return -1; + } + if (pvalue->type) { + DO(datum_merge(pvalue, src.u.tab.value[i], sm, reason)); + } else { + datum_free(pvalue); + DO(datum_copy(pvalue, src.u.tab.value[i], sm, reason)); + } + } + return 0; + case TOML_ARRAY: + if (is_array_of_tables(*dst) && is_array_of_tables(src)) { + // append src array to dst + for (int i = 0; i < src.u.arr.size; i++) { + toml_datum_t *pelem = arr_emplace(dst, reason); + if (!pelem) { + return -1; + } + DO(datum_copy(pelem, src.u.arr.elem[i], sm, reason)); + } + return 0; + } + // fallthru + default: + break; + } + datum_free(dst); + return datum_copy(dst, src, sm, reason); +} + +// Compare the content of a and b. +static bool datum_equiv(toml_datum_t a, toml_datum_t b) { + if (a.type != b.type) { + return false; + } + int N; + switch (a.type) { + case TOML_STRING: + return a.u.str.len == b.u.str.len && + 0 == memcmp(a.u.str.ptr, b.u.str.ptr, a.u.str.len); + case TOML_INT64: + return a.u.int64 == b.u.int64; + case TOML_FP64: + return a.u.fp64 == b.u.fp64 || (isnan(a.u.fp64) && isnan(b.u.fp64)); + case TOML_BOOLEAN: + return !!a.u.boolean == !!b.u.boolean; + case TOML_DATE: + return a.u.ts.year == b.u.ts.year && a.u.ts.month == b.u.ts.month && + a.u.ts.day == b.u.ts.day; + case TOML_TIME: + return a.u.ts.hour == b.u.ts.hour && a.u.ts.minute == b.u.ts.minute && + a.u.ts.second == b.u.ts.second && a.u.ts.usec == b.u.ts.usec; + case TOML_DATETIME: + return a.u.ts.year == b.u.ts.year && a.u.ts.month == b.u.ts.month && + a.u.ts.day == b.u.ts.day && a.u.ts.hour == b.u.ts.hour && + a.u.ts.minute == b.u.ts.minute && a.u.ts.second == b.u.ts.second && + a.u.ts.usec == b.u.ts.usec; + case TOML_DATETIMETZ: + return a.u.ts.year == b.u.ts.year && a.u.ts.month == b.u.ts.month && + a.u.ts.day == b.u.ts.day && a.u.ts.hour == b.u.ts.hour && + a.u.ts.minute == b.u.ts.minute && a.u.ts.second == b.u.ts.second && + a.u.ts.usec == b.u.ts.usec && a.u.ts.tz == b.u.ts.tz; + case TOML_ARRAY: + N = a.u.arr.size; + if (N != b.u.arr.size) { + return false; + } + for (int i = 0; i < N; i++) { + if (!datum_equiv(a.u.arr.elem[i], b.u.arr.elem[i])) { + return false; + } + } + return true; + case TOML_TABLE: + // Tables are unordered maps: match each key of a against any key of b, + // not positionally. Equal sizes plus a full one-way key match implies a + // bijection because keys are unique within a table. + N = a.u.tab.size; + if (N != b.u.tab.size) { + return false; + } + for (int i = 0; i < N; i++) { + int len = a.u.tab.len[i]; + int j = 0; + for (; j < N; j++) { + if (len == b.u.tab.len[j] && + 0 == memcmp(a.u.tab.key[i], b.u.tab.key[j], len)) { + break; + } + } + if (j == N) { + return false; + } + if (!datum_equiv(a.u.tab.value[i], b.u.tab.value[j])) { + return false; + } + } + return true; + default: + break; + } + return false; +} + +/** + * Override values in r1 using r2. Return a new result. All results + * (i.e., r1, r2 and the returned result) must be freed using toml_free() + * after use. + * + * LOGIC: + * ret = copy of r1 + * for each item x in r2: + * if x is not in ret: + * override + * elif x in ret is NOT of the same type: + * override + * elif x is an array of tables: + * append r2.x to ret.x + * elif x is a table: + * merge r2.x to ret.x + * else: + * override + */ +toml_result_t toml_merge(const toml_result_t *r1, const toml_result_t *r2) { + const char *reason = ""; + toml_result_t ret = {0}; + pool_t *pool = 0; + srcmap_t sm = {0}; + if (!r1->ok) { + reason = "param error: r1 not ok"; + goto bail; + } + if (!r2->ok) { + reason = "param error: r2 not ok"; + goto bail; + } + pool = pool_create(); + if (!pool) { + reason = "out of memory"; + goto bail; + } + // sm is scoped to this single merge call: its olds[]/news[] memo arrays + // are just bookkeeping to dedup source-name pointers while copying below, + // and are freed once the copy/merge is done. The deduplicated string + // copies they produced live on in `pool`, which becomes the result's pool. + sm.pool = pool; + + // Make a copy of r1 + if (datum_copy(&ret.toptab, r1->toptab, &sm, &reason)) { + goto bail; + } + + // Merge r2 into the result + if (datum_merge(&ret.toptab, r2->toptab, &sm, &reason)) { + goto bail; + } + + cell_free((char *)(void *)sm.olds); + cell_free((char *)(void *)sm.news); + ret.ok = 1; + ret.__internal = pool; + return ret; + +bail: + cell_free((char *)(void *)sm.olds); + cell_free((char *)(void *)sm.news); + pool_destroy(pool); + snprintf(ret.errmsg, sizeof(ret.errmsg), "%s", reason); + return ret; +} + +bool toml_equiv(const toml_result_t *r1, const toml_result_t *r2) { + if (!(r1->ok && r2->ok)) { + return false; + } + return datum_equiv(r1->toptab, r2->toptab); +} + +/** + * Find a key in a toml_table. Return the value of the key if found, + * or a TOML_UNKNOWN otherwise. + */ +toml_datum_t toml_get(toml_datum_t datum, const char *key) { + if (datum.type == TOML_TABLE) { + int n = datum.u.tab.size; + if (n > 0) { + int key_len = strlen(key); + const char **pkey = datum.u.tab.key; + int *plen = datum.u.tab.len; + toml_datum_t *pvalue = datum.u.tab.value; + for (int i = 0; i < n; i++) { + if (plen[i] == key_len && 0 == memcmp(pkey[i], key, key_len)) { + return pvalue[i]; + } + } + } + } + return DATUM_ZERO; +} + +/** + * Locate a value starting from a toml_table. Return the value of the key if + * found, or a TOML_UNKNOWN otherwise. + * + * Note: the multipart-key is separated by DOT, and must not have any escape + * chars. + */ +toml_datum_t toml_seek(toml_datum_t table, const char *multipart_key) { + if (table.type != TOML_TABLE) { + return DATUM_ZERO; + } + + // Make a mutable copy of the multipart_key for splitting + char buf[256]; + if (copystring(buf, sizeof(buf), multipart_key)) { + // if the multipart_key is longer than buffer, just + // signal a not-found. + return DATUM_ZERO; + } + + // Go through the multipart name part by part. + char *p = buf; + toml_datum_t datum = table; + while (datum.type == TOML_TABLE) { + char *q = strchr(p, '.'); + if (q) { + // traverse to next key + *q = 0; + datum = toml_get(datum, p); + p = q + 1; + continue; + } + + // At end of last keypart. + // look up p in the final table + return toml_get(datum, p); + } + + return DATUM_ZERO; +} + +/** + * Return the default options. + */ +toml_option_t toml_default_option(void) { + toml_option_t opt = {0, realloc, free}; + return opt; +} + +/** + * Override the current options. + */ +void toml_set_option(toml_option_t opt) { toml_option = opt; } + +/** + * Free the result returned by toml_parse(). + */ +void toml_free(toml_result_t result) { + datum_free(&result.toptab); + pool_destroy((pool_t *)result.__internal); +} + +/** + * Parse a toml document. + */ +toml_result_t toml_parse_file_ex(const char *fname) { + toml_result_t result = {0}; + FILE *fp = fopen(fname, "r"); + if (!fp) { + snprintf(result.errmsg, sizeof(result.errmsg), "fopen %s: %s", fname, + strerror(errno)); + return result; + } + result = toml_parse_file_named(fp, fname); + fclose(fp); + return result; +} + +toml_result_t toml_parse_file(FILE *fp) { + return toml_parse_file_named(fp, NULL); +} + +/** + * Parse a toml document from a file, tagging datums with name. + */ +toml_result_t toml_parse_file_named(FILE *fp, const char *name) { + toml_result_t result = {0}; + if (!fp) { + snprintf(result.errmsg, sizeof(result.errmsg), "fp is NULL"); + return result; + } + char *buf = 0; + int top = 0; // number of bytes read into buf[] + enum { CHUNKSZ = 8 * 1024 }; // bytes to read per iteration + + // Read file into memory. cell_realloc handles capacity growth, so we only + // need to ask for room for one more chunk each pass. Drive the loop off + // fread's return value rather than feof(): feof() only reports true after + // a read has already hit EOF. + for (;;) { + if (top > INT_MAX - CHUNKSZ) { + snprintf(result.errmsg, sizeof(result.errmsg), "file is too big"); + break; + } + char *tmp = cell_realloc(buf, top + CHUNKSZ); + if (!tmp) { + snprintf(result.errmsg, sizeof(result.errmsg), "out of memory"); + break; + } + buf = tmp; + + errno = 0; + size_t n = fread(buf + top, 1, CHUNKSZ, fp); + top += n; + if (n < CHUNKSZ) { // short read => EOF or error + if (ferror(fp)) { + snprintf(result.errmsg, sizeof(result.errmsg), "%s", + errno ? strerror(errno) : "Error reading file"); + break; + } + if (feof(fp)) { + break; + } + // small probability of short read due to signal, etc. + } + } + // error? + if (result.errmsg[0]) { + cell_free(buf); + return result; + } + result = toml_parse_named(buf, top, name); + cell_free(buf); + return result; +} + +static void set_source_recursive(toml_datum_t *datum, const char *source) { + datum->source = source; + switch (datum->type) { + case TOML_ARRAY: + for (int i = 0, top = datum->u.arr.size; i < top; i++) { + set_source_recursive(&datum->u.arr.elem[i], source); + } + break; + case TOML_TABLE: + for (int i = 0, top = datum->u.tab.size; i < top; i++) { + set_source_recursive(&datum->u.tab.value[i], source); + } + break; + default: + break; + } +} + +/** + * Parse a toml document. + */ +toml_result_t toml_parse(const char *src, int len) { + return toml_parse_named(src, len, NULL); +} + +toml_result_t toml_parse_named(const char *src, int len, const char *name) { + toml_result_t result = {0}; + parser_t parser = {0}; + parser_t *pp = &parser; + + // Check that src is not NULL. + if (!src) { + snprintf(result.errmsg, sizeof(result.errmsg), "src is NULL"); + goto bail; + } + + // A UTF-8 BOM at the very start is an encoding artifact rather than content, + // so drop it before scanning. Everywhere else U+FEFF stays exactly as it is + // and remains an error, which is what bom-not-at-start expects. + if (len >= 3 && (unsigned char)src[0] == 0xEF && + (unsigned char)src[1] == 0xBB && (unsigned char)src[2] == 0xBF) { + src += 3; + len -= 3; + } + + // If user insists, check that src[] is a valid utf8 string. + if (toml_option.check_utf8) { + int line = 1; // keeps track of line number + for (int i = 0; i < len;) { + uint32_t ch; + int n = utf8_to_ucs(src + i, len - i, &ch); + if (n < 0) { + snprintf(result.errmsg, sizeof(result.errmsg), + "invalid UTF8 char on line %d", line); + goto bail; + } + if (0xD800 <= ch && ch <= 0xDFFF) { + // explicitly prohibit surrogates (non-scalar unicode code point) + snprintf(result.errmsg, sizeof(result.errmsg), + "invalid UTF8 char \\u%04x on line %d", ch, line); + goto bail; + } + line += (ch == '\n' ? 1 : 0); + i += n; + } + } + + // Initialize parser + pp->toptab = mkdatum_at(TOML_TABLE, 1, 1); + pp->curtab = &pp->toptab; + pp->ebuf.ptr = result.errmsg; // parse error will be printed into pp->ebuf + pp->ebuf.len = sizeof(result.errmsg); + + // Alloc memory pool + int namelen = name ? (int)strlen(name) + 1 : 0; + pp->pool = pool_create(); + if (!pp->pool) { + snprintf(result.errmsg, sizeof(result.errmsg), "out of memory"); + goto bail; + } + if (name) { + char *p = pool_alloc(pp->pool, namelen); + if (!p) { + snprintf(result.errmsg, sizeof(result.errmsg), "out of memory"); + goto bail; + } + memcpy(p, name, namelen); + pp->srcname = p; + } + + // Initialize scanner. Scan error will be printed into pp->ebuf. + scan_init(&pp->scanner, src, len, pp->ebuf.ptr, pp->ebuf.len); + + // Keep parsing until FIN + for (;;) { + token_t tok; + if (scan_key(&pp->scanner, &tok)) { + goto bail; + } + // break on FIN + if (tok.toktyp == TOK_FIN) { + break; + } + switch (tok.toktyp) { + case TOK_ENDL: // skip blank lines + continue; + case TOK_LBRACK: + if (parse_std_table_expr(pp, tok)) { + goto bail; + } + break; + case TOK_LLBRACK: + if (parse_array_table_expr(pp, tok)) { + goto bail; + } + break; + default: + // non-blank line: parse an expression + if (parse_keyvalue_expr(pp, tok)) { + goto bail; + } + break; + } + // each expression must be followed by newline + if (scan_key(&pp->scanner, &tok)) { + goto bail; + } + if (tok.toktyp == TOK_FIN || tok.toktyp == TOK_ENDL) { + continue; + } + SETERROR(pp->ebuf, tok.lineno, "ENDL expected"); + goto bail; + } + + // return result + result.ok = true; + set_source_recursive(&pp->toptab, pp->srcname); + result.toptab = pp->toptab; + result.__internal = (void *)pp->pool; + return result; + +bail: + // return error + datum_free(&pp->toptab); + pool_destroy(pp->pool); + result.ok = false; + if (result.errmsg[0] == '\0') { + assert(0); + snprintf(result.errmsg, sizeof(result.errmsg), "Error near line %d\n", + pp->scanner.lineno); + } + return result; +} + +// Convert a (LITSTRING, LIT, MLLITSTRING, MLSTRING, or STRING) token to a +// datum. +static int token_to_string(parser_t *pp, token_t tok, toml_datum_t *ret) { + *ret = mkdatum_at(TOML_STRING, tok.lineno, tok.colno); + span_t span; + DO(parse_norm(pp, tok, &span)); + ret->u.str.ptr = (char *)span.ptr; + ret->u.str.len = span.len; + return 0; +} + +// Convert a TIME/DATE/DATETIME/DATETIMETZ to a datum +static int token_to_timestamp(parser_t *pp, token_t tok, toml_datum_t *ret) { + (void)pp; + static const toml_type_t map[] = {[TOK_TIME] = TOML_TIME, + [TOK_DATE] = TOML_DATE, + [TOK_DATETIME] = TOML_DATETIME, + [TOK_DATETIMETZ] = TOML_DATETIMETZ}; + switch (tok.toktyp) { + case TOK_TIME: + case TOK_DATE: + case TOK_DATETIME: + case TOK_DATETIMETZ: + break; + default: + assert(0 && "unexpected token type"); + return -1; + } + + *ret = mkdatum_at(map[tok.toktyp], tok.lineno, tok.colno); + ret->u.ts.year = tok.u.tsval.year; + ret->u.ts.month = tok.u.tsval.month; + ret->u.ts.day = tok.u.tsval.day; + ret->u.ts.hour = tok.u.tsval.hour; + ret->u.ts.minute = tok.u.tsval.minute; + ret->u.ts.second = tok.u.tsval.sec; + ret->u.ts.usec = tok.u.tsval.usec; + ret->u.ts.tz = tok.u.tsval.tz; + return 0; +} + +// Convert an int64 token to a datum. +static int token_to_int64(parser_t *pp, token_t tok, toml_datum_t *ret) { + (void)pp; + assert(tok.toktyp == TOK_INTEGER); + *ret = mkdatum_at(TOML_INT64, tok.lineno, tok.colno); + ret->u.int64 = tok.u.int64; + return 0; +} + +// Convert a fp64 token to a datum. +static int token_to_fp64(parser_t *pp, token_t tok, toml_datum_t *ret) { + (void)pp; + assert(tok.toktyp == TOK_FLOAT); + *ret = mkdatum_at(TOML_FP64, tok.lineno, tok.colno); + ret->u.fp64 = tok.u.fp64; + return 0; +} + +// Convert a boolean token to a datum. +static int token_to_boolean(parser_t *pp, token_t tok, toml_datum_t *ret) { + (void)pp; + assert(tok.toktyp == TOK_BOOL); + *ret = mkdatum_at(TOML_BOOLEAN, tok.lineno, tok.colno); + ret->u.boolean = tok.u.b1; + return 0; +} + +// Parse a multipart key. Return 0 on success, -1 otherwise. +static int parse_key(parser_t *pp, token_t tok, keypart_t *ret_keypart) { + ret_keypart->nspan = 0; + // key = simple-key | dotted_key + // simple-key = STRING | LITSTRING | LIT + // dotted-key = simple-key (DOT simple-key)+ + if (tok.toktyp != TOK_STRING && tok.toktyp != TOK_LITSTRING && + tok.toktyp != TOK_LIT) { + return SETERROR(pp->ebuf, tok.lineno, "missing key"); + } + + int n = 0; + span_t *kpspan = ret_keypart->span; + + // Normalize the first keypart + if (parse_norm(pp, tok, &kpspan[n])) { + return SETERROR(pp->ebuf, tok.lineno, + "unable to normalize string; probably a unicode issue"); + } + n++; + + // Scan and normalize the second to last keypart + while (1) { + scanner_state_t mark = scan_mark(&pp->scanner); + + // Eat the dot if it is there + DO(scan_key(&pp->scanner, &tok)); + + // If not a dot, we are done with keyparts. + if (tok.toktyp != TOK_DOT) { + scan_restore(&pp->scanner, mark); + break; + } + + // Scan the n-th key + DO(scan_key(&pp->scanner, &tok)); + + if (tok.toktyp != TOK_STRING && tok.toktyp != TOK_LITSTRING && + tok.toktyp != TOK_LIT) { + return SETERROR(pp->ebuf, tok.lineno, "expects a string in dotted-key"); + } + + if (n >= KEYPARTMAX) { + return SETERROR(pp->ebuf, tok.lineno, "too many key parts"); + } + + // Normalize the n-th key. + DO(parse_norm(pp, tok, &kpspan[n])); + n++; + } + + // This key has n parts. + ret_keypart->nspan = n; + return 0; +} + +// Starting at toptab, descend following keypart[]. If a key does not +// exist in the current table, create a new table entry for the +// key. Returns the final table represented by the key. +static toml_datum_t *descend_keypart(parser_t *pp, int lineno, int colno, + toml_datum_t *toptab, keypart_t *keypart, + bool stdtabexpr) { + toml_datum_t *tab = toptab; // current tab + + for (int i = 0; i < keypart->nspan; i++) { + const char *reason; + // Find the i-th keypart + int j = tab_find(tab, keypart->span[i]); + // Not found: add a new (key, tab) pair. + if (j < 0) { + toml_datum_t newtab = mkdatum_at(TOML_TABLE, lineno, colno); + newtab.flag |= stdtabexpr ? TOML_FLAG_STDEXPR : 0; + if (tab_add(tab, keypart->span[i], newtab, &reason)) { + SETERROR(pp->ebuf, lineno, "%s", reason); + return NULL; + } + tab = &tab->u.tab.value[tab->u.tab.size - 1]; // descend + continue; + } + + // Found: extract the value of the key. + toml_datum_t *value = &tab->u.tab.value[j]; + + // If the value is a table, descend. + if (value->type == TOML_TABLE) { + tab = value; // descend + continue; + } + + // If the value is an array: locate the last entry and descend. + if (value->type == TOML_ARRAY) { + // If empty: error. + if (value->u.arr.size <= 0) { + SETERROR(pp->ebuf, lineno, "array %s has no elements", + keypart->span[i].ptr); + return NULL; + } + + // Extract the last element of the array. + value = &value->u.arr.elem[value->u.arr.size - 1]; + + // It must be a table! + if (value->type != TOML_TABLE) { + SETERROR(pp->ebuf, lineno, "array %s must be array of tables", + keypart->span[i].ptr); + return NULL; + } + tab = value; // descend + continue; + } + + // key not found + SETERROR(pp->ebuf, lineno, "cannot locate table at key %s", + keypart->span[i].ptr); + return NULL; + } + + // Return the table corresponding to the keypart[]. + return tab; +} + +// Recursively set flags on datum +static void set_flag_recursive(toml_datum_t *datum, uint32_t flag) { + datum->flag |= flag; + switch (datum->type) { + case TOML_ARRAY: + for (int i = 0, top = datum->u.arr.size; i < top; i++) { + set_flag_recursive(&datum->u.arr.elem[i], flag); + } + break; + case TOML_TABLE: + for (int i = 0, top = datum->u.tab.size; i < top; i++) { + set_flag_recursive(&datum->u.tab.value[i], flag); + } + break; + default: + break; + } +} + +// Parse an inline array. +static int parse_inline_array(parser_t *pp, token_t tok, + toml_datum_t *ret_datum) { + assert(tok.toktyp == TOK_LBRACK); + *ret_datum = mkdatum_at(TOML_ARRAY, tok.lineno, tok.colno); + int need_comma = 0; + + // loop until RBRACK + for (;;) { + // skip ENDL + do { + DO(scan_value(&pp->scanner, &tok)); + } while (tok.toktyp == TOK_ENDL); + + // If got an RBRACK: done! + if (tok.toktyp == TOK_RBRACK) { + break; + } + + // If got a COMMA: check if it is expected. + if (tok.toktyp == TOK_COMMA) { + if (need_comma) { + need_comma = 0; + continue; + } + return SETERROR(pp->ebuf, tok.lineno, + "syntax error while parsing array: unexpected comma"); + } + + // Not a comma, but need a comma: error! + if (need_comma) { + return SETERROR(pp->ebuf, tok.lineno, + "syntax error while parsing array: missing comma"); + } + + // This is a valid value! Obtain the value. + toml_datum_t value = DATUM_ZERO; + if (parse_val(pp, tok, &value)) { + datum_free(&value); + return -1; + } + + // Add the value to the array. + const char *reason; + toml_datum_t *pelem = arr_emplace(ret_datum, &reason); + if (!pelem) { + datum_free(&value); + return SETERROR(pp->ebuf, tok.lineno, "while parsing array: %s", reason); + } + *pelem = value; + + // Need comma before the next value. + need_comma = 1; + } + + // Set the INLINE flag for all things in this array. + set_flag_recursive(ret_datum, TOML_FLAG_INLINED); + return 0; +} + +// Parse an inline table. +static int parse_inline_table(parser_t *pp, token_t tok, + toml_datum_t *ret_datum) { + assert(tok.toktyp == TOK_LBRACE); + *ret_datum = mkdatum_at(TOML_TABLE, tok.lineno, tok.colno); + bool need_comma = 0; + bool was_comma = 0; + + // loop until RBRACE + for (;;) { + DO(scan_key(&pp->scanner, &tok)); + + // Got an RBRACE: done! + if (tok.toktyp == TOK_RBRACE) { + if (was_comma) { + /* + return SETERROR(pp->ebuf, tok.lineno, + "extra comma before closing brace"); + */ + // extra comma before RBRACE is allowed for v1.1 + (void)0; + } + break; + } + + // Got a comma: check if it is expected. + if (tok.toktyp == TOK_COMMA) { + if (need_comma) { + need_comma = 0, was_comma = 1; + continue; + } + return SETERROR(pp->ebuf, tok.lineno, "unexpected comma"); + } + + // Newline not allowed in inline table. + // newline is allowed in v1.1 + if (tok.toktyp == TOK_ENDL) { + // return SETERROR(pp->ebuf, tok.lineno, "unexpected newline"); + continue; + } + + // Not a comma, but need a comma: error! + if (need_comma) { + return SETERROR(pp->ebuf, tok.lineno, "missing comma"); + } + + // Get the keyparts + keypart_t keypart = {0}; + int keylineno = tok.lineno; + int keycolno = tok.colno; + DO(parse_key(pp, tok, &keypart)); + + // Descend to one keypart before last + assert(keypart.nspan > 0); + span_t lastkeypart = keypart.span[--keypart.nspan]; + toml_datum_t *tab = + descend_keypart(pp, keylineno, keycolno, ret_datum, &keypart, false); + if (!tab) { + return -1; + } + + // If tab is a previously declared inline table: error. + if (tab->flag & TOML_FLAG_INLINED) { + return SETERROR(pp->ebuf, tok.lineno, "inline table cannot be extended"); + } + + // We are explicitly defining it now. + tab->flag |= TOML_FLAG_EXPLICIT; + + // match EQUAL + DO(scan_value(&pp->scanner, &tok)); + + if (tok.toktyp != TOK_EQUAL) { + if (tok.toktyp == TOK_ENDL) { + return SETERROR(pp->ebuf, tok.lineno, "unexpected newline"); + } else { + return SETERROR(pp->ebuf, tok.lineno, "missing '='"); + } + } + + // obtain the value + DO(scan_value(&pp->scanner, &tok)); + toml_datum_t value = DATUM_ZERO; + if (parse_val(pp, tok, &value)) { + datum_free(&value); + return -1; + } + + // Add the value to tab. + const char *reason; + if (tab_add(tab, lastkeypart, value, &reason)) { + datum_free(&value); + return SETERROR(pp->ebuf, tok.lineno, "%s", reason); + } + need_comma = 1, was_comma = 0; + } + + set_flag_recursive(ret_datum, TOML_FLAG_INLINED); + return 0; +} + +// Parse a value. +static int parse_val(parser_t *pp, token_t tok, toml_datum_t *ret) { + *ret = DATUM_ZERO; // initialize + + // val = string / boolean / array / inline-table / date-time / float / integer + switch (tok.toktyp) { + case TOK_STRING: + case TOK_MLSTRING: + case TOK_LITSTRING: + case TOK_MLLITSTRING: + return token_to_string(pp, tok, ret); + case TOK_TIME: + case TOK_DATE: + case TOK_DATETIME: + case TOK_DATETIMETZ: + return token_to_timestamp(pp, tok, ret); + case TOK_INTEGER: + return token_to_int64(pp, tok, ret); + case TOK_FLOAT: + return token_to_fp64(pp, tok, ret); + case TOK_BOOL: + return token_to_boolean(pp, tok, ret); + case TOK_LBRACK: // inline-array + return parse_inline_array(pp, tok, ret); + case TOK_LBRACE: // inline-table + return parse_inline_table(pp, tok, ret); + default: + return SETERROR(pp->ebuf, tok.lineno, "missing value"); + } +} + +// Parse a standard table expression, and set the curtab of the parser +// to the table referenced. A standard table expression is a line +// like [a.b.c.d]. +static int parse_std_table_expr(parser_t *pp, token_t tok) { + // std-table = [ key ] + // Eat the [ + assert(tok.toktyp == TOK_LBRACK); // [ ate by caller + + // Read the first keypart + DO(scan_key(&pp->scanner, &tok)); + + // Extract the keypart[] + int keylineno = tok.lineno; + int keycolno = tok.colno; + keypart_t keypart; + DO(parse_key(pp, tok, &keypart)); + + // Eat the ] + DO(scan_key(&pp->scanner, &tok)); + if (tok.toktyp != TOK_RBRACK) { + return SETERROR(pp->ebuf, tok.lineno, "missing right-bracket"); + } + + // Descend to one keypart before last. + span_t lastkeypart = keypart.span[--keypart.nspan]; + + // Descend keypart from the toptab. + toml_datum_t *tab = + descend_keypart(pp, keylineno, keycolno, &pp->toptab, &keypart, true); + if (!tab) { + return -1; + } + + // Look for the last keypart in the final tab + int j = tab_find(tab, lastkeypart); + if (j < 0) { + // If not found: add it. + if (tab->flag & TOML_FLAG_INLINED) { + return SETERROR(pp->ebuf, keylineno, "inline table cannot be extended"); + } + const char *reason; + toml_datum_t newtab = mkdatum_at(TOML_TABLE, keylineno, keycolno); + newtab.flag |= TOML_FLAG_STDEXPR; + if (tab_add(tab, lastkeypart, newtab, &reason)) { + return SETERROR(pp->ebuf, keylineno, "%s", reason); + } + // this is the new tab + tab = &tab->u.tab.value[tab->u.tab.size - 1]; + } else { + // Found: check for errors + tab = &tab->u.tab.value[j]; + if (tab->flag & TOML_FLAG_EXPLICIT) { + /* + This is not OK: + [x.y.z] + [x.y.z] + + but this is OK: + [x.y.z] + [x] + */ + return SETERROR(pp->ebuf, keylineno, "table defined more than once"); + } + if (!(tab->flag & TOML_FLAG_STDEXPR)) { + /* + [t1] # OK + t2.t3.v = 0 # OK + [t1.t2] # should FAIL - t2 was non-explicit but was not + created by std-table-expr + */ + return SETERROR(pp->ebuf, keylineno, "table defined before"); + } + } + + // Set explicit flag on tab + tab->flag |= TOML_FLAG_EXPLICIT; + tab->lineno = keylineno; + tab->colno = keycolno; + + // Set tab as curtab of the parser + pp->curtab = tab; + return 0; +} + +// Parse an array table expression, and set the curtab of the parser +// to the table referenced. A standard array table expression is a line +// like [[a.b.c.d]]. +static int parse_array_table_expr(parser_t *pp, token_t tok) { + // array-table = [[ key ]] + assert(tok.toktyp == TOK_LLBRACK); // [[ ate by caller + + // Read the first keypart + DO(scan_key(&pp->scanner, &tok)); + + int keylineno = tok.lineno; + int keycolno = tok.colno; + keypart_t keypart; + DO(parse_key(pp, tok, &keypart)); + + // eat the ]] + token_t rrb; + DO(scan_key(&pp->scanner, &rrb)); + if (rrb.toktyp != TOK_RRBRACK) { + return SETERROR(pp->ebuf, rrb.lineno, "missing ']]'"); + } + + // remove the last keypart from keypart[] + assert(keypart.nspan > 0); + span_t lastkeypart = keypart.span[--keypart.nspan]; + + // descend intermediate keys from toptab + toml_datum_t *tab = + descend_keypart(pp, keylineno, keycolno, &pp->toptab, &keypart, true); + if (!tab) + return -1; + + // For the final keypart, make sure entry at key is an array of tables + const char *reason; + int idx = tab_find(tab, lastkeypart); + if (idx == -1) { + // If not found, add an array of table. + if (tab->flag & TOML_FLAG_INLINED) { + return SETERROR(pp->ebuf, keylineno, "inline table cannot be extended"); + } + toml_datum_t newarr = mkdatum_at(TOML_ARRAY, keylineno, keycolno); + if (tab_add(tab, lastkeypart, newarr, &reason)) { + return SETERROR(pp->ebuf, keylineno, "%s", reason); + } + idx = tab_find(tab, lastkeypart); + assert(idx >= 0); + } + // Check that this is an array. + if (tab->u.tab.value[idx].type != TOML_ARRAY) { + return SETERROR(pp->ebuf, keylineno, "entry must be an array"); + } + // Add an empty table to the array + toml_datum_t *arr = &tab->u.tab.value[idx]; + if (arr->flag & TOML_FLAG_INLINED) { + return SETERROR(pp->ebuf, keylineno, "cannot extend a static array"); + } + toml_datum_t *pelem = arr_emplace(arr, &reason); + if (!pelem) { + return SETERROR(pp->ebuf, keylineno, "%s", reason); + } + *pelem = mkdatum_at(TOML_TABLE, keylineno, keycolno); + + // Set the last element of this array as curtab of the parser + pp->curtab = &arr->u.arr.elem[arr->u.arr.size - 1]; + assert(pp->curtab->type == TOML_TABLE); + + return 0; +} + +// Parse an expression. A toml doc is just a list of expressions. +static int parse_keyvalue_expr(parser_t *pp, token_t tok) { + // Obtain the key + int keylineno = tok.lineno; + int keycolno = tok.colno; + keypart_t keypart; + DO(parse_key(pp, tok, &keypart)); + + // match the '=' + DO(scan_key(&pp->scanner, &tok)); + if (tok.toktyp != TOK_EQUAL) { + return SETERROR(pp->ebuf, tok.lineno, "expect '='"); + } + + // Locate the last table using keypart[] + const char *reason; + toml_datum_t *tab = pp->curtab; + for (int i = 0; i < keypart.nspan - 1; i++) { + int j = tab_find(tab, keypart.span[i]); + if (j < 0) { + if (i > 0 && (tab->flag & TOML_FLAG_EXPLICIT)) { + return SETERROR( + pp->ebuf, keylineno, + "cannot extend a previously defined table using dotted expression"); + } + toml_datum_t newtab = mkdatum_at(TOML_TABLE, keylineno, keycolno); + if (tab_add(tab, keypart.span[i], newtab, &reason)) { + return SETERROR(pp->ebuf, keylineno, "%s", reason); + } + tab = &tab->u.tab.value[tab->u.tab.size - 1]; + continue; + } + toml_datum_t *value = &tab->u.tab.value[j]; + if (value->type == TOML_TABLE) { + tab = value; + continue; + } + if (value->type == TOML_ARRAY) { + return SETERROR(pp->ebuf, keylineno, + "encountered previously declared array '%s'", + keypart.span[i].ptr); + } + return SETERROR(pp->ebuf, keylineno, "cannot locate table at '%s'", + keypart.span[i].ptr); + } + + // Check for disallowed situations. + if (tab->flag & TOML_FLAG_INLINED) { + return SETERROR(pp->ebuf, keylineno, "inline table cannot be extended"); + } + if (keypart.nspan > 1 && (tab->flag & TOML_FLAG_EXPLICIT)) { + return SETERROR( + pp->ebuf, keylineno, + "cannot extend a previously defined table using dotted expression"); + } + + // Obtain the value + DO(scan_value(&pp->scanner, &tok)); + toml_datum_t newval = DATUM_ZERO; + if (parse_val(pp, tok, &newval)) { + datum_free(&newval); + return -1; + } + + // Add a new key/value for tab. + if (tab_add(tab, keypart.span[keypart.nspan - 1], newval, &reason)) { + datum_free(&newval); + return SETERROR(pp->ebuf, keylineno, "%s", reason); + } + + return 0; +} + +// Normalize a LIT/STRING/MLSTRING/LITSTRING/MLLITSTRING +// -> unescape all escaped chars +// The returned string is allocated out of pp->pool +static int parse_norm(parser_t *pp, token_t tok, span_t *ret_span) { + // Allocate a buffer to store the normalized string. Add one + // extra-byte for terminating NUL. + char *p = pool_alloc(pp->pool, tok.str.len + 1); + if (!p) { + return SETERROR(pp->ebuf, tok.lineno, "out of memory"); + } + + // Copy from token string into buffer + memcpy(p, tok.str.ptr, tok.str.len); + p[tok.str.len] = 0; // additional NUL term for safety + + ret_span->ptr = p; + ret_span->len = tok.str.len; + + switch (tok.toktyp) { + case TOK_LIT: + case TOK_LITSTRING: + case TOK_MLLITSTRING: + // no need to handle escape chars + return 0; + + case TOK_STRING: + case TOK_MLSTRING: + // need to handle escape chars + break; + + default: + return SETERROR(pp->ebuf, 0, "internal: arg must be a string"); + } + + // if there is no escape char, then done! + if (!tok.u.escp) { + return 0; // success + } + + // p points to the backslash + p += (tok.u.escp - tok.str.ptr); + assert(p - ret_span->ptr == tok.u.escp - tok.str.ptr); + assert(*p == '\\'); + + // Normalize the escaped chars + char *dst = p; + while (*p) { + if (*p != '\\') { + *dst++ = *p++; + continue; + } + switch (p[1]) { + case '"': + case '\\': + *dst++ = p[1]; + p += 2; + continue; + case 'b': + *dst++ = '\b'; + p += 2; + continue; + case 't': + *dst++ = '\t'; + p += 2; + continue; + case 'n': + *dst++ = '\n'; + p += 2; + continue; + case 'f': + *dst++ = '\f'; + p += 2; + continue; + case 'r': + *dst++ = '\r'; + p += 2; + continue; + case 'e': + *dst++ = '\033'; + p += 2; + continue; + case 'x': { + char buf[3]; + memcpy(buf, p + 2, 2); + buf[2] = 0; + // There is no need to check for two hex digits here because + // the scanner already checked it. + int32_t ucs = strtol(buf, 0, 16); + int n = ucs_to_utf8(ucs, dst); + if (n < 0) { + return SETERROR(pp->ebuf, tok.lineno, "error converting UCS %s to UTF8", + buf); + } + dst += n; + p += 2 + 2; // \xNN + continue; + } + case 'u': + case 'U': { + char buf[9]; + int sz = (p[1] == 'u' ? 4 : 8); + memcpy(buf, p + 2, sz); + buf[sz] = 0; + // There is no need to check for 4 or 8 hex digits here because + // the scanner already checked it. + int32_t ucs = strtol(buf, 0, 16); + if (0xD800 <= ucs && ucs <= 0xDFFF) { + // explicitly prohibit surrogates (non-scalar unicode code point) + return SETERROR(pp->ebuf, tok.lineno, "invalid UTF8 char \\u%04x", ucs); + } + int n = ucs_to_utf8(ucs, dst); + if (n < 0) { + return SETERROR(pp->ebuf, tok.lineno, "error converting UCS %s to UTF8", + buf); + } + dst += n; + p += 2 + sz; // \uNNNN or \UNNNNNNNN + continue; + } + + case ' ': + case '\t': + case '\r': + // line-ending backslash + // --- allow for extra whitespace chars after backslash + // --- skip until newline + p++; // skip the escape char + p += strspn(p, " \t\r"); // skip whitespaces + if (*p != '\n') { + return SETERROR(pp->ebuf, tok.lineno, + "unexpected char after line-ending backslash"); + } + // fallthru + case '\n': + // skip all whitespaces including newline + p++; + p += strspn(p, " \t\r\n"); + continue; + default: + return SETERROR(pp->ebuf, tok.lineno, + "internal: unknown escape char \\%c", p[1]); + } + } + *dst = 0; + ret_span->len = dst - ret_span->ptr; + return 0; +} + +// =================================================================== +// == SCANNER SECTION +// =================================================================== + +// Get the next char +static int scan_get(scanner_t *sp) { + int ret = TOK_FIN; + const char *p = sp->cur; + if (p < sp->endp) { + ret = *p++; + if (ret == '\r' && p < sp->endp && *p == '\n') { + ret = *p++; + } + } + sp->cur = p; + if (ret == '\n') { + sp->lineno += 1; + sp->line_start = p; + } + return ret; +} + +// Check if the next char matches ch. +static inline bool scan_match(scanner_t *sp, int ch) { + const char *p = sp->cur; + // exact match? done. + if (p < sp->endp && *p == ch) { + return true; + } + // \n also matches \r\n + if (ch == '\n' && p + 1 < sp->endp) { + return p[0] == '\r' && p[1] == '\n'; + } + // not a match + return false; +} + +// Check if the next char is in accept[]. +static bool scan_matchany(scanner_t *sp, const char *accept) { + for (; *accept; accept++) { + if (scan_match(sp, *accept)) { + return true; + } + } + return false; +} + +// Check if the next n chars match ch. +static inline bool scan_nmatch(scanner_t *sp, int ch, int n) { + assert(ch != '\n'); // not handled + if (sp->cur + n > sp->endp) { + return false; + } + const char *p = sp->cur; + int i; + for (i = 0; i < n && p[i] == ch; i++) + ; + return i == n; +} + +// Initialize a token. +static inline token_t mktoken(scanner_t *sp, toktyp_t typ) { + token_t tok = {0}; + tok.toktyp = typ; + tok.str.ptr = sp->cur; + tok.lineno = sp->lineno; + tok.colno = (int)(sp->cur - sp->line_start) + 1; + return tok; +} + +#define S_GET() scan_get(sp) +#define S_MATCH(ch) scan_match(sp, (ch)) +#define S_MATCH3(ch) scan_nmatch(sp, (ch), 3) +#define S_MATCH4(ch) scan_nmatch(sp, (ch), 4) +#define S_MATCH6(ch) scan_nmatch(sp, (ch), 6) + +static inline bool is_valid_char(int ch) { + // i.e. (0x20 <= ch && ch <= 0x7e) || (ch & 0x80); + return isprint((unsigned char)ch) || (ch & 0x80); +} + +static inline bool is_hex_char(int ch) { + ch = toupper((unsigned char)ch); + return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F'); +} + +// Initialize a scanner +static void scan_init(scanner_t *sp, const char *src, int len, char *errbuf, + int errbufsz) { + memset(sp, 0, sizeof(*sp)); + sp->src = src; + sp->endp = src + len; + sp->cur = src; + sp->lineno = 1; + sp->line_start = src; + sp->ebuf.ptr = errbuf; + sp->ebuf.len = errbufsz; +} + +// Scan escape sequence characters after a backslash. +// Return #char match on success, 0 if no match, -1 on error. +static int scan_escape_chars(scanner_t *sp) { + const char *p = sp->cur; + if (p >= sp->endp) { + return 0; + } + int ch = *p++; + if (ch && strchr("btnfre\"\\", ch)) { + return p - sp->cur; + } + int hex = (ch == 'x') ? 2 : (ch == 'u') ? 4 : (ch == 'U') ? 8 : 0; + if (hex) { + int i = 0; + for (; i < hex && p < sp->endp && is_hex_char(*p); i++, p++) + ; + if (i != hex) { + return SETERROR(sp->ebuf, sp->lineno, "expect %d hex digits after \\%c", + hex, ch); + } + return p - sp->cur; + } + return 0; +} + +// Scan """ ... """ +static int scan_multiline_string(scanner_t *sp, token_t *tok) { + assert(S_MATCH3('"')); + S_GET(), S_GET(), S_GET(); // skip opening """ + + // According to spec: trim first newline after """ + if (S_MATCH('\n')) { + S_GET(); + } + + *tok = mktoken(sp, TOK_MLSTRING); + // scan until terminating """ + const char *escp = NULL; + while (1) { + if (S_MATCH3('"')) { + if (S_MATCH4('"')) { + // special case... """abcd """" -> (abcd ") + // but sequences of 3 or more double quotes are not allowed + if (S_MATCH6('"')) { + return SETERROR(sp->ebuf, sp->lineno, + "detected sequences of 3 or more double quotes"); + } else { + ; // no problem + } + } else { + break; // found terminating """ + } + } + int ch = S_GET(); + if (ch == TOK_FIN) { + return SETERROR(sp->ebuf, sp->lineno, "unterminated \"\"\""); + } + // If non-escaped char ... + if (ch != '\\') { + if (!(is_valid_char(ch) || (ch && strchr(" \t\n", ch)))) { + return SETERROR(sp->ebuf, sp->lineno, "invalid char in string"); + } + continue; + } + // ch is backslash + if (!escp) { + escp = sp->cur - 1; /* mark the first esc position */ + assert(*escp == '\\'); + } + + // handle escape char + int cnt = scan_escape_chars(sp); + if (cnt < 0) { + return -1; + } + if (cnt > 0) { + sp->cur += cnt; // skip the escape sequence + continue; + } + assert(cnt == 0); + + // handle line-ending backslash + ch = S_GET(); + if (ch == ' ' || ch == '\t') { + // Although the spec does not allow for whitespace following a + // line-ending backslash, some standard tests expect it. + // Skip whitespace till EOL. + while (ch != TOK_FIN && ch && strchr(" \t", ch)) { + ch = S_GET(); + } + if (ch != '\n') { + // Got a backslash followed by whitespace, followed by some char + // before newline + return SETERROR(sp->ebuf, sp->lineno, "bad escape char in string"); + } + // fallthru + } + if (ch == '\n') { + // got a line-ending backslash + // - skip all whitespaces + while (scan_matchany(sp, " \t\n")) { + S_GET(); + } + continue; + } + return SETERROR(sp->ebuf, sp->lineno, "bad escape char in string"); + } + tok->str.len = sp->cur - tok->str.ptr; + tok->u.escp = escp; + + assert(S_MATCH3('"')); + S_GET(), S_GET(), S_GET(); + return 0; +} + +// Scan " ... " +static int scan_string(scanner_t *sp, token_t *tok) { + assert(S_MATCH('"')); + if (S_MATCH3('"')) { + return scan_multiline_string(sp, tok); + } + S_GET(); // skip opening " + + // scan until closing " + *tok = mktoken(sp, TOK_STRING); + const char *escp = NULL; + while (!S_MATCH('"')) { + int ch = S_GET(); + if (ch == TOK_FIN) { + return SETERROR(sp->ebuf, sp->lineno, "unterminated string"); + } + // If non-escaped char ... + if (ch != '\\') { + if (!(is_valid_char(ch) || ch == ' ' || ch == '\t')) { + return SETERROR(sp->ebuf, sp->lineno, "invalid char in string"); + } + continue; + } + // ch is backslash + if (!escp) { + escp = sp->cur - 1; + assert(*escp == '\\'); + } + + // handle escape char + int cnt = scan_escape_chars(sp); + if (cnt < 0) { + return -1; + } + if (cnt > 0) { + sp->cur += cnt; + continue; + } + return SETERROR(sp->ebuf, sp->lineno, "bad escape char in string"); + } + tok->str.len = sp->cur - tok->str.ptr; + tok->u.escp = escp; + + assert(S_MATCH('"')); + S_GET(); // skip the terminating " + return 0; +} + +// Scan ''' ... ''' +static int scan_multiline_litstring(scanner_t *sp, token_t *tok) { + assert(S_MATCH3('\'')); + S_GET(), S_GET(), S_GET(); // skip opening ''' + + // According to spec: trim first newline after ''' + if (S_MATCH('\n')) { + S_GET(); + } + + // scan until terminating ''' + *tok = mktoken(sp, TOK_MLLITSTRING); + while (1) { + if (S_MATCH3('\'')) { + if (S_MATCH4('\'')) { + // special case... '''abcd '''' -> (abcd ') + // but sequences of 3 or more single quotes are not allowed + if (S_MATCH6('\'')) { + return SETERROR(sp->ebuf, sp->lineno, + "sequences of 3 or more single quotes"); + } else { + ; // no problem + } + } else { + break; // found terminating ''' + } + } + int ch = S_GET(); + if (ch == TOK_FIN) { + return SETERROR(sp->ebuf, sp->lineno, + "unterminated multiline lit string"); + } + if (!(is_valid_char(ch) || (ch && strchr(" \t\n", ch)))) { + return SETERROR(sp->ebuf, sp->lineno, "invalid char in string"); + } + } + tok->str.len = sp->cur - tok->str.ptr; + + assert(S_MATCH3('\'')); + S_GET(), S_GET(), S_GET(); + return 0; +} + +// Scan ' ... ' +static int scan_litstring(scanner_t *sp, token_t *tok) { + assert(S_MATCH('\'')); + if (S_MATCH3('\'')) { + return scan_multiline_litstring(sp, tok); + } + S_GET(); // skip opening ' + + // scan until closing ' + *tok = mktoken(sp, TOK_LITSTRING); + while (!S_MATCH('\'')) { + int ch = S_GET(); + if (ch == TOK_FIN) { + return SETERROR(sp->ebuf, sp->lineno, "unterminated string"); + } + if (!(is_valid_char(ch) || ch == '\t')) { + return SETERROR(sp->ebuf, sp->lineno, "invalid char in string"); + } + } + tok->str.len = sp->cur - tok->str.ptr; + assert(S_MATCH('\'')); + S_GET(); + return 0; +} + +static bool is_valid_date(int year, int month, int day) { + if (!(1 <= year)) { + return false; + } + if (!(1 <= month && month <= 12)) { + return false; + } + int is_leap_year = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); + int days_in_month[] = { + 31, 28 + is_leap_year, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + return (1 <= day && day <= days_in_month[month - 1]); +} + +static bool is_valid_time(int hour, int minute, int sec, int usec) { + if (!(0 <= hour && hour <= 23)) { + return false; + } + if (!(0 <= minute && minute <= 59)) { + return false; + } + // RFC 3339 permits sec == 60 for leap seconds; reject it deliberately. + if (!(0 <= sec && sec <= 59)) { + return false; + } + if (!(0 <= usec)) { + return false; + } + return true; +} + +static bool is_valid_timezone(int minute) { + minute = (minute < 0 ? -minute : minute); + int hour = minute / 60; + minute = minute % 60; + if (!(0 <= hour && hour <= 23)) { + return false; + } + if (!(0 <= minute && minute < 60)) { + return false; + } + return true; +} + +// Read an int (without signs) from the string p. +static int read_int(const char *p, int *ret) { + const char *pp = p; + int64_t val = 0; + for (; isdigit((unsigned char)*p); p++) { + val = val * 10 + (*p - '0'); + if (val > INT_MAX) { + return 0; // overflowed + } + } + *ret = (int)val; + return p - pp; +} + +// Read a date as YYYY-MM-DD from p[]. Return #bytes consumed. +static int read_date(const char *p, int *year, int *month, int *day) { + const char *pp = p; + int n; + n = read_int(p, year); + if (n != 4 || p[4] != '-') { + return 0; + } + n = read_int(p += n + 1, month); + if (n != 2 || p[2] != '-') { + return 0; + } + n = read_int(p += n + 1, day); + if (n != 2) { + return 0; + } + p += 2; + assert(p - pp == 10); + return p - pp; +} + +// Read a time as HH:MM:SS.subsec from p[]. Return #bytes consumed. +static int read_time(const char *p, int *hour, int *minute, int *second, + int *usec) { + const char *pp = p; + int n; + *hour = *minute = *second = *usec = 0; + // scan hours + n = read_int(p, hour); + if (n != 2 || p[2] != ':') { + return 0; + } + p += 3; + + // scan minutes + n = read_int(p, minute); + if (n != 2) { + return 0; + } + if (p[2] != ':') { + // seconds are optional in v1.1 + p += 2; + return p - pp; + } + p += 3; + + // scan seconds + n = read_int(p, second); + if (n != 2) { + return 0; + } + p += 2; + + if (*p != '.') { + return p - pp; + } + p++; // skip the period + if (!isdigit((unsigned char)*p)) { + // trailing period + return 0; + } + int micro_factor = 100000; + while (isdigit((unsigned char)*p) && micro_factor) { + *usec += (*p - '0') * micro_factor; + micro_factor /= 10; + p++; + } + while (isdigit((unsigned char)*p)) + p++; // consume extra sub-microsecond digits + return p - pp; +} + +// Reads a timezone from p[]. Return #bytes consumed. +// tzhours and tzminutes restricted to 2-char integers only. +static int read_tzone(const char *p, char *tzsign, int *tzhour, int *tzminute) { + const char *pp = p; + + // Default values + *tzhour = *tzminute = 0; + *tzsign = '+'; + + // Look for Zulu + if (*p == 'Z' || *p == 'z') { + return 1; // done! tz is +00:00. + } + + // Look for +/- + *tzsign = *p++; + if (!(*tzsign == '+' || *tzsign == '-')) { + return 0; + } + + // Look for HH:MM + int n; + n = read_int(p, tzhour); + if (n != 2 || p[2] != ':') { + return 0; + } + n = read_int(p += 3, tzminute); + if (n != 2) { + return 0; + } + p += 2; + return p - pp; +} + +// Scan hh:mm:ss.xxxxx +static int scan_time(scanner_t *sp, token_t *tok) { + int lineno = sp->lineno; + char buffer[LITBUF_SIZE]; + scan_copystr(sp, buffer, sizeof(buffer)); + + char *p = buffer; + int hour, minute, sec, usec; + int len = read_time(p, &hour, &minute, &sec, &usec); + if (len == 0) { + return SETERROR(sp->ebuf, lineno, "invalid time"); + } + if (len + 1 >= (int)sizeof(buffer)) { + return SETERROR(sp->ebuf, lineno, "timestamp too long"); + } + if (!is_valid_time(hour, minute, sec, usec)) { + return SETERROR(sp->ebuf, lineno, "invalid time"); + } + + *tok = mktoken(sp, TOK_TIME); + tok->str.len = len; + sp->cur += len; + tok->u.tsval.year = -1; + tok->u.tsval.month = -1; + tok->u.tsval.day = -1; + tok->u.tsval.hour = hour; + tok->u.tsval.minute = minute; + tok->u.tsval.sec = sec; + tok->u.tsval.usec = usec; + tok->u.tsval.tz = -1; + return 0; +} + +// Scan a time, a date, a datetime, or a datatimetz +static int scan_timestamp(scanner_t *sp, token_t *tok) { + int year, month, day, hour, minute, sec, usec, tz; + year = month = day = hour = minute = sec = usec = tz = -1; + + int n; + // make a copy of sp->cur into buffer to ensure NUL terminated string + char buffer[LITBUF_SIZE]; + scan_copystr(sp, buffer, sizeof(buffer)); + + toktyp_t toktyp = TOK_FIN; + int lineno = sp->lineno; + + // See if this a TIME only + const char *p = buffer; + if (isdigit((unsigned char)p[0]) && isdigit((unsigned char)p[1]) && + p[2] == ':') { + n = read_time(buffer, &hour, &minute, &sec, &usec); + if (!n) { + return SETERROR(sp->ebuf, lineno, "invalid time"); + } + toktyp = TOK_TIME; + p += n; + goto done; + } + + // Try reading a DATE + n = read_date(p, &year, &month, &day); + if (!n) { + return SETERROR(sp->ebuf, lineno, "invalid date"); + } + toktyp = TOK_DATE; + p += n; + + // Check if there is no time component in addition + if (!((p[0] == 'T' || p[0] == ' ' || p[0] == 't') && + isdigit((unsigned char)p[1]) && isdigit((unsigned char)p[2]) && + p[3] == ':')) { + goto done; // no TIME component. we are done. + } + + // Read the TIME + n = read_time(p += 1, &hour, &minute, &sec, &usec); + if (!n) { + return SETERROR(sp->ebuf, lineno, "invalid timestamp"); + } + toktyp = TOK_DATETIME; + p += n; + + // Read the (optional) timezone + char tzsign; + int tzhour, tzminute; + n = read_tzone(p, &tzsign, &tzhour, &tzminute); + if (n == 0) { + goto done; // datetime only + } + toktyp = TOK_DATETIMETZ; + p += n; + + // Check tzminute range. This must be done here instead of is_valid_timezone() + // because we combine tzhour and tzminute into tz (by minutes only). + if (!(0 <= tzminute && tzminute < 60)) { + return SETERROR(sp->ebuf, lineno, "invalid timezone"); + } + tz = (tzhour * 60 + tzminute) * (tzsign == '-' ? -1 : 1); + goto done; // datetimetz + +done: + *tok = mktoken(sp, toktyp); + n = p - buffer; + if (n + 1 >= (int)sizeof(buffer)) { + return SETERROR(sp->ebuf, lineno, "timestamp too long"); + } + tok->str.len = n; + sp->cur += n; + + tok->u.tsval.year = year; + tok->u.tsval.month = month; + tok->u.tsval.day = day; + tok->u.tsval.hour = hour; + tok->u.tsval.minute = minute; + tok->u.tsval.sec = sec; + tok->u.tsval.usec = usec; + tok->u.tsval.tz = tz; + + // Do some error checks based on type + switch (tok->toktyp) { + case TOK_TIME: + if (!is_valid_time(hour, minute, sec, usec)) { + return SETERROR(sp->ebuf, lineno, "invalid time"); + } + break; + case TOK_DATE: + if (!is_valid_date(year, month, day)) { + return SETERROR(sp->ebuf, lineno, "invalid date"); + } + break; + case TOK_DATETIME: + case TOK_DATETIMETZ: + if (!is_valid_date(year, month, day)) { + return SETERROR(sp->ebuf, lineno, "invalid date"); + } + if (!is_valid_time(hour, minute, sec, usec)) { + return SETERROR(sp->ebuf, lineno, "invalid time"); + } + if (tok->toktyp == TOK_DATETIMETZ && !is_valid_timezone(tz)) { + return SETERROR(sp->ebuf, lineno, "invalid timezone"); + } + break; + default: + assert(0); + return SETERROR(sp->ebuf, lineno, "internal error"); + } + + return 0; +} + +// Given a toml number (int and float) in buffer[]: +// 1. squeeze out '_' +// 2. check for syntax restrictions +static int process_numstr(char *buffer, int base, const char **reason) { + // squeeze out _ + char *q = strchr(buffer, '_'); + if (q) { + for (int i = q - buffer; buffer[i]; i++) { + if (buffer[i] != '_') { + *q++ = buffer[i]; + continue; + } + int left = (i == 0) ? 0 : (unsigned char)buffer[i - 1]; + int right = (unsigned char)buffer[i + 1]; + if (!isdigit(left) && !(base == 16 && is_hex_char(left))) { + *reason = "underscore only allowed between digits"; + return -1; + } + if (!isdigit(right) && !(base == 16 && is_hex_char(right))) { + *reason = "underscore only allowed between digits"; + return -1; + } + } + *q = 0; + } + + // decimal points must be surrounded by digits. Also, convert to lowercase. + for (int i = 0; buffer[i]; i++) { + if (buffer[i] == '.') { + if (i == 0 || !isdigit((unsigned char)buffer[i - 1]) || + !isdigit((unsigned char)buffer[i + 1])) { + *reason = "decimal point must be surrounded by digits"; + return -1; + } + } else if ('A' <= buffer[i] && buffer[i] <= 'Z') { + buffer[i] = tolower((unsigned char)buffer[i]); + } + } + + if (base == 10) { + // check for leading 0: '+01' is an error! + q = buffer; + q += (*q == '+' || *q == '-') ? 1 : 0; + if (q[0] == '0' && isdigit((unsigned char)q[1])) { + *reason = "leading 0 in numbers"; + return -1; + } + } + + return 0; +} + +static int scan_float(scanner_t *sp, token_t *tok) { + char buffer[LITBUF_SIZE]; + scan_copystr(sp, buffer, sizeof(buffer)); + + int lineno = sp->lineno; + char *p = buffer; + p += (*p == '+' || *p == '-') ? 1 : 0; + if (0 == memcmp(p, "nan", 3) || (0 == memcmp(p, "inf", 3))) { + p += 3; + } else { + p += strspn(p, "_0123456789eE.+-"); + } + int len = p - buffer; + if (len + 1 >= (int)sizeof(buffer)) { + return SETERROR(sp->ebuf, lineno, "number too long"); + } + buffer[len] = 0; + + const char *reason; + if (process_numstr(buffer, 10, &reason)) { + return SETERROR(sp->ebuf, lineno, "%s", reason); + } + + errno = 0; + char *q; + double fp64 = strtod(buffer, &q); + // glibc sets ERANGE on underflow even when strtod's result is correctly + // rounded, e.g. 5e-324; accept such results, but still reject a value that + // underflowed to zero or overflowed to infinity. Test the raw bits rather + // than "fp64 != 0.0" and isfinite(): if the calling program enables + // denormals-are-zero (e.g. an app linked with -ffast-math, or built with + // Intel icx), an FP compare reads a subnormal as 0.0 (issue #49). + static_assert(sizeof(fp64) == sizeof(uint64_t), "double must be 64 bits"); + uint64_t fp64_bits; + memcpy(&fp64_bits, &fp64, sizeof(fp64)); + uint64_t fp64_mag = fp64_bits & 0x7fffffffffffffffULL; // drop the sign bit + int is_ok_subnormal = + (errno == ERANGE) && fp64_mag != 0 && fp64_mag < 0x7ff0000000000000ULL; + if ((errno && !is_ok_subnormal) || *q || q == buffer) { + return SETERROR(sp->ebuf, lineno, "error parsing float"); + } + + *tok = mktoken(sp, TOK_FLOAT); + tok->u.fp64 = fp64; + tok->str.len = len; + sp->cur += len; + return 0; +} + +static int scan_number(scanner_t *sp, token_t *tok) { + const char *reason; + char buffer[LITBUF_SIZE]; + scan_copystr(sp, buffer, sizeof(buffer)); + + char *p = buffer; + int lineno = sp->lineno; + // process %0x, %0o or %0b integers + if (p[0] == '0') { + const char *span = 0; + int base = 0; + switch (p[1]) { + case 'x': + base = 16; + span = "_0123456789abcdefABCDEF"; + break; + case 'o': + base = 8; + span = "_01234567"; + break; + case 'b': + base = 2; + span = "_01"; + break; + } + if (base) { + p += 2; + p += strspn(p, span); + int len = p - buffer; + if (len + 1 >= (int)sizeof(buffer)) { + return SETERROR(sp->ebuf, lineno, "number too long"); + } + buffer[len] = 0; + + if (process_numstr(buffer + 2, base, &reason)) { + return SETERROR(sp->ebuf, lineno, "%s", reason); + } + + // use strtoll to obtain the value + *tok = mktoken(sp, TOK_INTEGER); + char *q; + errno = 0; + tok->u.int64 = strtoll(buffer + 2, &q, base); + if (errno || *q || q == buffer + 2) { + return SETERROR(sp->ebuf, lineno, "error parsing integer"); + } + tok->str.len = len; + sp->cur += len; + return 0; + } + } + + // handle inf/nan + if (*p == '+' || *p == '-') { + p++; + } + if (*p == 'i' || *p == 'n') { + return scan_float(sp, tok); + } + + // regular int or float + p = buffer; + p += strspn(p, "0123456789_+-.eE"); + int len = p - buffer; + if (len + 1 >= (int)sizeof(buffer)) { + return SETERROR(sp->ebuf, lineno, "number too long"); + } + buffer[len] = 0; + + if (process_numstr(buffer, 10, &reason)) { + return SETERROR(sp->ebuf, lineno, "%s", reason); + } + + *tok = mktoken(sp, TOK_INTEGER); + char *q; + errno = 0; + tok->u.int64 = strtoll(buffer, &q, 10); + if (errno || *q || q == buffer) { + if (*q && strchr(".eE", *q)) { + return scan_float(sp, tok); // try to fit a float + } + return SETERROR(sp->ebuf, lineno, "error parsing integer"); + } + + tok->str.len = len; + sp->cur += len; + return 0; +} + +static int scan_bool(scanner_t *sp, token_t *tok) { + char buffer[10]; + scan_copystr(sp, buffer, sizeof(buffer)); + + int lineno = sp->lineno; + bool val = false; + const char *p = buffer; + if (0 == strncmp(p, "true", 4)) { + val = true; + p += 4; + } else if (0 == strncmp(p, "false", 5)) { + val = false; + p += 5; + } else { + return SETERROR(sp->ebuf, lineno, "invalid boolean value"); + } + if (*p && !strchr("# \r\n\t,}]", *p)) { + return SETERROR(sp->ebuf, lineno, "invalid boolean value"); + } + + int len = p - buffer; + *tok = mktoken(sp, TOK_BOOL); + tok->u.b1 = val; + tok->str.len = len; + sp->cur += len; + return 0; +} + +// Check if the next token may be TIME +static inline bool test_time(const char *p, const char *endp) { + return &p[2] < endp && isdigit((unsigned char)p[0]) && + isdigit((unsigned char)p[1]) && p[2] == ':'; +} + +// Check if the next token may be DATE +static inline bool test_date(const char *p, const char *endp) { + return &p[4] < endp && isdigit((unsigned char)p[0]) && + isdigit((unsigned char)p[1]) && isdigit((unsigned char)p[2]) && + isdigit((unsigned char)p[3]) && p[4] == '-'; +} + +// Check if the next token may be BOOL +static inline bool test_bool(const char *p, const char *endp) { + return &p[0] < endp && (*p == 't' || *p == 'f'); +} + +// Check if the next token may be NUMBER +static bool test_number(const char *p, const char *endp) { + if (&p[0] < endp && *p && strchr("0123456789+-._", *p)) { + return true; + } + if (&p[2] < endp) { + if (0 == memcmp(p, "nan", 3) || 0 == memcmp(p, "inf", 3)) { + return true; + } + } + return false; +} + +// Scan a literal that is not a string +static int scan_nonstring_literal(scanner_t *sp, token_t *tok) { + int lineno = sp->lineno; + if (test_time(sp->cur, sp->endp)) { + return scan_time(sp, tok); + } + + if (test_date(sp->cur, sp->endp)) { + return scan_timestamp(sp, tok); + } + + if (test_bool(sp->cur, sp->endp)) { + return scan_bool(sp, tok); + } + + if (test_number(sp->cur, sp->endp)) { + return scan_number(sp, tok); + } + return SETERROR(sp->ebuf, lineno, "invalid value"); +} + +// Return true if Unicode codepoint is allowed in a TOML 1.1 bare key. +// Ranges taken verbatim from the TOML 1.1 spec grammar for bare-key-char. +static bool is_unicode_bare_key_char(uint32_t cp) { + if (cp == 0xB2 || cp == 0xB3 || cp == 0xB9) + return true; + if (0xBC <= cp && cp <= 0xBE) + return true; + if (0xC0 <= cp && cp <= 0xD6) + return true; + if (0xD8 <= cp && cp <= 0xF6) + return true; + if (0xF8 <= cp && cp <= 0x37D) + return true; + if (0x37F <= cp && cp <= 0x1FFF) + return true; + if (cp == 0x200C || cp == 0x200D) + return true; + if (0x203F <= cp && cp <= 0x2040) + return true; + if (0x2070 <= cp && cp <= 0x218F) + return true; + if (0x2460 <= cp && cp <= 0x24FF) + return true; + if (0x2C00 <= cp && cp <= 0x2FEF) + return true; + if (0x3001 <= cp && cp <= 0xD7FF) + return true; + if (0xF900 <= cp && cp <= 0xFDCF) + return true; + if (0xFDF0 <= cp && cp <= 0xFFFD) + return cp != 0xFEFF; // BOM: stripped when leading, an error anywhere else + if (0x10000 <= cp && cp <= 0xEFFFF) + return true; + return false; +} + +// Scan a literal +static int scan_literal(scanner_t *sp, token_t *tok) { + *tok = mktoken(sp, TOK_LIT); + const char *p = sp->cur; + while (p < sp->endp) { + if (isalnum((unsigned char)*p) || *p == '_' || *p == '-') { + p++; + continue; + } + if ((unsigned char)*p >= 0x80) { + uint32_t cp; + int n = utf8_to_ucs(p, sp->endp - p, &cp); + if (n > 0 && is_unicode_bare_key_char(cp)) { + p += n; + continue; + } + } + break; + } + tok->str.len = p - tok->str.ptr; + sp->cur = p; + return 0; +} + +// Save the current state of the scanner +static scanner_state_t scan_mark(scanner_t *sp) { + scanner_state_t mark; + mark.sp = sp; + mark.cur = sp->cur; + mark.lineno = sp->lineno; + mark.line_start = sp->line_start; + return mark; +} + +// Restore the scanner state to a previously saved state +static void scan_restore(scanner_t *sp, scanner_state_t mark) { + assert(mark.sp == sp); + sp->cur = mark.cur; + sp->lineno = mark.lineno; + sp->line_start = mark.line_start; +} + +// Return the next token +static int scan_next(scanner_t *sp, bool keymode, token_t *tok) { + static const toktyp_t map[128] = { + ['\n'] = TOK_ENDL, ['.'] = TOK_DOT, ['='] = TOK_EQUAL, + [','] = TOK_COMMA, ['{'] = TOK_LBRACE, ['}'] = TOK_RBRACE}; +again: + *tok = mktoken(sp, TOK_FIN); + + int ch = S_GET(); + if (ch == TOK_FIN) { + return 0; + } + + tok->str.len = 1; + if (0 <= ch && ch < 128 && map[ch]) { + // map simple char to token type and done + tok->toktyp = map[ch]; + return 0; + } + + // handle char that require logic + switch (ch) { + case ' ': + case '\t': + goto again; // skip whitespace + + case '#': + // comment: skip until newline + while (!S_MATCH('\n')) { + ch = S_GET(); + if (ch == TOK_FIN) + break; + if ((0 <= ch && ch <= 0x8) || (0x0a <= ch && ch <= 0x1f) || + (ch == 0x7f)) { + return SETERROR(sp->ebuf, sp->lineno, "bad control char in comment"); + } + } + goto again; // skip comment + + case '[': + tok->toktyp = TOK_LBRACK; + if (keymode && S_MATCH('[')) { + S_GET(); + tok->toktyp = TOK_LLBRACK; + tok->str.len = 2; + } + break; + + case ']': + tok->toktyp = TOK_RBRACK; + if (keymode && S_MATCH(']')) { + S_GET(); + tok->toktyp = TOK_RRBRACK; + tok->str.len = 2; + } + break; + + case '"': + sp->cur--; + DO(scan_string(sp, tok)); + break; + + case '\'': + sp->cur--; + DO(scan_litstring(sp, tok)); + break; + + default: + sp->cur--; + DO(keymode ? scan_literal(sp, tok) : scan_nonstring_literal(sp, tok)); + break; + } + + return 0; +} + +// Check for stack overflow due to excessive number of brackets or braces +static int check_overflow(scanner_t *sp, token_t *tok) { + switch (tok->toktyp) { + case TOK_LBRACK: + sp->bracket_level++; + if (sp->bracket_level > BRACKET_LEVEL_MAX) { + return SETERROR(sp->ebuf, sp->lineno, "stack overflow"); + } + break; + case TOK_RBRACK: + sp->bracket_level--; + break; + case TOK_LBRACE: + sp->brace_level++; + if (sp->brace_level > BRACE_LEVEL_MAX) { + return SETERROR(sp->ebuf, sp->lineno, "stack overflow"); + } + break; + case TOK_RBRACE: + sp->brace_level--; + break; + default: + break; + } + return 0; +} + +static int scan_key(scanner_t *sp, token_t *tok) { + if (sp->errmsg) { + return -1; + } + if (scan_next(sp, true, tok) || check_overflow(sp, tok)) { + sp->errmsg = sp->ebuf.ptr; + return -1; + } + return 0; +} + +static int scan_value(scanner_t *sp, token_t *tok) { + if (sp->errmsg) { + return -1; + } + if (scan_next(sp, false, tok) || check_overflow(sp, tok)) { + sp->errmsg = sp->ebuf.ptr; + return -1; + } + return 0; +} + +/** + * Convert a char in utf8 into UCS, and store it in *ret. + * Return #bytes consumed or -1 on failure. + */ +static int utf8_to_ucs(const char *orig, int len, uint32_t *ret) { + const unsigned char *buf = (const unsigned char *)orig; + unsigned i = *buf++; + uint32_t v; + + /* 0x00000000 - 0x0000007F: + 0xxxxxxx + */ + if (0 == (i >> 7)) { + if (len < 1) + return -1; + v = i; + return *ret = v, 1; + } + /* 0x00000080 - 0x000007FF: + 110xxxxx 10xxxxxx + */ + if (0x6 == (i >> 5)) { + if (len < 2) + return -1; + v = i & 0x1f; + for (int j = 0; j < 1; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x00000800 - 0x0000FFFF: + 1110xxxx 10xxxxxx 10xxxxxx + */ + if (0xE == (i >> 4)) { + if (len < 3) + return -1; + v = i & 0x0F; + for (int j = 0; j < 2; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x00010000 - 0x001FFFFF: + 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (0x1E == (i >> 3)) { + if (len < 4) + return -1; + v = i & 0x07; + for (int j = 0; j < 3; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + if (0) { + // NOTE: these code points taking more than 4 bytes are not supported + + /* 0x00200000 - 0x03FFFFFF: + 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (0x3E == (i >> 2)) { + if (len < 5) + return -1; + v = i & 0x03; + for (int j = 0; j < 4; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x04000000 - 0x7FFFFFFF: + 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (0x7e == (i >> 1)) { + if (len < 6) + return -1; + v = i & 0x01; + for (int j = 0; j < 5; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + } + + return -1; +} + +/** + * Convert a UCS char to utf8 code, and return it in buf. + * Return #bytes used in buf to encode the char, or + * -1 on error. + */ +static int ucs_to_utf8(uint32_t code, char buf[4]) { + /* http://stackoverflow.com/questions/6240055/manually-converting-unicode-codepoints-into-utf-8-and-utf-16 + */ + /* The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well + * as 0xfffe and 0xffff (UCS noncharacters) should not appear in + * conforming UTF-8 streams. + */ + /* + * https://github.com/toml-lang/toml-test/issues/165 + * [0xd800, 0xdfff] and [0xfffe, 0xffff] are implicitly allowed by TOML, so + * we disable the check. + */ + if (0) { + if (0xd800 <= code && code <= 0xdfff) + return -1; + if (0xfffe <= code && code <= 0xffff) + return -1; + } + + /* 0x00000000 - 0x0000007F: + 0xxxxxxx + */ + if (code <= 0x7F) { + buf[0] = (unsigned char)code; + return 1; + } + + /* 0x00000080 - 0x000007FF: + 110xxxxx 10xxxxxx + */ + if (code <= 0x000007FF) { + buf[0] = (unsigned char)(0xc0 | (code >> 6)); + buf[1] = (unsigned char)(0x80 | (code & 0x3f)); + return 2; + } + + /* 0x00000800 - 0x0000FFFF: + 1110xxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x0000FFFF) { + buf[0] = (unsigned char)(0xe0 | (code >> 12)); + buf[1] = (unsigned char)(0x80 | ((code >> 6) & 0x3f)); + buf[2] = (unsigned char)(0x80 | (code & 0x3f)); + return 3; + } + + /* 0x00010000 - 0x001FFFFF: + 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x001FFFFF) { + buf[0] = (unsigned char)(0xf0 | (code >> 18)); + buf[1] = (unsigned char)(0x80 | ((code >> 12) & 0x3f)); + buf[2] = (unsigned char)(0x80 | ((code >> 6) & 0x3f)); + buf[3] = (unsigned char)(0x80 | (code & 0x3f)); + return 4; + } + +#ifdef UNDEF + if (0) { + // NOTE: these code points taking more than 4 bytes are not supported + /* 0x00200000 - 0x03FFFFFF: + 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x03FFFFFF) { + buf[0] = (unsigned char)(0xf8 | (code >> 24)); + buf[1] = (unsigned char)(0x80 | ((code >> 18) & 0x3f)); + buf[2] = (unsigned char)(0x80 | ((code >> 12) & 0x3f)); + buf[3] = (unsigned char)(0x80 | ((code >> 6) & 0x3f)); + buf[4] = (unsigned char)(0x80 | (code & 0x3f)); + return 5; + } + + /* 0x04000000 - 0x7FFFFFFF: + 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x7FFFFFFF) { + buf[0] = (unsigned char)(0xfc | (code >> 30)); + buf[1] = (unsigned char)(0x80 | ((code >> 24) & 0x3f)); + buf[2] = (unsigned char)(0x80 | ((code >> 18) & 0x3f)); + buf[3] = (unsigned char)(0x80 | ((code >> 12) & 0x3f)); + buf[4] = (unsigned char)(0x80 | ((code >> 6) & 0x3f)); + buf[5] = (unsigned char)(0x80 | (code & 0x3f)); + return 6; + } + } +#endif + + return -1; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.h new file mode 100644 index 000000000000..3c367be1f2ad --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/tomlc17.h @@ -0,0 +1,285 @@ +/* Copyright (c) 2024-2026, CK Tan. + * https://github.com/cktan/tomlc17/blob/main/LICENSE + */ + +/** + * @file tomlc17.h + * @brief A TOML parser for C17. + * + * This library provides a simple and efficient way to parse TOML documents + * in C. It supports standard TOML features and provides an easy-to-use API + * for traversing the parsed data. + */ + +#ifndef TOMLC17_H +#define TOMLC17_H + +// A crude way to determine version. Manually changed. +#define TOMLC17_RELEASE_AFTER "260618" + +/* + * USAGE: + * + * 1. Call toml_parse(), toml_parse_file(), or toml_parse_file_ex() + * 2. Check result.ok + * 3. Use toml_get() or toml_seek() to query and traverse the + * result.toptab + * 4. Call toml_free() to release resources. + * + */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> + +#ifdef __cplusplus +#define TOML_EXTERN extern "C" +#else +#define TOML_EXTERN extern +#endif + +/** + * @brief Enumeration of TOML data types. + */ +enum toml_type_t { + TOML_UNKNOWN = 0, /**< Unknown or invalid type */ + TOML_STRING, /**< String type */ + TOML_INT64, /**< 64-bit integer type */ + TOML_FP64, /**< 64-bit floating point type */ + TOML_BOOLEAN, /**< Boolean type */ + TOML_DATE, /**< Local date type */ + TOML_TIME, /**< Local time type */ + TOML_DATETIME, /**< Local datetime type */ + TOML_DATETIMETZ, /**< Offset datetime type */ + TOML_ARRAY, /**< Array type */ + TOML_TABLE, /**< Table type */ +}; +typedef enum toml_type_t toml_type_t; + +/** + * @brief Represents a single piece of TOML data. + * + * This structure is a node in a tree that represents a TOML document. + * The `u` union contains the actual value based on the `type` field. + */ +typedef struct toml_datum_t toml_datum_t; +struct toml_datum_t { + toml_type_t type; /**< Type of the datum */ + uint32_t flag; /**< Internal flag, do not use */ + int lineno; /**< 1-based source line number, 0 if synthesized */ + int colno; /**< 1-based source column number, 0 if synthesized */ + const char *source; /**< Source name (e.g. filename), NULL if not provided. + Owned by the result; valid until toml_free(). */ + union { + const char *s; /**< Shorthand for str.ptr */ + struct { + const char *ptr; /**< NUL terminated string pointer */ + int len; /**< Length of the string excluding the terminating NUL */ + } str; + int64_t int64; /**< 64-bit integer value */ + double fp64; /**< 64-bit floating point value */ + bool boolean; /**< Boolean value */ + struct { /**< Date and time components */ + int16_t year, month, day; + int16_t hour, minute, second; + int32_t usec; + int16_t tz; /**< Timezone offset in minutes */ + } ts; + struct { /**< Array data */ + int32_t size; /**< Number of elements in the array */ + toml_datum_t *elem; /**< Array of elements */ + } arr; + struct { /**< Table data */ + int32_t size; /**< Number of keys in the table */ + const char **key; /**< Array of keys */ + int *len; /**< Array of key lengths */ + toml_datum_t *value; /**< Array of values corresponding to keys */ + } tab; + } u; +}; + +/** + * @brief Bit values for toml_datum_t::flag. + * + * These record how a datum appeared in the source document. They are set + * by the parser and are informational for API users. + */ +#define TOML_FLAG_INLINED \ + 1 /**< appeared in inline form, e.g. {x = 1} or [1, 2] */ +#define TOML_FLAG_STDEXPR 2 /**< table created by a [table] header */ +#define TOML_FLAG_EXPLICIT 4 /**< table explicitly defined */ + +/** + * @brief Result of a TOML parsing operation. + */ +typedef struct toml_result_t toml_result_t; +struct toml_result_t { + bool ok; /**< True if parsing was successful */ + toml_datum_t toptab; /**< The top-level table (valid if ok is true) */ + char errmsg[200]; /**< Error message (valid if ok is false) */ + void *__internal; /**< Internal state, do not use */ +}; + +/** + * @brief Parse a TOML document from a string. + * + * @param src A string containing the TOML document. + * @param len The length of the string. src need not be NUL-terminated; + * only the first len bytes are read. + * @return A toml_result_t structure. Must be freed with toml_free(). + */ +TOML_EXTERN toml_result_t toml_parse(const char *src, int len); + +/** + * @brief Parse a TOML document, tagging every datum with a source name. + * + * @param src A string containing the TOML document. + * @param len The length of the string. src need not be NUL-terminated; + * only the first len bytes are read. + * @param name A source name (e.g. filename) copied into the result, or NULL. + * Every parsed datum's `source` is set to this name (or NULL). + * @return A toml_result_t structure. Must be freed with toml_free(). + */ +TOML_EXTERN toml_result_t toml_parse_named(const char *src, int len, + const char *name); + +/** + * @brief Parse a TOML document from a file pointer. + * + * @param fp A pointer to the open file. The caller is responsible for closing + * it. + * @return A toml_result_t structure. Must be freed with toml_free(). + * + * IMPORTANT: you are still responsible to fclose(fp). + */ +TOML_EXTERN toml_result_t toml_parse_file(FILE *fp); + +/** + * @brief Parse a TOML document from a file pointer, tagging datums with a name. + * + * @param fp A pointer to the open file. The caller is responsible for closing + * it. + * @param name A source name copied into the result, or NULL. + * @return A toml_result_t structure. Must be freed with toml_free(). + * + * IMPORTANT: you are still responsible to fclose(fp). + */ +TOML_EXTERN toml_result_t toml_parse_file_named(FILE *fp, const char *name); + +/** + * @brief Parse a TOML document from a file path. + * + * @param fname The path to the TOML file. + * @return A toml_result_t structure. Must be freed with toml_free(). + */ +TOML_EXTERN toml_result_t toml_parse_file_ex(const char *fname); + +/** + * @brief Release resources allocated for a TOML result. + * + * @param result The TOML result to free. + */ +TOML_EXTERN void toml_free(toml_result_t result); + +/** + * @brief Find a value for a specific key in a TOML table. + * + * @param table The TOML table to search in. + * @param key The key to look for. + * @return The value associated with the key, or a datum with type TOML_UNKNOWN + * if not found. + */ +TOML_EXTERN toml_datum_t toml_get(toml_datum_t table, const char *key); + +/** + * @brief Locate a value using a multipart-key (e.g., "a.b.c"). + * + * @param table The TOML table to start the search from. + * @param multipart_key A dot-separated key string. No escape characters + * allowed. Maximum length is 255 bytes. + * @return The value found, or a datum with type TOML_UNKNOWN if not found. + */ +TOML_EXTERN toml_datum_t toml_seek(toml_datum_t table, + const char *multipart_key); + +/** + * @brief OBSOLETE: use toml_get() instead. + * Find a key in a toml_table. Return the value of the key if found, + * or a TOML_UNKNOWN otherwise. + */ +static inline toml_datum_t toml_table_find(toml_datum_t table, + const char *key) { + return toml_get(table, key); +} + +/** + * @brief Merge two TOML results. + * + * All results (r1, r2, and the returned result) must be freed independently. + * + * @param r1 The base TOML result. + * @param r2 The TOML result containing overrides. + * @return A new toml_result_t representing the merged document. + * + * LOGIC: + * ret = copy of r1 + * for each item x in r2: + * if x is not in ret: + * set x in ret + * elif x in ret is NOT of the same type: + * override + * elif x in ret is an array of tables: + * append r2.x to ret.x + * elif x in ret is a table: + * merge r2.x to ret.x + * else: + * override + */ +TOML_EXTERN toml_result_t toml_merge(const toml_result_t *r1, + const toml_result_t *r2); + +/** + * @brief Compare two TOML results for equality. + * + * Tables compare as unordered maps (keys matched by name); array element + * order is significant. + * + * @param r1 The first TOML result. + * @param r2 The second TOML result. + * @return True if they are equivalent, false otherwise. + */ +TOML_EXTERN bool toml_equiv(const toml_result_t *r1, const toml_result_t *r2); + +/** + * @brief Global options for the TOML parser. + */ +typedef struct toml_option_t toml_option_t; +struct toml_option_t { + bool check_utf8; /**< If true, check if all characters are valid UTF-8. + Default: false. */ + void *(*mem_realloc)(void *ptr, + size_t size); /**< Custom realloc function (called with + size > 0). Default: realloc(). */ + void (*mem_free)(void *ptr); /**< Custom free function. Default: free(). */ +}; + +/** + * @brief Get the default parser options. + * + * Use this to obtain and initialize a toml_option_t structure before + * customizing it. + * + * @return A toml_option_t with default values. + */ +TOML_EXTERN toml_option_t toml_default_option(void); + +/** + * @brief Set the global parser options. + * + * Call this only if you need to override the default behavior. + * + * @param opt The options to set. + */ +TOML_EXTERN void toml_set_option(toml_option_t opt); + +#endif // TOMLC17_H diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/video-inhibition.md b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/video-inhibition.md new file mode 100644 index 000000000000..393c1c52cf09 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/video-inhibition.md @@ -0,0 +1,66 @@ +# Application idle inhibition + +This integration is included in Ambient Background. See the configuration guide for current file paths and the WCM controls for continuous live wallpapers; helper and socket identifiers retain their compatible names. + +See [Configuration](configuration.md) for the full option reference and reload rules. + +Enable the Wayfire `digitalrain-idle` plugin alongside `ipc`, +`ipc-rules`, and `digitalrain-cursor` in Wayfire's `core/plugins` setting. + +`INHIBIT_VIDEO = true` in the `[global]` section of ambient-background.toml enables this feature +(default). Set it to false to disable it. The manager watches configuration saves. + +Applications that create Wayland idle inhibitors, including tested mpv playback, +keep their window's output awake. Moving the window moves the protection. When +the application releases its inhibitor (normally on pause or close), the normal +idle countdown resumes. Minimized +windows, disabled scene nodes, and windows off the current workspace are ignored. +An already active saver on the playback output is dismissed. Explicit activation +and lock requests still take precedence until user input resumes. + +Browser requests through `org.freedesktop.portal.Inhibit` are also supported +with digitalrain r3 and the Wayfire bridge in xdg-desktop-portal-gtk 1.15.3-r1. +The GTK backend keeps its usual portal routing and non-Wayfire fallback. It +forwards idle requests through a held Wayfire IPC connection rather than a +global ScreenSaver cookie. The compositor resolves the native Wayland parent +handle and verifies its owner against the original portal caller's bus PID. +Digitalrain r4 also accepts Firefox/LibreWolf's bare program-name placeholder +(for example `librewolf`) as a missing parent. The placeholder is not trusted +as an application or window identity: selection still uses the original bus +caller's PID. For a missing parent, it selects that caller's focused root or sole +mapped root; an ambiguous unfocused multi-window request is rejected. Invalid +or foreign `wayland:` handles never fall back to this selection, and other +prefixed handles (including `x11:`) remain unsupported. App IDs, +the portal broker's PID, MPRIS playback state, and fullscreen alone do not +create inhibition. + +Each request binds one window lifetime. Closing it, losing the original bus +caller or broker, backend exit, window unmap, or plugin unload releases it. +Minimizing or moving off-workspace temporarily stops its protection; restoring +the same mapped window resumes it while the request is held. Multiple requests +remain independent. New requests are needed after a plugin/backend restart; +already-failed portal calls are not silently replayed. Restarting the desktop +once activates both installed components, or relaunch the backend and renew +the browser request yourself. No user configuration rewrite is required. + +This follows application keep-awake requests, not video frame analysis. Players +that use neither supported Wayland nor portal inhibition are not detected. Applications +may also use inhibitors for non-video tasks. For ordinary outputs the window's primary output is used. With stitched outputs, +each physical member intersected by the playback window is protected. + +The manager uses ext-idle-notify v2's input-only notification to avoid one video +inhibiting idle detection across all monitors. With protocol v1 it falls back to +the old notification and logs a warning about the focused output also staying awake. + +IPC diagnostic: `digitalrain/idle_inhibitors` returns +`{"outputs":["DP-2"],"portal-requests":1}` for one portal request on DP-2. +The output list combines native and portal requests; the count includes held +portal leases even when their window is temporarily minimized or off-workspace. +Manager logs contain per-output `video inhibition active/released` transitions. + +Validation: build with `make all`; the C regression test is +`tests/test-inhibition.c` (link with the same protocol sources and libraries as +`files/digitalrain.c`). This also verifies that repeated seat-wide idle/resume +events refresh only the focused output, preserving other outputs' idle deadlines. +Live mpv tests covered playback on DP-1, moving to DP-3, +pause, resume, and preserving an existing DP-2 inhibitor throughout. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/wlr-layer-shell-unstable-v1-client-protocol.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/wlr-layer-shell-unstable-v1-client-protocol.h new file mode 100644 index 000000000000..660337bfad11 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/wlr-layer-shell-unstable-v1-client-protocol.h @@ -0,0 +1,599 @@ +/* Generated by wayland-scanner 1.26.0 */ + +#ifndef WLR_LAYER_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define WLR_LAYER_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_wlr_layer_shell_unstable_v1 The wlr_layer_shell_unstable_v1 protocol + * @section page_ifaces_wlr_layer_shell_unstable_v1 Interfaces + * - @subpage page_iface_zwlr_layer_shell_v1 - create surfaces that are layers of the desktop + * - @subpage page_iface_zwlr_layer_surface_v1 - layer metadata interface + * @section page_copyright_wlr_layer_shell_unstable_v1 Copyright + * <pre> + * + * Copyright © 2017 Drew DeVault + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * the copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + * </pre> + */ +struct wl_output; +struct wl_surface; +struct xdg_popup; +struct zwlr_layer_shell_v1; +struct zwlr_layer_surface_v1; + +#ifndef ZWLR_LAYER_SHELL_V1_INTERFACE +#define ZWLR_LAYER_SHELL_V1_INTERFACE +/** + * @page page_iface_zwlr_layer_shell_v1 zwlr_layer_shell_v1 + * @section page_iface_zwlr_layer_shell_v1_desc Description + * + * Clients can use this interface to assign the surface_layer role to + * wl_surfaces. Such surfaces are assigned to a "layer" of the output and + * rendered with a defined z-depth respective to each other. They may also be + * anchored to the edges and corners of a screen and specify input handling + * semantics. This interface should be suitable for the implementation of + * many desktop shell components, and a broad number of other applications + * that interact with the desktop. + * @section page_iface_zwlr_layer_shell_v1_api API + * See @ref iface_zwlr_layer_shell_v1. + */ +/** + * @defgroup iface_zwlr_layer_shell_v1 The zwlr_layer_shell_v1 interface + * + * Clients can use this interface to assign the surface_layer role to + * wl_surfaces. Such surfaces are assigned to a "layer" of the output and + * rendered with a defined z-depth respective to each other. They may also be + * anchored to the edges and corners of a screen and specify input handling + * semantics. This interface should be suitable for the implementation of + * many desktop shell components, and a broad number of other applications + * that interact with the desktop. + */ +extern const struct wl_interface zwlr_layer_shell_v1_interface; +#endif +#ifndef ZWLR_LAYER_SURFACE_V1_INTERFACE +#define ZWLR_LAYER_SURFACE_V1_INTERFACE +/** + * @page page_iface_zwlr_layer_surface_v1 zwlr_layer_surface_v1 + * @section page_iface_zwlr_layer_surface_v1_desc Description + * + * An interface that may be implemented by a wl_surface, for surfaces that + * are designed to be rendered as a layer of a stacked desktop-like + * environment. + * + * Layer surface state (layer, size, anchor, exclusive zone, + * margin, interactivity) is double-buffered, and will be applied at the + * time wl_surface.commit of the corresponding wl_surface is called. + * @section page_iface_zwlr_layer_surface_v1_api API + * See @ref iface_zwlr_layer_surface_v1. + */ +/** + * @defgroup iface_zwlr_layer_surface_v1 The zwlr_layer_surface_v1 interface + * + * An interface that may be implemented by a wl_surface, for surfaces that + * are designed to be rendered as a layer of a stacked desktop-like + * environment. + * + * Layer surface state (layer, size, anchor, exclusive zone, + * margin, interactivity) is double-buffered, and will be applied at the + * time wl_surface.commit of the corresponding wl_surface is called. + */ +extern const struct wl_interface zwlr_layer_surface_v1_interface; +#endif + +#ifndef ZWLR_LAYER_SHELL_V1_ERROR_ENUM +#define ZWLR_LAYER_SHELL_V1_ERROR_ENUM +enum zwlr_layer_shell_v1_error { + /** + * wl_surface has another role + */ + ZWLR_LAYER_SHELL_V1_ERROR_ROLE = 0, + /** + * layer value is invalid + */ + ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER = 1, + /** + * wl_surface has a buffer attached or committed + */ + ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED = 2, +}; +#endif /* ZWLR_LAYER_SHELL_V1_ERROR_ENUM */ + +#ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM +#define ZWLR_LAYER_SHELL_V1_LAYER_ENUM +/** + * @ingroup iface_zwlr_layer_shell_v1 + * available layers for surfaces + * + * These values indicate which layers a surface can be rendered in. They + * are ordered by z depth, bottom-most first. Traditional shell surfaces + * will typically be rendered between the bottom and top layers. + * Fullscreen shell surfaces are typically rendered at the top layer. + * Multiple surfaces can share a single layer, and ordering within a + * single layer is undefined. + */ +enum zwlr_layer_shell_v1_layer { + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND = 0, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM = 1, + ZWLR_LAYER_SHELL_V1_LAYER_TOP = 2, + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY = 3, +}; +#endif /* ZWLR_LAYER_SHELL_V1_LAYER_ENUM */ + +#define ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE 0 +#define ZWLR_LAYER_SHELL_V1_DESTROY 1 + + +/** + * @ingroup iface_zwlr_layer_shell_v1 + */ +#define ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_shell_v1 + */ +#define ZWLR_LAYER_SHELL_V1_DESTROY_SINCE_VERSION 3 + +/** @ingroup iface_zwlr_layer_shell_v1 */ +static inline void +zwlr_layer_shell_v1_set_user_data(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_layer_shell_v1, user_data); +} + +/** @ingroup iface_zwlr_layer_shell_v1 */ +static inline void * +zwlr_layer_shell_v1_get_user_data(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_layer_shell_v1); +} + +static inline uint32_t +zwlr_layer_shell_v1_get_version(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1); +} + +/** + * @ingroup iface_zwlr_layer_shell_v1 + * + * Create a layer surface for an existing surface. This assigns the role of + * layer_surface, or raises a protocol error if another role is already + * assigned. + * + * Creating a layer surface from a wl_surface which has a buffer attached + * or committed is a client error, and any attempts by a client to attach + * or manipulate a buffer prior to the first layer_surface.configure call + * must also be treated as errors. + * + * You may pass NULL for output to allow the compositor to decide which + * output to use. Generally this will be the one that the user most + * recently interacted with. + * + * Clients can specify a namespace that defines the purpose of the layer + * surface. + */ +static inline struct zwlr_layer_surface_v1 * +zwlr_layer_shell_v1_get_layer_surface(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, struct wl_surface *surface, struct wl_output *output, uint32_t layer, const char *namespace) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_shell_v1, + ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE, &zwlr_layer_surface_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1), 0, NULL, surface, output, layer, namespace); + + return (struct zwlr_layer_surface_v1 *) id; +} + +/** + * @ingroup iface_zwlr_layer_shell_v1 + * + * This request indicates that the client will not use the layer_shell + * object any more. Objects that have been created through this instance + * are not affected. + */ +static inline void +zwlr_layer_shell_v1_destroy(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_shell_v1, + ZWLR_LAYER_SHELL_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifndef ZWLR_LAYER_SURFACE_V1_ERROR_ENUM +#define ZWLR_LAYER_SURFACE_V1_ERROR_ENUM +enum zwlr_layer_surface_v1_error { + /** + * provided surface state is invalid + */ + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE = 0, + /** + * size is invalid + */ + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE = 1, + /** + * anchor bitfield is invalid + */ + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR = 2, +}; +#endif /* ZWLR_LAYER_SURFACE_V1_ERROR_ENUM */ + +#ifndef ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM +#define ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM +enum zwlr_layer_surface_v1_anchor { + /** + * the top edge of the anchor rectangle + */ + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP = 1, + /** + * the bottom edge of the anchor rectangle + */ + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM = 2, + /** + * the left edge of the anchor rectangle + */ + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT = 4, + /** + * the right edge of the anchor rectangle + */ + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT = 8, +}; +#endif /* ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM */ + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * @struct zwlr_layer_surface_v1_listener + */ +struct zwlr_layer_surface_v1_listener { + /** + * suggest a surface change + * + * The configure event asks the client to resize its surface. + * + * Clients should arrange their surface for the new states, and + * then send an ack_configure request with the serial sent in this + * configure event at some point before committing the new surface. + * + * The client is free to dismiss all but the last configure event + * it received. + * + * The width and height arguments specify the size of the window in + * surface-local coordinates. + * + * The size is a hint, in the sense that the client is free to + * ignore it if it doesn't resize, pick a smaller size (to satisfy + * aspect ratio or resize in steps of NxM pixels). If the client + * picks a smaller size and is anchored to two opposite anchors + * (e.g. 'top' and 'bottom'), the surface will be centered on this + * axis. + * + * If the width or height arguments are zero, it means the client + * should decide its own window dimension. + */ + void (*configure)(void *data, + struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, + uint32_t serial, + uint32_t width, + uint32_t height); + /** + * surface should be closed + * + * The closed event is sent by the compositor when the surface + * will no longer be shown. The output may have been destroyed or + * the user may have asked for it to be removed. Further changes to + * the surface will be ignored. The client should destroy the + * resource after receiving this event, and create a new surface if + * they so choose. + */ + void (*closed)(void *data, + struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1); +}; + +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +static inline int +zwlr_layer_surface_v1_add_listener(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, + const struct zwlr_layer_surface_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_layer_surface_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_LAYER_SURFACE_V1_SET_SIZE 0 +#define ZWLR_LAYER_SURFACE_V1_SET_ANCHOR 1 +#define ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE 2 +#define ZWLR_LAYER_SURFACE_V1_SET_MARGIN 3 +#define ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY 4 +#define ZWLR_LAYER_SURFACE_V1_GET_POPUP 5 +#define ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE 6 +#define ZWLR_LAYER_SURFACE_V1_DESTROY 7 +#define ZWLR_LAYER_SURFACE_V1_SET_LAYER 8 + +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_CLOSED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_MARGIN_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION 2 + +/** @ingroup iface_zwlr_layer_surface_v1 */ +static inline void +zwlr_layer_surface_v1_set_user_data(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_layer_surface_v1, user_data); +} + +/** @ingroup iface_zwlr_layer_surface_v1 */ +static inline void * +zwlr_layer_surface_v1_get_user_data(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_layer_surface_v1); +} + +static inline uint32_t +zwlr_layer_surface_v1_get_version(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * + * Sets the size of the surface in surface-local coordinates. The + * compositor will display the surface centered with respect to its + * anchors. + * + * If you pass 0 for either value, the compositor will assign it and + * inform you of the assignment in the configure event. You must set your + * anchor to opposite edges in the dimensions you omit; not doing so is a + * protocol error. Both values are 0 by default. + * + * Size is double-buffered, see wl_surface.commit. + */ +static inline void +zwlr_layer_surface_v1_set_size(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t width, uint32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, width, height); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * + * Requests that the compositor anchor the surface to the specified edges + * and corners. If two orthogonal edges are specified (e.g. 'top' and + * 'left'), then the anchor point will be the intersection of the edges + * (e.g. the top left corner of the output); otherwise the anchor point + * will be centered on that edge, or in the center if none is specified. + * + * Anchor is double-buffered, see wl_surface.commit. + */ +static inline void +zwlr_layer_surface_v1_set_anchor(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, anchor); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * + * Requests that the compositor avoids occluding an area with other + * surfaces. The compositor's use of this information is + * implementation-dependent - do not assume that this region will not + * actually be occluded. + * + * A positive value is only meaningful if the surface is anchored to one + * edge or an edge and both perpendicular edges. If the surface is not + * anchored, anchored to only two perpendicular edges (a corner), anchored + * to only two parallel edges or anchored to all edges, a positive value + * will be treated the same as zero. + * + * A positive zone is the distance from the edge in surface-local + * coordinates to consider exclusive. + * + * Surfaces that do not wish to have an exclusive zone may instead specify + * how they should interact with surfaces that do. If set to zero, the + * surface indicates that it would like to be moved to avoid occluding + * surfaces with a positive exclusive zone. If set to -1, the surface + * indicates that it would not like to be moved to accommodate for other + * surfaces, and the compositor should extend it all the way to the edges + * it is anchored to. + * + * For example, a panel might set its exclusive zone to 10, so that + * maximized shell surfaces are not shown on top of it. A notification + * might set its exclusive zone to 0, so that it is moved to avoid + * occluding the panel, but shell surfaces are shown underneath it. A + * wallpaper or lock screen might set their exclusive zone to -1, so that + * they stretch below or over the panel. + * + * The default value is 0. + * + * Exclusive zone is double-buffered, see wl_surface.commit. + */ +static inline void +zwlr_layer_surface_v1_set_exclusive_zone(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t zone) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, zone); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * + * Requests that the surface be placed some distance away from the anchor + * point on the output, in surface-local coordinates. Setting this value + * for edges you are not anchored to has no effect. + * + * The exclusive zone includes the margin. + * + * Margin is double-buffered, see wl_surface.commit. + */ +static inline void +zwlr_layer_surface_v1_set_margin(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t top, int32_t right, int32_t bottom, int32_t left) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_SET_MARGIN, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, top, right, bottom, left); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * + * Set to 1 to request that the seat send keyboard events to this layer + * surface. For layers below the shell surface layer, the seat will use + * normal focus semantics. For layers above the shell surface layers, the + * seat will always give exclusive keyboard focus to the top-most layer + * which has keyboard interactivity set to true. + * + * Layer surfaces receive pointer, touch, and tablet events normally. If + * you do not want to receive them, set the input region on your surface + * to an empty region. + * + * Events is double-buffered, see wl_surface.commit. + */ +static inline void +zwlr_layer_surface_v1_set_keyboard_interactivity(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t keyboard_interactivity) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, keyboard_interactivity); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * + * This assigns an xdg_popup's parent to this layer_surface. This popup + * should have been created via xdg_surface::get_popup with the parent set + * to NULL, and this request must be invoked before committing the popup's + * initial state. + * + * See the documentation of xdg_popup for more details about what an + * xdg_popup is and how it is used. + */ +static inline void +zwlr_layer_surface_v1_get_popup(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, struct xdg_popup *popup) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_GET_POPUP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, popup); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * + * When a configure event is received, if a client commits the + * surface in response to the configure event, then the client + * must make an ack_configure request sometime before the commit + * request, passing along the serial of the configure event. + * + * If the client receives multiple configure events before it + * can respond to one, it only has to ack the last configure event. + * + * A client is not required to commit immediately after sending + * an ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before committing, but + * only the last request sent before a commit indicates which configure + * event the client really is responding to. + */ +static inline void +zwlr_layer_surface_v1_ack_configure(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, serial); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * + * This request destroys the layer surface. + */ +static inline void +zwlr_layer_surface_v1_destroy(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * + * Change the layer that the surface is rendered on. + * + * Layer is double-buffered, see wl_surface.commit. + */ +static inline void +zwlr_layer_surface_v1_set_layer(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t layer) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_SET_LAYER, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, layer); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/wlr-layer-shell-unstable-v1-protocol.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/wlr-layer-shell-unstable-v1-protocol.c new file mode 100644 index 000000000000..e9da255d87c6 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/wlr-layer-shell-unstable-v1-protocol.c @@ -0,0 +1,94 @@ +/* Generated by wayland-scanner 1.26.0 */ + +/* + * Copyright © 2017 Drew DeVault + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * the copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface xdg_popup_interface; +extern const struct wl_interface zwlr_layer_surface_v1_interface; + +static const struct wl_interface *wlr_layer_shell_unstable_v1_types[] = { + NULL, + NULL, + NULL, + NULL, + &zwlr_layer_surface_v1_interface, + &wl_surface_interface, + &wl_output_interface, + NULL, + NULL, + &xdg_popup_interface, +}; + +static const struct wl_message zwlr_layer_shell_v1_requests[] = { + { "get_layer_surface", "no?ous", wlr_layer_shell_unstable_v1_types + 4 }, + { "destroy", "3", wlr_layer_shell_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_layer_shell_v1_interface = { + "zwlr_layer_shell_v1", 3, + 2, zwlr_layer_shell_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwlr_layer_surface_v1_requests[] = { + { "set_size", "uu", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_anchor", "u", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_exclusive_zone", "i", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_margin", "iiii", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_keyboard_interactivity", "u", wlr_layer_shell_unstable_v1_types + 0 }, + { "get_popup", "o", wlr_layer_shell_unstable_v1_types + 9 }, + { "ack_configure", "u", wlr_layer_shell_unstable_v1_types + 0 }, + { "destroy", "", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_layer", "2u", wlr_layer_shell_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_layer_surface_v1_events[] = { + { "configure", "uuu", wlr_layer_shell_unstable_v1_types + 0 }, + { "closed", "", wlr_layer_shell_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_layer_surface_v1_interface = { + "zwlr_layer_surface_v1", 3, + 9, zwlr_layer_surface_v1_requests, + 2, zwlr_layer_surface_v1_events, +}; + diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/wlr-layer-shell-unstable-v1.xml b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 000000000000..fa67001dfeb1 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,311 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="wlr_layer_shell_unstable_v1"> + <copyright> + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + </copyright> + + <interface name="zwlr_layer_shell_v1" version="3"> + <description summary="create surfaces that are layers of the desktop"> + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + </description> + + <request name="get_layer_surface"> + <description summary="create a layer_surface from a surface"> + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + You may pass NULL for output to allow the compositor to decide which + output to use. Generally this will be the one that the user most + recently interacted with. + + Clients can specify a namespace that defines the purpose of the layer + surface. + </description> + <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/> + <arg name="surface" type="object" interface="wl_surface"/> + <arg name="output" type="object" interface="wl_output" allow-null="true"/> + <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/> + <arg name="namespace" type="string" summary="namespace for the layer surface"/> + </request> + + <enum name="error"> + <entry name="role" value="0" summary="wl_surface has another role"/> + <entry name="invalid_layer" value="1" summary="layer value is invalid"/> + <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/> + </enum> + + <enum name="layer"> + <description summary="available layers for surfaces"> + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + </description> + + <entry name="background" value="0"/> + <entry name="bottom" value="1"/> + <entry name="top" value="2"/> + <entry name="overlay" value="3"/> + </enum> + + <!-- Version 3 additions --> + + <request name="destroy" type="destructor" since="3"> + <description summary="destroy the layer_shell object"> + This request indicates that the client will not use the layer_shell + object any more. Objects that have been created through this instance + are not affected. + </description> + </request> + </interface> + + <interface name="zwlr_layer_surface_v1" version="3"> + <description summary="layer metadata interface"> + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (layer, size, anchor, exclusive zone, + margin, interactivity) is double-buffered, and will be applied at the + time wl_surface.commit of the corresponding wl_surface is called. + </description> + + <request name="set_size"> + <description summary="sets the size of the surface"> + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + </description> + <arg name="width" type="uint"/> + <arg name="height" type="uint"/> + </request> + + <request name="set_anchor"> + <description summary="configures the anchor point of the surface"> + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthogonal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + </description> + <arg name="anchor" type="uint" enum="anchor"/> + </request> + + <request name="set_exclusive_zone"> + <description summary="configures the exclusive geometry of this surface"> + Requests that the compositor avoids occluding an area with other + surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to one + edge or an edge and both perpendicular edges. If the surface is not + anchored, anchored to only two perpendicular edges (a corner), anchored + to only two parallel edges or anchored to all edges, a positive value + will be treated the same as zero. + + A positive zone is the distance from the edge in surface-local + coordinates to consider exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive exclusive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + </description> + <arg name="zone" type="int"/> + </request> + + <request name="set_margin"> + <description summary="sets a margin from the anchor point"> + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + </description> + <arg name="top" type="int"/> + <arg name="right" type="int"/> + <arg name="bottom" type="int"/> + <arg name="left" type="int"/> + </request> + + <request name="set_keyboard_interactivity"> + <description summary="requests keyboard events"> + Set to 1 to request that the seat send keyboard events to this layer + surface. For layers below the shell surface layer, the seat will use + normal focus semantics. For layers above the shell surface layers, the + seat will always give exclusive keyboard focus to the top-most layer + which has keyboard interactivity set to true. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Events is double-buffered, see wl_surface.commit. + </description> + <arg name="keyboard_interactivity" type="uint"/> + </request> + + <request name="get_popup"> + <description summary="assign this layer_surface as an xdg_popup parent"> + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + </description> + <arg name="popup" type="object" interface="xdg_popup"/> + </request> + + <request name="ack_configure"> + <description summary="ack a configure event"> + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + </description> + <arg name="serial" type="uint" summary="the serial from the configure event"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy the layer_surface"> + This request destroys the layer surface. + </description> + </request> + + <event name="configure"> + <description summary="suggest a surface change"> + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + </description> + <arg name="serial" type="uint"/> + <arg name="width" type="uint"/> + <arg name="height" type="uint"/> + </event> + + <event name="closed"> + <description summary="surface should be closed"> + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + </description> + </event> + + <enum name="error"> + <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/> + <entry name="invalid_size" value="1" summary="size is invalid"/> + <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/> + </enum> + + <enum name="anchor" bitfield="true"> + <entry name="top" value="1" summary="the top edge of the anchor rectangle"/> + <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/> + <entry name="left" value="4" summary="the left edge of the anchor rectangle"/> + <entry name="right" value="8" summary="the right edge of the anchor rectangle"/> + </enum> + + <!-- Version 2 additions --> + + <request name="set_layer" since="2"> + <description summary="change the layer of the surface"> + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + </description> + <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/> + </request> + </interface> +</protocol> diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-output-unstable-v1-client-protocol.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-output-unstable-v1-client-protocol.h new file mode 100644 index 000000000000..5dfc398fd2cd --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-output-unstable-v1-client-protocol.h @@ -0,0 +1,415 @@ +/* Generated by wayland-scanner 1.26.0 */ + +#ifndef XDG_OUTPUT_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define XDG_OUTPUT_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_output_unstable_v1 The xdg_output_unstable_v1 protocol + * Protocol to describe output regions + * + * @section page_desc_xdg_output_unstable_v1 Description + * + * This protocol aims at describing outputs in a way which is more in line + * with the concept of an output on desktop oriented systems. + * + * Some information are more specific to the concept of an output for + * a desktop oriented system and may not make sense in other applications, + * such as IVI systems for example. + * + * Typically, the global compositor space on a desktop system is made of + * a contiguous or overlapping set of rectangular regions. + * + * The logical_position and logical_size events defined in this protocol + * might provide information identical to their counterparts already + * available from wl_output, in which case the information provided by this + * protocol should be preferred to their equivalent in wl_output. The goal is + * to move the desktop specific concepts (such as output location within the + * global compositor space, etc.) out of the core wl_output protocol. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible + * changes may be added together with the corresponding interface + * version bump. + * Backward incompatible changes are done by bumping the version + * number in the protocol and interface names and resetting the + * interface version. Once the protocol is to be declared stable, + * the 'z' prefix and the version number in the protocol and + * interface names are removed and the interface version number is + * reset. + * + * @section page_ifaces_xdg_output_unstable_v1 Interfaces + * - @subpage page_iface_zxdg_output_manager_v1 - manage xdg_output objects + * - @subpage page_iface_zxdg_output_v1 - compositor logical output region + * @section page_copyright_xdg_output_unstable_v1 Copyright + * <pre> + * + * Copyright © 2017 Red Hat Inc. + * + * 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. + * </pre> + */ +struct wl_output; +struct zxdg_output_manager_v1; +struct zxdg_output_v1; + +#ifndef ZXDG_OUTPUT_MANAGER_V1_INTERFACE +#define ZXDG_OUTPUT_MANAGER_V1_INTERFACE +/** + * @page page_iface_zxdg_output_manager_v1 zxdg_output_manager_v1 + * @section page_iface_zxdg_output_manager_v1_desc Description + * + * A global factory interface for xdg_output objects. + * @section page_iface_zxdg_output_manager_v1_api API + * See @ref iface_zxdg_output_manager_v1. + */ +/** + * @defgroup iface_zxdg_output_manager_v1 The zxdg_output_manager_v1 interface + * + * A global factory interface for xdg_output objects. + */ +extern const struct wl_interface zxdg_output_manager_v1_interface; +#endif +#ifndef ZXDG_OUTPUT_V1_INTERFACE +#define ZXDG_OUTPUT_V1_INTERFACE +/** + * @page page_iface_zxdg_output_v1 zxdg_output_v1 + * @section page_iface_zxdg_output_v1_desc Description + * + * An xdg_output describes part of the compositor geometry. + * + * This typically corresponds to a monitor that displays part of the + * compositor space. + * + * For objects version 3 onwards, after all xdg_output properties have been + * sent (when the object is created and when properties are updated), a + * wl_output.done event is sent. This allows changes to the output + * properties to be seen as atomic, even if they happen via multiple events. + * @section page_iface_zxdg_output_v1_api API + * See @ref iface_zxdg_output_v1. + */ +/** + * @defgroup iface_zxdg_output_v1 The zxdg_output_v1 interface + * + * An xdg_output describes part of the compositor geometry. + * + * This typically corresponds to a monitor that displays part of the + * compositor space. + * + * For objects version 3 onwards, after all xdg_output properties have been + * sent (when the object is created and when properties are updated), a + * wl_output.done event is sent. This allows changes to the output + * properties to be seen as atomic, even if they happen via multiple events. + */ +extern const struct wl_interface zxdg_output_v1_interface; +#endif + +#define ZXDG_OUTPUT_MANAGER_V1_DESTROY 0 +#define ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT 1 + + +/** + * @ingroup iface_zxdg_output_manager_v1 + */ +#define ZXDG_OUTPUT_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_output_manager_v1 + */ +#define ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_output_manager_v1 */ +static inline void +zxdg_output_manager_v1_set_user_data(struct zxdg_output_manager_v1 *zxdg_output_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_output_manager_v1, user_data); +} + +/** @ingroup iface_zxdg_output_manager_v1 */ +static inline void * +zxdg_output_manager_v1_get_user_data(struct zxdg_output_manager_v1 *zxdg_output_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_output_manager_v1); +} + +static inline uint32_t +zxdg_output_manager_v1_get_version(struct zxdg_output_manager_v1 *zxdg_output_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_output_manager_v1); +} + +/** + * @ingroup iface_zxdg_output_manager_v1 + * + * Using this request a client can tell the server that it is not + * going to use the xdg_output_manager object anymore. + * + * Any objects already created through this instance are not affected. + */ +static inline void +zxdg_output_manager_v1_destroy(struct zxdg_output_manager_v1 *zxdg_output_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_output_manager_v1, + ZXDG_OUTPUT_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_output_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_output_manager_v1 + * + * This creates a new xdg_output object for the given wl_output. + */ +static inline struct zxdg_output_v1 * +zxdg_output_manager_v1_get_xdg_output(struct zxdg_output_manager_v1 *zxdg_output_manager_v1, struct wl_output *output) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_output_manager_v1, + ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT, &zxdg_output_v1_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_output_manager_v1), 0, NULL, output); + + return (struct zxdg_output_v1 *) id; +} + +/** + * @ingroup iface_zxdg_output_v1 + * @struct zxdg_output_v1_listener + */ +struct zxdg_output_v1_listener { + /** + * position of the output within the global compositor space + * + * The position event describes the location of the wl_output + * within the global compositor space. + * + * The logical_position event is sent after creating an xdg_output + * (see xdg_output_manager.get_xdg_output) and whenever the + * location of the output changes within the global compositor + * space. + * @param x x position within the global compositor space + * @param y y position within the global compositor space + */ + void (*logical_position)(void *data, + struct zxdg_output_v1 *zxdg_output_v1, + int32_t x, + int32_t y); + /** + * size of the output in the global compositor space + * + * The logical_size event describes the size of the output in the + * global compositor space. + * + * Most regular Wayland clients should not pay attention to the + * logical size and would rather rely on xdg_shell interfaces. + * + * Some clients such as Xwayland, however, need this to configure + * their surfaces in the global compositor space as the compositor + * may apply a different scale from what is advertised by the + * output scaling property (to achieve fractional scaling, for + * example). + * + * For example, for a wl_output mode 3840×2160 and a scale factor + * 2: + * + * - A compositor not scaling the monitor viewport in its + * compositing space will advertise a logical size of 3840×2160, + * + * - A compositor scaling the monitor viewport with scale factor 2 + * will advertise a logical size of 1920×1080, + * + * - A compositor scaling the monitor viewport using a fractional + * scale of 1.5 will advertise a logical size of 2560×1440. + * + * For example, for a wl_output mode 1920×1080 and a 90 degree + * rotation, the compositor will advertise a logical size of + * 1080x1920. + * + * The logical_size event is sent after creating an xdg_output (see + * xdg_output_manager.get_xdg_output) and whenever the logical size + * of the output changes, either as a result of a change in the + * applied scale or because of a change in the corresponding output + * mode(see wl_output.mode) or transform (see wl_output.transform). + * @param width width in global compositor space + * @param height height in global compositor space + */ + void (*logical_size)(void *data, + struct zxdg_output_v1 *zxdg_output_v1, + int32_t width, + int32_t height); + /** + * all information about the output have been sent + * + * This event is sent after all other properties of an xdg_output + * have been sent. + * + * This allows changes to the xdg_output properties to be seen as + * atomic, even if they happen via multiple events. + * + * For objects version 3 onwards, this event is deprecated. + * Compositors are not required to send it anymore and must send + * wl_output.done instead. + * @deprecated Deprecated since version 3 + */ + void (*done)(void *data, + struct zxdg_output_v1 *zxdg_output_v1); + /** + * name of this output + * + * Many compositors will assign names to their outputs, show them + * to the user, allow them to be configured by name, etc. The + * client may wish to know this name as well to offer the user + * similar behaviors. + * + * The naming convention is compositor defined, but limited to + * alphanumeric characters and dashes (-). Each name is unique + * among all wl_output globals, but if a wl_output global is + * destroyed the same name may be reused later. The names will also + * remain consistent across sessions with the same hardware and + * software configuration. + * + * 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 creating an xdg_output (see + * xdg_output_manager.get_xdg_output). This event is only sent once + * per xdg_output, and the name does not change over the lifetime + * of the wl_output global. + * + * This event is deprecated, instead clients should use + * wl_output.name. Compositors must still support this event. + * @param name output name + * @since 2 + */ + void (*name)(void *data, + struct zxdg_output_v1 *zxdg_output_v1, + const char *name); + /** + * 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, to communicate the user for various purposes. + * + * The description is a UTF-8 string with no convention defined for + * its contents. Examples might include 'Foocorp 11" Display' or + * 'Virtual X11 output via :1'. + * + * The description event is sent after creating an xdg_output (see + * xdg_output_manager.get_xdg_output) and whenever the description + * changes. The description is optional, and may not be sent at + * all. + * + * For objects of version 2 and lower, this event is only sent once + * per xdg_output, and the description does not change over the + * lifetime of the wl_output global. + * + * This event is deprecated, instead clients should use + * wl_output.description. Compositors must still support this + * event. + * @param description output description + * @since 2 + */ + void (*description)(void *data, + struct zxdg_output_v1 *zxdg_output_v1, + const char *description); +}; + +/** + * @ingroup iface_zxdg_output_v1 + */ +static inline int +zxdg_output_v1_add_listener(struct zxdg_output_v1 *zxdg_output_v1, + const struct zxdg_output_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zxdg_output_v1, + (void (**)(void)) listener, data); +} + +#define ZXDG_OUTPUT_V1_DESTROY 0 + +/** + * @ingroup iface_zxdg_output_v1 + */ +#define ZXDG_OUTPUT_V1_LOGICAL_POSITION_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_output_v1 + */ +#define ZXDG_OUTPUT_V1_LOGICAL_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_output_v1 + */ +#define ZXDG_OUTPUT_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_output_v1 + */ +#define ZXDG_OUTPUT_V1_NAME_SINCE_VERSION 2 +/** + * @ingroup iface_zxdg_output_v1 + */ +#define ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION 2 + +/** + * @ingroup iface_zxdg_output_v1 + */ +#define ZXDG_OUTPUT_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_output_v1 */ +static inline void +zxdg_output_v1_set_user_data(struct zxdg_output_v1 *zxdg_output_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_output_v1, user_data); +} + +/** @ingroup iface_zxdg_output_v1 */ +static inline void * +zxdg_output_v1_get_user_data(struct zxdg_output_v1 *zxdg_output_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_output_v1); +} + +static inline uint32_t +zxdg_output_v1_get_version(struct zxdg_output_v1 *zxdg_output_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_output_v1); +} + +/** + * @ingroup iface_zxdg_output_v1 + * + * Using this request a client can tell the server that it is not + * going to use the xdg_output object anymore. + */ +static inline void +zxdg_output_v1_destroy(struct zxdg_output_v1 *zxdg_output_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_output_v1, + ZXDG_OUTPUT_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_output_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-output-unstable-v1-protocol.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-output-unstable-v1-protocol.c new file mode 100644 index 000000000000..acb89285552e --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-output-unstable-v1-protocol.c @@ -0,0 +1,79 @@ +/* Generated by wayland-scanner 1.26.0 */ + +/* + * Copyright © 2017 Red Hat Inc. + * + * 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. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface zxdg_output_v1_interface; + +static const struct wl_interface *xdg_output_unstable_v1_types[] = { + NULL, + NULL, + &zxdg_output_v1_interface, + &wl_output_interface, +}; + +static const struct wl_message zxdg_output_manager_v1_requests[] = { + { "destroy", "", xdg_output_unstable_v1_types + 0 }, + { "get_xdg_output", "no", xdg_output_unstable_v1_types + 2 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_output_manager_v1_interface = { + "zxdg_output_manager_v1", 3, + 2, zxdg_output_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zxdg_output_v1_requests[] = { + { "destroy", "", xdg_output_unstable_v1_types + 0 }, +}; + +static const struct wl_message zxdg_output_v1_events[] = { + { "logical_position", "ii", xdg_output_unstable_v1_types + 0 }, + { "logical_size", "ii", xdg_output_unstable_v1_types + 0 }, + { "done", "", xdg_output_unstable_v1_types + 0 }, + { "name", "2s", xdg_output_unstable_v1_types + 0 }, + { "description", "2s", xdg_output_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_output_v1_interface = { + "zxdg_output_v1", 3, + 1, zxdg_output_v1_requests, + 5, zxdg_output_v1_events, +}; + diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-shell-client-protocol.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-shell-client-protocol.h new file mode 100644 index 000000000000..64470e4e9163 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-shell-client-protocol.h @@ -0,0 +1,2392 @@ +/* Generated by wayland-scanner 1.26.0 */ + +#ifndef XDG_SHELL_CLIENT_PROTOCOL_H +#define XDG_SHELL_CLIENT_PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_shell The xdg_shell protocol + * @section page_ifaces_xdg_shell Interfaces + * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces + * - @subpage page_iface_xdg_positioner - child surface positioner + * - @subpage page_iface_xdg_surface - desktop user interface surface base interface + * - @subpage page_iface_xdg_toplevel - toplevel surface + * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus + * @section page_copyright_xdg_shell Copyright + * <pre> + * + * Copyright © 2008-2013 Kristian Høgsberg + * Copyright © 2013 Rafael Antognolli + * Copyright © 2013 Jasper St. Pierre + * Copyright © 2010-2013 Intel Corporation + * Copyright © 2015-2017 Samsung Electronics Co., Ltd + * Copyright © 2015-2017 Red Hat Inc. + * + * 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. + * </pre> + */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct xdg_popup; +struct xdg_positioner; +struct xdg_surface; +struct xdg_toplevel; +struct xdg_wm_base; + +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE +/** + * @page page_iface_xdg_wm_base xdg_wm_base + * @section page_iface_xdg_wm_base_desc Description + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + * @section page_iface_xdg_wm_base_api API + * See @ref iface_xdg_wm_base. + */ +/** + * @defgroup iface_xdg_wm_base The xdg_wm_base interface + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + */ +extern const struct wl_interface xdg_wm_base_interface; +#endif +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE +/** + * @page page_iface_xdg_positioner xdg_positioner + * @section page_iface_xdg_positioner_desc Description + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + * @section page_iface_xdg_positioner_api API + * See @ref iface_xdg_positioner. + */ +/** + * @defgroup iface_xdg_positioner The xdg_positioner interface + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + */ +extern const struct wl_interface xdg_positioner_interface; +#endif +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE +/** + * @page page_iface_xdg_surface xdg_surface + * @section page_iface_xdg_surface_desc Description + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up (e.g. by sending + * the title, app ID, size constraints, parent, etc), the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + * @section page_iface_xdg_surface_api API + * See @ref iface_xdg_surface. + */ +/** + * @defgroup iface_xdg_surface The xdg_surface interface + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up (e.g. by sending + * the title, app ID, size constraints, parent, etc), the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + */ +extern const struct wl_interface xdg_surface_interface; +#endif +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE +/** + * @page page_iface_xdg_toplevel xdg_toplevel + * @section page_iface_xdg_toplevel_desc Description + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * An xdg_toplevel by default is responsible for providing the full intended + * visual representation of the toplevel, which depending on the window + * state, may mean things like a title bar, window controls and drop shadow. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by performing a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + * @section page_iface_xdg_toplevel_api API + * See @ref iface_xdg_toplevel. + */ +/** + * @defgroup iface_xdg_toplevel The xdg_toplevel interface + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * An xdg_toplevel by default is responsible for providing the full intended + * visual representation of the toplevel, which depending on the window + * state, may mean things like a title bar, window controls and drop shadow. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by performing a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + */ +extern const struct wl_interface xdg_toplevel_interface; +#endif +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE +/** + * @page page_iface_xdg_popup xdg_popup + * @section page_iface_xdg_popup_desc Description + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + * @section page_iface_xdg_popup_api API + * See @ref iface_xdg_popup. + */ +/** + * @defgroup iface_xdg_popup The xdg_popup interface + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + */ +extern const struct wl_interface xdg_popup_interface; +#endif + +#ifndef XDG_WM_BASE_ERROR_ENUM +#define XDG_WM_BASE_ERROR_ENUM +enum xdg_wm_base_error { + /** + * given wl_surface has another role + */ + XDG_WM_BASE_ERROR_ROLE = 0, + /** + * xdg_wm_base was destroyed before children + */ + XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, + /** + * the client tried to map or destroy a non-topmost popup + */ + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, + /** + * the client specified an invalid popup parent surface + */ + XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, + /** + * the client provided an invalid surface state + */ + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, + /** + * the client provided an invalid positioner + */ + XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, + /** + * the client didn’t respond to a ping event in time + */ + XDG_WM_BASE_ERROR_UNRESPONSIVE = 6, +}; +#endif /* XDG_WM_BASE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_wm_base + * @struct xdg_wm_base_listener + */ +struct xdg_wm_base_listener { + /** + * check if the client is alive + * + * The ping event asks the client if it's still alive. Pass the + * serial specified in the event back to the compositor by sending + * a "pong" request back with the specified serial. See + * xdg_wm_base.pong. + * + * Compositors can use this to determine if the client is still + * alive. It's unspecified what will happen if the client doesn't + * respond to the ping request, or in what timeframe. Clients + * should try to respond in a reasonable amount of time. The + * “unresponsive” error is provided for compositors that wish + * to disconnect unresponsive clients. + * + * A compositor is free to ping in any way it wants, but a client + * must always respond to any xdg_wm_base object it created. + * @param serial pass this to the pong request + */ + void (*ping)(void *data, + struct xdg_wm_base *xdg_wm_base, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_wm_base + */ +static inline int +xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, + const struct xdg_wm_base_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, + (void (**)(void)) listener, data); +} + +#define XDG_WM_BASE_DESTROY 0 +#define XDG_WM_BASE_CREATE_POSITIONER 1 +#define XDG_WM_BASE_GET_XDG_SURFACE 2 +#define XDG_WM_BASE_PONG 3 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PING_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PONG_SINCE_VERSION 1 + +/** @ingroup iface_xdg_wm_base */ +static inline void +xdg_wm_base_set_user_data(struct xdg_wm_base *xdg_wm_base, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_wm_base, user_data); +} + +/** @ingroup iface_xdg_wm_base */ +static inline void * +xdg_wm_base_get_user_data(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_wm_base); +} + +static inline uint32_t +xdg_wm_base_get_version(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_wm_base); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Destroy this xdg_wm_base object. + * + * Destroying a bound xdg_wm_base object while there are surfaces + * still alive created by this xdg_wm_base object instance is illegal + * and will result in a defunct_surfaces error. + */ +static inline void +xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Create a positioner object. A positioner object is used to position + * surfaces relative to some parent surface. See the interface description + * and xdg_surface.get_popup for details. + */ +static inline struct xdg_positioner * +xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL); + + return (struct xdg_positioner *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * This creates an xdg_surface for the given surface. An xdg_surface is + * used as basis to define a role to a given surface, such as xdg_toplevel + * or xdg_popup. It also manages functionality shared between xdg_surface + * based surface roles. + * + * While xdg_surface itself is not a role, the corresponding surface may + * only be assigned a role extending xdg_surface, such as xdg_toplevel or + * xdg_popup. It is illegal to create an xdg_surface for a wl_surface which + * already has anassigned role and this will result in a role error. + * + * See the documentation of xdg_surface for more details about what an + * xdg_surface is and how it is used. + */ +static inline struct xdg_surface * +xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface); + + return (struct xdg_surface *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * A client must respond to a ping event with a pong request or + * the client may be deemed unresponsive. See xdg_wm_base.ping + * and xdg_wm_base.error.unresponsive. + */ +static inline void +xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, serial); +} + +#ifndef XDG_POSITIONER_ERROR_ENUM +#define XDG_POSITIONER_ERROR_ENUM +enum xdg_positioner_error { + /** + * invalid input provided + */ + XDG_POSITIONER_ERROR_INVALID_INPUT = 0, +}; +#endif /* XDG_POSITIONER_ERROR_ENUM */ + +#ifndef XDG_POSITIONER_ANCHOR_ENUM +#define XDG_POSITIONER_ANCHOR_ENUM +enum xdg_positioner_anchor { + XDG_POSITIONER_ANCHOR_NONE = 0, + XDG_POSITIONER_ANCHOR_TOP = 1, + XDG_POSITIONER_ANCHOR_BOTTOM = 2, + XDG_POSITIONER_ANCHOR_LEFT = 3, + XDG_POSITIONER_ANCHOR_RIGHT = 4, + XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, + XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, + XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_ANCHOR_ENUM */ + +#ifndef XDG_POSITIONER_GRAVITY_ENUM +#define XDG_POSITIONER_GRAVITY_ENUM +enum xdg_positioner_gravity { + XDG_POSITIONER_GRAVITY_NONE = 0, + XDG_POSITIONER_GRAVITY_TOP = 1, + XDG_POSITIONER_GRAVITY_BOTTOM = 2, + XDG_POSITIONER_GRAVITY_LEFT = 3, + XDG_POSITIONER_GRAVITY_RIGHT = 4, + XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, + XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, + XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_GRAVITY_ENUM */ + +#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +/** + * @ingroup iface_xdg_positioner + * constraint adjustments + * + * The constraint adjustment value define ways the compositor will adjust + * the position of the surface, if the unadjusted position would result + * in the surface being partly constrained. + * + * Whether a surface is considered 'constrained' is left to the compositor + * to determine. For example, the surface may be partly outside the + * compositor's defined 'work area', thus necessitating the child surface's + * position be adjusted until it is entirely inside the work area. + * + * The adjustments can be combined, according to a defined precedence: 1) + * Flip, 2) Slide, 3) Resize. + */ +enum xdg_positioner_constraint_adjustment { + /** + * don't move the child surface when constrained + * + * Don't alter the surface position even if it is constrained on + * some axis, for example partially outside the edge of an output. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, + /** + * move along the x axis until unconstrained + * + * Slide the surface along the x axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the x + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the x axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, + /** + * move along the y axis until unconstrained + * + * Slide the surface along the y axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the y + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the y axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, + /** + * invert the anchor and gravity on the x axis + * + * Invert the anchor and gravity on the x axis if the surface is + * constrained on the x axis. For example, if the left edge of the + * surface is constrained, the gravity is 'left' and the anchor is + * 'left', change the gravity to 'right' and the anchor to 'right'. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_x adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, + /** + * invert the anchor and gravity on the y axis + * + * Invert the anchor and gravity on the y axis if the surface is + * constrained on the y axis. For example, if the bottom edge of + * the surface is constrained, the gravity is 'bottom' and the + * anchor is 'bottom', change the gravity to 'top' and the anchor + * to 'top'. + * + * The adjusted position is calculated given the original anchor + * rectangle and offset, but with the new flipped anchor and + * gravity values. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_y adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, + /** + * horizontally resize the surface + * + * Resize the surface horizontally so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, + /** + * vertically resize the surface + * + * Resize the surface vertically so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, +}; +#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ + +#define XDG_POSITIONER_DESTROY 0 +#define XDG_POSITIONER_SET_SIZE 1 +#define XDG_POSITIONER_SET_ANCHOR_RECT 2 +#define XDG_POSITIONER_SET_ANCHOR 3 +#define XDG_POSITIONER_SET_GRAVITY 4 +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 +#define XDG_POSITIONER_SET_OFFSET 6 +#define XDG_POSITIONER_SET_REACTIVE 7 +#define XDG_POSITIONER_SET_PARENT_SIZE 8 +#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 + + +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 + +/** @ingroup iface_xdg_positioner */ +static inline void +xdg_positioner_set_user_data(struct xdg_positioner *xdg_positioner, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_positioner, user_data); +} + +/** @ingroup iface_xdg_positioner */ +static inline void * +xdg_positioner_get_user_data(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_positioner); +} + +static inline uint32_t +xdg_positioner_get_version(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_positioner); +} + +/** + * @ingroup iface_xdg_positioner + * + * Notify the compositor that the xdg_positioner will no longer be used. + */ +static inline void +xdg_positioner_destroy(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the size of the surface that is to be positioned with the positioner + * object. The size is in surface-local coordinates and corresponds to the + * window geometry. See xdg_surface.set_window_geometry. + * + * If a zero or negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the anchor rectangle within the parent surface that the child + * surface will be placed relative to. The rectangle is relative to the + * window geometry as defined by xdg_surface.set_window_geometry of the + * parent surface. + * + * When the xdg_positioner object is used to position a child surface, the + * anchor rectangle may not extend outside the window geometry of the + * positioned child's parent surface. + * + * If a negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines the anchor point for the anchor rectangle. The specified anchor + * is used to derive an anchor point that the child surface will be + * positioned relative to. If a corner anchor is set (e.g. 'top_left' or + * 'bottom_right'), the anchor point will be at the specified corner; + * otherwise, the derived anchor point will be centered on the specified + * edge, or in the center of the anchor rectangle if no edge is specified. + */ +static inline void +xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines in what direction a surface should be positioned, relative to + * the anchor point of the parent surface. If a corner gravity is + * specified (e.g. 'bottom_right' or 'top_left'), then the child surface + * will be placed towards the specified gravity; otherwise, the child + * surface will be centered over the anchor point on any axis that had no + * gravity specified. If the gravity is not in the ‘gravity’ enum, an + * invalid_input error is raised. + */ +static inline void +xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify how the window should be positioned if the originally intended + * position caused the surface to be constrained, meaning at least + * partially outside positioning boundaries set by the compositor. The + * adjustment is set by constructing a bitmask describing the adjustment to + * be made when the surface is constrained on that axis. + * + * If no bit for one axis is set, the compositor will assume that the child + * surface should not change its position on that axis when constrained. + * + * If more than one bit for one axis is set, the order of how adjustments + * are applied is specified in the corresponding adjustment descriptions. + * + * The default adjustment is none. + */ +static inline void +xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the surface position offset relative to the position of the + * anchor on the anchor rectangle and the anchor on the surface. For + * example if the anchor of the anchor rectangle is at (x, y), the surface + * has the gravity bottom|right, and the offset is (ox, oy), the calculated + * surface position will be (x + ox, y + oy). The offset position of the + * surface is the one used for constraint testing. See + * set_constraint_adjustment. + * + * An example use case is placing a popup menu on top of a user interface + * element, while aligning the user interface element of the parent surface + * with some user interface element placed somewhere in the popup surface. + */ +static inline void +xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y); +} + +/** + * @ingroup iface_xdg_positioner + * + * When set reactive, the surface is reconstrained if the conditions used + * for constraining changed, e.g. the parent window moved. + * + * If the conditions changed and the popup was reconstrained, an + * xdg_popup.configure event is sent with updated geometry, followed by an + * xdg_surface.configure event. + */ +static inline void +xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_REACTIVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the parent window geometry the compositor should use when + * positioning the popup. The compositor may use this information to + * determine the future state the popup should be constrained using. If + * this doesn't match the dimension of the parent the popup is eventually + * positioned against, the behavior is undefined. + * + * The arguments are given in the surface-local coordinate space. + */ +static inline void +xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, parent_width, parent_height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the serial of an xdg_surface.configure event this positioner will be + * used in response to. The compositor may use this information together + * with set_parent_size to determine what future state the popup should be + * constrained using. + */ +static inline void +xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, serial); +} + +#ifndef XDG_SURFACE_ERROR_ENUM +#define XDG_SURFACE_ERROR_ENUM +enum xdg_surface_error { + /** + * Surface was not fully constructed + */ + XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, + /** + * Surface was already constructed + */ + XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, + /** + * Attaching a buffer to an unconfigured surface + */ + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, + /** + * Invalid serial number when acking a configure event + */ + XDG_SURFACE_ERROR_INVALID_SERIAL = 4, + /** + * Width or height was zero or negative + */ + XDG_SURFACE_ERROR_INVALID_SIZE = 5, + /** + * Surface was destroyed before its role object + */ + XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT = 6, +}; +#endif /* XDG_SURFACE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_surface + * @struct xdg_surface_listener + */ +struct xdg_surface_listener { + /** + * suggest a surface change + * + * The configure event marks the end of a configure sequence. A + * configure sequence is a set of one or more events configuring + * the state of the xdg_surface, including the final + * xdg_surface.configure event. + * + * Where applicable, xdg_surface surface roles will during a + * configure sequence extend this event as a latched state sent as + * events before the xdg_surface.configure event. Such events + * should be considered to make up a set of atomically applied + * configuration states, where the xdg_surface.configure commits + * the accumulated state. + * + * Clients should arrange their surface for the new states, and + * then send an ack_configure request with the serial sent in this + * configure event at some point before committing the new surface. + * + * If the client receives multiple configure events before it can + * respond to one, it is free to discard all but the last event it + * received. + * @param serial serial of the configure event + */ + void (*configure)(void *data, + struct xdg_surface *xdg_surface, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_surface + */ +static inline int +xdg_surface_add_listener(struct xdg_surface *xdg_surface, + const struct xdg_surface_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, + (void (**)(void)) listener, data); +} + +#define XDG_SURFACE_DESTROY 0 +#define XDG_SURFACE_GET_TOPLEVEL 1 +#define XDG_SURFACE_GET_POPUP 2 +#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3 +#define XDG_SURFACE_ACK_CONFIGURE 4 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 + +/** @ingroup iface_xdg_surface */ +static inline void +xdg_surface_set_user_data(struct xdg_surface *xdg_surface, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data); +} + +/** @ingroup iface_xdg_surface */ +static inline void * +xdg_surface_get_user_data(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface); +} + +static inline uint32_t +xdg_surface_get_version(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_surface); +} + +/** + * @ingroup iface_xdg_surface + * + * Destroy the xdg_surface object. An xdg_surface must only be destroyed + * after its role object has been destroyed, otherwise + * a defunct_role_object error is raised. + */ +static inline void +xdg_surface_destroy(struct xdg_surface *xdg_surface) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_toplevel object for the given xdg_surface and gives + * the associated wl_surface the xdg_toplevel role. + * + * See the documentation of xdg_toplevel for more details about what an + * xdg_toplevel is and how it is used. + */ +static inline struct xdg_toplevel * +xdg_surface_get_toplevel(struct xdg_surface *xdg_surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL); + + return (struct xdg_toplevel *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_popup object for the given xdg_surface and gives + * the associated wl_surface the xdg_popup role. + * + * If null is passed as a parent, a parent surface must be specified using + * some other protocol, before committing the initial state. + * + * See the documentation of xdg_popup for more details about what an + * xdg_popup is and how it is used. + */ +static inline struct xdg_popup * +xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner); + + return (struct xdg_popup *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * The window geometry of a surface is its "visible bounds" from the + * user's perspective. Client-side decorations often have invisible + * portions like drop-shadows which should be ignored for the + * purposes of aligning, placing and constraining windows. Note that + * in some situations, compositors may clip rendering to the window + * geometry, so the client should avoid putting functional elements + * outside of it. + * + * The window geometry is double-buffered state, see wl_surface.commit. + * + * When maintaining a position, the compositor should treat the (x, y) + * coordinate of the window geometry as the top left corner of the window. + * A client changing the (x, y) window geometry coordinate should in + * general not alter the position of the window. + * + * Once the window geometry of the surface is set, it is not possible to + * unset it, and it will remain the same until set_window_geometry is + * called again, even if a new subsurface or buffer is attached. + * + * If never set, the value is the full bounds of the surface, + * including any subsurfaces. This updates dynamically on every + * commit. This unset is meant for extremely simple clients. + * + * The arguments are given in the surface-local coordinate space of + * the wl_surface associated with this xdg_surface, and may extend outside + * of the wl_surface itself to mark parts of the subsurface tree as part of + * the window geometry. + * + * When applied, the effective window geometry will be the set window + * geometry clamped to the bounding rectangle of the combined + * geometry of the surface of the xdg_surface and the associated + * subsurfaces. + * + * The effective geometry will not be recalculated unless a new call to + * set_window_geometry is done and the new pending surface state is + * subsequently applied. + * + * The width and height of the effective window geometry must be + * greater than zero. Setting an invalid size will raise an + * invalid_size error. + */ +static inline void +xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_surface + * + * When a configure event is received, if a client commits the + * surface in response to the configure event, then the client + * must make an ack_configure request sometime before the commit + * request, passing along the serial of the configure event. + * + * For instance, for toplevel surfaces the compositor might use this + * information to move a surface to the top left only when the client has + * drawn itself for the maximized or fullscreen state. + * + * If the client receives multiple configure events before it + * can respond to one, it only has to ack the last configure event. + * Acking a configure event that was never sent raises an invalid_serial + * error. + * + * A client is not required to commit immediately after sending + * an ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before committing, but + * only the last request sent before a commit indicates which configure + * event the client really is responding to. + * + * Sending an ack_configure request consumes the serial number sent with + * the request, as well as serial numbers sent by all configure events + * sent on this xdg_surface prior to the configure event referenced by + * the committed serial. + * + * It is an error to issue multiple ack_configure requests referencing a + * serial from the same configure event, or to issue an ack_configure + * request referencing a serial from a configure event issued before the + * event identified by the last ack_configure request for the same + * xdg_surface. Doing so will raise an invalid_serial error. + */ +static inline void +xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, serial); +} + +#ifndef XDG_TOPLEVEL_ERROR_ENUM +#define XDG_TOPLEVEL_ERROR_ENUM +enum xdg_toplevel_error { + /** + * provided value is not a valid variant of the resize_edge enum + */ + XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0, + /** + * invalid parent toplevel + */ + XDG_TOPLEVEL_ERROR_INVALID_PARENT = 1, + /** + * client provided an invalid min or max size + */ + XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2, +}; +#endif /* XDG_TOPLEVEL_ERROR_ENUM */ + +#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM +#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM +/** + * @ingroup iface_xdg_toplevel + * edge values for resizing + * + * These values are used to indicate which edge of a surface + * is being dragged in a resize operation. + */ +enum xdg_toplevel_resize_edge { + XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, + XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, + XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, + XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, +}; +#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ + +#ifndef XDG_TOPLEVEL_STATE_ENUM +#define XDG_TOPLEVEL_STATE_ENUM +/** + * @ingroup iface_xdg_toplevel + * types of state on the surface + * + * The different state values used on the surface. This is designed for + * state values like maximized, fullscreen. It is paired with the + * configure event to ensure that both the client and the compositor + * setting the state can be synchronized. + * + * States set in this way are double-buffered, see wl_surface.commit. + */ +enum xdg_toplevel_state { + /** + * the surface is maximized + * the surface is maximized + * + * The surface is maximized. The window geometry specified in the + * configure event must be obeyed by the client, or the + * xdg_wm_base.invalid_surface_state error is raised. + * + * The client should draw without shadow or other decoration + * outside of the window geometry. + */ + XDG_TOPLEVEL_STATE_MAXIMIZED = 1, + /** + * the surface is fullscreen + * the surface is fullscreen + * + * The surface is fullscreen. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. For a surface to cover the whole fullscreened area, + * the geometry dimensions must be obeyed by the client. For more + * details, see xdg_toplevel.set_fullscreen. + */ + XDG_TOPLEVEL_STATE_FULLSCREEN = 2, + /** + * the surface is being resized + * the surface is being resized + * + * The surface is being resized. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. Clients that have aspect ratio or cell sizing + * configuration can use a smaller size, however. + */ + XDG_TOPLEVEL_STATE_RESIZING = 3, + /** + * the surface is now activated + * the surface is now activated + * + * Client window decorations should be painted as if the window + * is active. Do not assume this means that the window actually has + * keyboard or pointer focus. + */ + XDG_TOPLEVEL_STATE_ACTIVATED = 4, + /** + * the surface’s left edge is tiled + * + * The window is currently in a tiled layout and the left edge is + * considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the left edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_LEFT = 5, + /** + * the surface’s right edge is tiled + * + * The window is currently in a tiled layout and the right edge + * is considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the right edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, + /** + * the surface’s top edge is tiled + * + * The window is currently in a tiled layout and the top edge is + * considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the top edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_TOP = 7, + /** + * the surface’s bottom edge is tiled + * + * The window is currently in a tiled layout and the bottom edge + * is considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the bottom edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, + /** + * surface repaint is suspended + * + * The surface is currently not ordinarily being repainted; for + * example because its content is occluded by another window, or + * its outputs are switched off due to screen locking. + * @since 6 + */ + XDG_TOPLEVEL_STATE_SUSPENDED = 9, + /** + * the surface’s left edge is constrained + * + * The left edge of the window is currently constrained, meaning + * it shouldn't attempt to resize from that edge. It can for + * example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT = 10, + /** + * the surface’s right edge is constrained + * + * The right edge of the window is currently constrained, meaning + * it shouldn't attempt to resize from that edge. It can for + * example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT = 11, + /** + * the surface’s top edge is constrained + * + * The top edge of the window is currently constrained, meaning + * it shouldn't attempt to resize from that edge. It can for + * example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_TOP = 12, + /** + * the surface’s bottom edge is constrained + * + * The bottom edge of the window is currently constrained, + * meaning it shouldn't attempt to resize from that edge. It can + * for example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM = 13, +}; +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION 6 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION 7 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT_SINCE_VERSION 7 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_TOP_SINCE_VERSION 7 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM_SINCE_VERSION 7 +#endif /* XDG_TOPLEVEL_STATE_ENUM */ + +#ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +#define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +enum xdg_toplevel_wm_capabilities { + /** + * show_window_menu is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1, + /** + * set_maximized and unset_maximized are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2, + /** + * set_fullscreen and unset_fullscreen are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3, + /** + * set_minimized is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4, +}; +#endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM */ + +/** + * @ingroup iface_xdg_toplevel + * @struct xdg_toplevel_listener + */ +struct xdg_toplevel_listener { + /** + * suggest a surface change + * + * This configure event asks the client to resize its toplevel + * surface or to change its state. The configured state should not + * be applied immediately. See xdg_surface.configure for details. + * + * The width and height arguments specify a hint to the window + * about how its surface should be resized in window geometry + * coordinates. See set_window_geometry. + * + * If the width or height arguments are zero, it means the client + * should decide its own window dimension. This may happen when the + * compositor needs to configure the state of the surface but + * doesn't have any information about any previous or expected + * dimension. + * + * The states listed in the event specify how the width/height + * arguments should be interpreted, and possibly how it should be + * drawn. + * + * The states are sent as an array of 32-bit unsigned integers in + * native endianness. State values are defined in the state enum. + * + * Clients must send an ack_configure in response to this event. + * See xdg_surface.configure and xdg_surface.ack_configure for + * details. + * @param width suggested width of window + * @param height suggested height of window + * @param states suggested states of the window + */ + void (*configure)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states); + /** + * surface wants to be closed + * + * The close event is sent by the compositor when the user wants + * the surface to be closed. This should be equivalent to the user + * clicking the close button in client-side decorations, if your + * application has any. + * + * This is only a request that the user intends to close the + * window. The client may choose to ignore this request, or show a + * dialog to ask the user to save their data, etc. + */ + void (*close)(void *data, + struct xdg_toplevel *xdg_toplevel); + /** + * recommended window geometry bounds + * + * The configure_bounds event may be sent prior to a + * xdg_toplevel.configure event to communicate the bounds a window + * geometry size is recommended to constrain to. + * + * The passed width and height are in surface coordinate space. If + * width and height are 0, it means bounds is unknown and + * equivalent to as if no configure_bounds event was ever sent for + * this surface. + * + * The bounds can for example correspond to the size of a monitor + * excluding any panels or other shell components, so that a + * surface isn't created in a way that it cannot fit. + * + * The bounds may change at any point, and in such a case, a new + * xdg_toplevel.configure_bounds will be sent, followed by + * xdg_toplevel.configure and xdg_surface.configure. + * @param width suggested maximum width of surface + * @param height suggested maximum height of surface + * @since 4 + */ + void (*configure_bounds)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height); + /** + * compositor capabilities + * + * This event advertises the capabilities supported by the + * compositor. If a capability isn't supported, clients should hide + * or disable the UI elements that expose this functionality. For + * instance, if the compositor doesn't advertise support for + * minimized toplevels, a button triggering the set_minimized + * request should not be displayed. + * + * The compositor will ignore requests it doesn't support. For + * instance, a compositor which doesn't advertise support for + * minimized will ignore set_minimized requests. + * + * Compositors must send this event once before the first + * xdg_surface.configure event. When the capabilities change, + * compositors must send this event again and then send an + * xdg_surface.configure event. + * + * The configured state should not be applied immediately. See + * xdg_surface.configure for details. + * + * The capabilities are sent as an array of 32-bit unsigned + * integers in native endianness. Capability values are defined in + * the wm_capabilities enum. + * @param capabilities array of 32-bit capabilities + * @since 5 + */ + void (*wm_capabilities)(void *data, + struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities); +}; + +/** + * @ingroup iface_xdg_toplevel + */ +static inline int +xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, + const struct xdg_toplevel_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, + (void (**)(void)) listener, data); +} + +#define XDG_TOPLEVEL_DESTROY 0 +#define XDG_TOPLEVEL_SET_PARENT 1 +#define XDG_TOPLEVEL_SET_TITLE 2 +#define XDG_TOPLEVEL_SET_APP_ID 3 +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4 +#define XDG_TOPLEVEL_MOVE 5 +#define XDG_TOPLEVEL_RESIZE 6 +#define XDG_TOPLEVEL_SET_MAX_SIZE 7 +#define XDG_TOPLEVEL_SET_MIN_SIZE 8 +#define XDG_TOPLEVEL_SET_MAXIMIZED 9 +#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10 +#define XDG_TOPLEVEL_SET_FULLSCREEN 11 +#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12 +#define XDG_TOPLEVEL_SET_MINIMIZED 13 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION 4 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION 5 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 + +/** @ingroup iface_xdg_toplevel */ +static inline void +xdg_toplevel_set_user_data(struct xdg_toplevel *xdg_toplevel, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_toplevel, user_data); +} + +/** @ingroup iface_xdg_toplevel */ +static inline void * +xdg_toplevel_get_user_data(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_toplevel); +} + +static inline uint32_t +xdg_toplevel_get_version(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_toplevel); +} + +/** + * @ingroup iface_xdg_toplevel + * + * This request destroys the role surface and unmaps the surface; + * see "Unmapping" behavior in interface section for details. + */ +static inline void +xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set the "parent" of this surface. This surface should be stacked + * above the parent surface and all other ancestor surfaces. + * + * Parent surfaces should be set on dialogs, toolboxes, or other + * "auxiliary" surfaces, so that the parent is raised when the dialog + * is raised. + * + * Setting a null parent for a child surface unsets its parent. Setting + * a null parent for a surface which currently has no parent is a no-op. + * + * Only mapped surfaces can have child surfaces. Setting a parent which + * is not mapped is equivalent to setting a null parent. If a surface + * becomes unmapped, its children's parent is set to the parent of + * the now-unmapped surface. If the now-unmapped surface has no parent, + * its children's parent is unset. If the now-unmapped surface becomes + * mapped again, its parent-child relationship is not restored. + * + * The parent toplevel must not be one of the child toplevel's + * descendants, and the parent must be different from the child toplevel, + * otherwise the invalid_parent protocol error is raised. + */ +static inline void +xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a short title for the surface. + * + * This string may be used to identify the surface in a task bar, + * window list, or other user interface elements provided by the + * compositor. + * + * The string must be encoded in UTF-8. + */ +static inline void +xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set an application identifier for the surface. + * + * The app ID identifies the general class of applications to which + * the surface belongs. The compositor can use this to group multiple + * surfaces together, or to determine how to launch a new application. + * + * For D-Bus activatable applications, the app ID is used as the D-Bus + * service name. + * + * The compositor shell will try to group application surfaces together + * by their app ID. As a best practice, it is suggested to select app + * ID's that match the basename of the application's .desktop file. + * For example, "org.freedesktop.FooViewer" where the .desktop file is + * "org.freedesktop.FooViewer.desktop". + * + * Like other properties, a set_app_id request can be sent after the + * xdg_toplevel has been mapped to update the property. + * + * See the desktop-entry specification [0] for more details on + * application identifiers and how they relate to well-known D-Bus + * names and .desktop files. + * + * [0] https://standards.freedesktop.org/desktop-entry-spec/ + */ +static inline void +xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Clients implementing client-side decorations might want to show + * a context menu when right-clicking on the decorations, giving the + * user a menu that they can use to maximize or minimize the window. + * + * This request asks the compositor to pop up such a window menu at + * the given position, relative to the local surface coordinates of + * the parent surface. There are no guarantees as to what menu items + * the window menu contains, or even if a window menu will be drawn + * at all. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. + */ +static inline void +xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start an interactive, user-driven move of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive move (touch, + * pointer, etc). + * + * The server may ignore move requests depending on the state of + * the surface (e.g. fullscreen or maximized), or if the passed serial + * is no longer valid. + * + * If triggered, the surface will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the move. It is up to the + * compositor to visually indicate that the move is taking place, such as + * updating a pointer cursor, during the move. There is no guarantee + * that the device focus will return when the move is completed. + */ +static inline void +xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start a user-driven, interactive resize of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive resize (touch, + * pointer, etc). + * + * The server may ignore resize requests depending on the state of + * the surface (e.g. fullscreen or maximized). + * + * If triggered, the client will receive configure events with the + * "resize" state enum value and the expected sizes. See the "resize" + * enum value for more details about what is required. The client + * must also acknowledge configure events using "ack_configure". After + * the resize is completed, the client will receive another "configure" + * event without the resize state. + * + * If triggered, the surface also will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the resize. It is up to the + * compositor to visually indicate that the resize is taking place, + * such as updating a pointer cursor, during the resize. There is no + * guarantee that the device focus will return when the resize is + * completed. + * + * The edges parameter specifies how the surface should be resized, and + * is one of the values of the resize_edge enum. Values not matching + * a variant of the enum will cause the invalid_resize_edge protocol error. + * The compositor may use this information to update the surface position + * for example when dragging the top left corner. The compositor may also + * use this information to adapt its behavior, e.g. choose an appropriate + * cursor image. + */ +static inline void +xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a maximum size for the window. + * + * The client can specify a maximum size so that the compositor does + * not try to configure the window beyond this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered, see wl_surface.commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the maximum + * size. The compositor may decide to ignore the values set by the + * client and request a larger size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected maximum size in the given dimension. + * As a result, a client wishing to reset the maximum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a maximum size to be smaller than the minimum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width or height will result in an + * invalid_size error. + */ +static inline void +xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a minimum size for the window. + * + * The client can specify a minimum size so that the compositor does + * not try to configure the window below this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered, see wl_surface.commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the minimum + * size. The compositor may decide to ignore the values set by the + * client and request a smaller size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected minimum size in the given dimension. + * As a result, a client wishing to reset the minimum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a minimum size to be larger than the maximum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width and height will result in an + * invalid_size error. + */ +static inline void +xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Maximize the surface. + * + * After requesting that the surface should be maximized, the compositor + * will respond by emitting a configure event. Whether this configure + * actually sets the window maximized is subject to compositor policies. + * The client must then update its content, drawing in the configured + * state. The client must also acknowledge the configure when committing + * the new content (see ack_configure). + * + * It is up to the compositor to decide how and where to maximize the + * surface, for example which output and what region of the screen should + * be used. + * + * If the surface was already maximized, the compositor will still emit + * a configure event with the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Unmaximize the surface. + * + * After requesting that the surface should be unmaximized, the compositor + * will respond by emitting a configure event. Whether this actually + * un-maximizes the window is subject to compositor policies. + * If available and applicable, the compositor will include the window + * geometry dimensions the window had prior to being maximized in the + * configure event. The client must then update its content, drawing it in + * the configured state. The client must also acknowledge the configure + * when committing the new content (see ack_configure). + * + * It is up to the compositor to position the surface after it was + * unmaximized; usually the position the surface had before maximizing, if + * applicable. + * + * If the surface was already not maximized, the compositor will still + * emit a configure event without the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface fullscreen. + * + * After requesting that the surface should be fullscreened, the + * compositor will respond by emitting a configure event. Whether the + * client is actually put into a fullscreen state is subject to compositor + * policies. The client must also acknowledge the configure when + * committing the new content (see ack_configure). + * + * The output passed by the request indicates the client's preference as + * to which display it should be set fullscreen on. If this value is NULL, + * it's up to the compositor to choose which display will be used to map + * this surface. + * + * If the surface doesn't cover the whole output, the compositor will + * position the surface in the center of the output and compensate with + * border fill covering the rest of the output. The content of the + * border fill is undefined, but should be assumed to be in some way that + * attempts to blend into the surrounding area (e.g. solid black). + * + * If the fullscreened surface is not opaque, the compositor must make + * sure that other screen content not part of the same surface tree (made + * up of subsurfaces, popups or similarly coupled surfaces) are not + * visible below the fullscreened surface. + */ +static inline void +xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface no longer fullscreen. + * + * After requesting that the surface should be unfullscreened, the + * compositor will respond by emitting a configure event. + * Whether this actually removes the fullscreen state of the client is + * subject to compositor policies. + * + * Making a surface unfullscreen sets states for the surface based on the following: + * * the state(s) it may have had before becoming fullscreen + * * any state(s) decided by the compositor + * * any state(s) requested by the client while the surface was fullscreen + * + * The compositor may include the previous window geometry dimensions in + * the configure event, if applicable. + * + * The client must also acknowledge the configure when committing the new + * content (see ack_configure). + */ +static inline void +xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Request that the compositor minimize your surface. There is no + * way to know if the surface is currently minimized, nor is there + * any way to unset minimization on this surface. + * + * If you are looking to throttle redrawing when minimized, please + * instead use the wl_surface.frame event for this, as this will + * also work with live previews on windows in Alt-Tab, Expose or + * similar compositor features. + */ +static inline void +xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +#ifndef XDG_POPUP_ERROR_ENUM +#define XDG_POPUP_ERROR_ENUM +enum xdg_popup_error { + /** + * tried to grab after being mapped + */ + XDG_POPUP_ERROR_INVALID_GRAB = 0, +}; +#endif /* XDG_POPUP_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_popup + * @struct xdg_popup_listener + */ +struct xdg_popup_listener { + /** + * configure the popup surface + * + * This event asks the popup surface to configure itself given + * the configuration. The configured state should not be applied + * immediately. See xdg_surface.configure for details. + * + * The x and y arguments represent the position the popup was + * placed at given the xdg_positioner rule, relative to the upper + * left corner of the window geometry of the parent surface. + * + * For version 2 or older, the configure event for an xdg_popup is + * only ever sent once for the initial configuration. Starting with + * version 3, it may be sent again if the popup is setup with an + * xdg_positioner with set_reactive requested, or in response to + * xdg_popup.reposition requests. + * @param x x position relative to parent surface window geometry + * @param y y position relative to parent surface window geometry + * @param width window geometry width + * @param height window geometry height + */ + void (*configure)(void *data, + struct xdg_popup *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * popup interaction is done + * + * The popup_done event is sent out when a popup is dismissed by + * the compositor. The client should destroy the xdg_popup object + * at this point. + */ + void (*popup_done)(void *data, + struct xdg_popup *xdg_popup); + /** + * signal the completion of a repositioned request + * + * The repositioned event is sent as part of a popup + * configuration sequence, together with xdg_popup.configure and + * lastly xdg_surface.configure to notify the completion of a + * reposition request. + * + * The repositioned event is to notify about the completion of a + * xdg_popup.reposition request. The token argument is the token + * passed in the xdg_popup.reposition request. + * + * Immediately after this event is emitted, xdg_popup.configure and + * xdg_surface.configure will be sent with the updated size and + * position, as well as a new configure serial. + * + * The client should optionally update the content of the popup, + * but must acknowledge the new popup configuration for the new + * position to take effect. See xdg_surface.ack_configure for + * details. + * @param token reposition request token + * @since 3 + */ + void (*repositioned)(void *data, + struct xdg_popup *xdg_popup, + uint32_t token); +}; + +/** + * @ingroup iface_xdg_popup + */ +static inline int +xdg_popup_add_listener(struct xdg_popup *xdg_popup, + const struct xdg_popup_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, + (void (**)(void)) listener, data); +} + +#define XDG_POPUP_DESTROY 0 +#define XDG_POPUP_GRAB 1 +#define XDG_POPUP_REPOSITION 2 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_GRAB_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 + +/** @ingroup iface_xdg_popup */ +static inline void +xdg_popup_set_user_data(struct xdg_popup *xdg_popup, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data); +} + +/** @ingroup iface_xdg_popup */ +static inline void * +xdg_popup_get_user_data(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup); +} + +static inline uint32_t +xdg_popup_get_version(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_popup); +} + +/** + * @ingroup iface_xdg_popup + * + * This destroys the popup. Explicitly destroying the xdg_popup + * object will also dismiss the popup, and unmap the surface. + * + * If this xdg_popup is not the "topmost" popup, the + * xdg_wm_base.not_the_topmost_popup protocol error will be sent. + */ +static inline void +xdg_popup_destroy(struct xdg_popup *xdg_popup) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_popup + * + * This request makes the created popup take an explicit grab. An explicit + * grab will be dismissed when the user dismisses the popup, or when the + * client destroys the xdg_popup. This can be done by the user clicking + * outside the surface, using the keyboard, or even locking the screen + * through closing the lid or a timeout. + * + * If the compositor denies the grab, the popup will be immediately + * dismissed. + * + * This request must be used in response to some sort of user action like a + * button press, key press, or touch down event. The serial number of the + * event should be passed as 'serial'. + * + * The parent of a grabbing popup must either be an xdg_toplevel surface or + * another xdg_popup with an explicit grab. If the parent is another + * xdg_popup it means that the popups are nested, with this popup now being + * the topmost popup. + * + * Nested popups must be destroyed in the reverse order they were created + * in, e.g. the only popup you are allowed to destroy at all times is the + * topmost one. + * + * When compositors choose to dismiss a popup, they may dismiss every + * nested grabbing popup as well. When a compositor dismisses popups, it + * will follow the same dismissing order as required from the client. + * + * If the topmost grabbing popup is destroyed, the grab will be returned to + * the parent of the popup, if that parent previously had an explicit grab. + * + * If the parent is a grabbing popup which has already been dismissed, this + * popup will be immediately dismissed. If the parent is a popup that did + * not take an explicit grab, an error will be raised. + * + * During a popup grab, the client owning the grab will receive pointer + * and touch events for all their surfaces as normal (similar to an + * "owner-events" grab in X11 parlance), while the top most grabbing popup + * will always have keyboard focus. + */ +static inline void +xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_popup + * + * Reposition an already-mapped popup. The popup will be placed given the + * details in the passed xdg_positioner object, and a + * xdg_popup.repositioned followed by xdg_popup.configure and + * xdg_surface.configure will be emitted in response. Any parameters set + * by the previous positioner will be discarded. + * + * The passed token will be sent in the corresponding + * xdg_popup.repositioned event. The new popup position will not take + * effect until the corresponding configure event is acknowledged by the + * client. See xdg_popup.repositioned for details. The token itself is + * opaque, and has no other special meaning. + * + * If multiple reposition requests are sent, the compositor may skip all + * but the last one. + * + * If the popup is repositioned in response to a configure event for its + * parent, the client should send an xdg_positioner.set_parent_configure + * and possibly an xdg_positioner.set_parent_size request to allow the + * compositor to properly constrain the popup. + * + * If the popup is repositioned together with a parent that is being + * resized, but not in response to a configure event, the client should + * send an xdg_positioner.set_parent_size request. + */ +static inline void +xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-shell-protocol.c b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-shell-protocol.c new file mode 100644 index 000000000000..33881aab5e02 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/digital-rain/xdg-shell-protocol.c @@ -0,0 +1,184 @@ +/* Generated by wayland-scanner 1.26.0 */ + +/* + * Copyright © 2008-2013 Kristian Høgsberg + * Copyright © 2013 Rafael Antognolli + * Copyright © 2013 Jasper St. Pierre + * Copyright © 2010-2013 Intel Corporation + * Copyright © 2015-2017 Samsung Electronics Co., Ltd + * Copyright © 2015-2017 Red Hat Inc. + * + * 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. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface xdg_popup_interface; +extern const struct wl_interface xdg_positioner_interface; +extern const struct wl_interface xdg_surface_interface; +extern const struct wl_interface xdg_toplevel_interface; + +static const struct wl_interface *xdg_shell_types[] = { + NULL, + NULL, + NULL, + NULL, + &xdg_positioner_interface, + &xdg_surface_interface, + &wl_surface_interface, + &xdg_toplevel_interface, + &xdg_popup_interface, + &xdg_surface_interface, + &xdg_positioner_interface, + &xdg_toplevel_interface, + &wl_seat_interface, + NULL, + NULL, + NULL, + &wl_seat_interface, + NULL, + &wl_seat_interface, + NULL, + NULL, + &wl_output_interface, + &wl_seat_interface, + NULL, + &xdg_positioner_interface, + NULL, +}; + +static const struct wl_message xdg_wm_base_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_wm_base_events[] = { + { "ping", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { + "xdg_wm_base", 7, + 4, xdg_wm_base_requests, + 1, xdg_wm_base_events, +}; + +static const struct wl_message xdg_positioner_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_positioner_interface = { + "xdg_positioner", 7, + 10, xdg_positioner_requests, + 0, NULL, +}; + +static const struct wl_message xdg_surface_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_surface_events[] = { + { "configure", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_surface_interface = { + "xdg_surface", 7, + 5, xdg_surface_requests, + 1, xdg_surface_events, +}; + +static const struct wl_message xdg_toplevel_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_toplevel_events[] = { + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, + { "configure_bounds", "4ii", xdg_shell_types + 0 }, + { "wm_capabilities", "5a", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { + "xdg_toplevel", 7, + 14, xdg_toplevel_requests, + 4, xdg_toplevel_events, +}; + +static const struct wl_message xdg_popup_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, +}; + +static const struct wl_message xdg_popup_events[] = { + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_popup_interface = { + "xdg_popup", 7, + 3, xdg_popup_requests, + 3, xdg_popup_events, +}; + diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-anomalies.tpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-anomalies.tpp new file mode 100644 index 000000000000..41b085be8a3f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-anomalies.tpp @@ -0,0 +1,52 @@ +// Optional distant encounters. Keep their rendering separate from the +// cloud volume and particle shaders so new kinds can be added independently. +static const char *hyperspace_anomaly_fragment = R"( +#version 100 +precision highp float; +uniform vec2 resolution, centre; +uniform float size, angle, shape, age, strength; +uniform int kind; +varying vec2 uv; +float noise2(vec2 p) +{ + vec2 cell=floor(p),f=fract(p);f=f*f*(3.0-2.0*f); + vec4 k=vec4(dot(cell,vec2(127.1,311.7)),dot(cell+vec2(1,0),vec2(127.1,311.7)), + dot(cell+vec2(0,1),vec2(127.1,311.7)),dot(cell+vec2(1,1),vec2(127.1,311.7))); + vec4 h=fract(sin(k+shape*83.0)*43758.5453); + return mix(mix(h.x,h.y,f.x),mix(h.z,h.w,f.x),f.y); +} +void main() +{ + vec2 pixel=(uv*resolution-centre)/resolution.y; + float c=cos(angle),s=sin(angle); + vec2 p=vec2(c*pixel.x+s*pixel.y,-s*pixel.x+c*pixel.y)/size; + vec3 color=vec3(0.0); + if (kind==0) + { + // A warm/cool binary pair with a faint shared tidal dust arc. + float orbit=age*(0.12+shape*0.10)+shape*6.28; + vec2 offset=vec2(cos(orbit),sin(orbit)*0.55)*0.24; + float a=dot(p-offset,p-offset),b=dot(p+offset*0.72,p+offset*0.72); + color=vec3(1.0,0.77,0.48)*(exp(-a*1800.0)*1.4+exp(-a*85.0)*0.045)+ + vec3(0.65,0.82,1.0)*(exp(-b*2400.0)*1.6+exp(-b*110.0)*0.04); + float ring=length(p*vec2(1.0,1.8)); + float arc=exp(-pow((ring-0.48)*19.0,2.0))*(0.25+0.75*smoothstep(-0.4,0.7,p.x)); + color+=vec3(0.36,0.43,0.53)*arc*0.018; + } else + { + // A distant, broken remnant shell with knots and dark missing arcs. + // Its structure is fixed; a very slow expansion suggests depth. + vec2 q=p/(1.0+age*0.0015); + float n=noise2(q*4.7),fine=noise2(q*15.0+23.0); + float radius=length(q*vec2(1.0,1.25)); + float rim=exp(-pow((radius-(0.58+(n-0.5)*0.24))*15.0,2.0)); + float holes=smoothstep(0.32,0.78,noise2(q*2.5+11.0)); + float knots=pow(fine,3.0); + vec3 cold=vec3(0.30,0.54,0.78),warm=vec3(0.77,0.34,0.22); + color=mix(cold,warm,shape*0.7)*rim*holes*(0.14+knots*0.45); + color+=vec3(0.77,0.87,1.0)*exp(-dot(q,q)*3500.0)*0.45; + } + color*=1.0-smoothstep(1.15,1.6,length(p)); + gl_FragColor=vec4(color*strength,0.0); +} +)"; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-asteroid-shaders.tpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-asteroid-shaders.tpp new file mode 100644 index 000000000000..4d5214ed358d --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-asteroid-shaders.tpp @@ -0,0 +1,88 @@ +// Solid geometry with an opaque silhouette: these bodies absorb background +// light rather than becoming another additive star or nebula sprite. +static const char *hyperspace_asteroid_vertex = R"( +#version 100 +precision highp float; +attribute vec3 position, normal; +uniform mat4 rotation; +uniform vec3 centre; +uniform vec2 resolution; +uniform float radius; +varying vec3 rock, rock_normal, eye; +void main() +{ + vec3 p=centre+(rotation*vec4(position*radius,0.0)).xyz; + rock=position;rock_normal=normal;eye=-p; + // Positive Z points along the journey. Match the starfield's perspective, + // with proper clip depth so front/back facets and overlapping rocks agree. + float near_plane=0.12,far_plane=160.0,focal=1.0; + gl_Position=vec4(p.x*focal/(resolution.x/resolution.y),-p.y*focal, + ((far_plane+near_plane)*p.z-2.0*near_plane*far_plane)/(far_plane-near_plane),p.z); +} +)"; +static const char *hyperspace_asteroid_fragment = R"( +#version 100 +precision highp float; +uniform vec3 albedo; +uniform float material_seed, brightness, visibility; +uniform mat4 rotation; +varying vec3 rock, rock_normal, eye; +float hash3(vec3 p) +{ + return fract(sin(dot(p,vec3(127.1,311.7,74.7))+material_seed)*43758.5453); +} +// Value and analytic gradient together: relief follows the same object-space +// minerals as the colour, without extra finite-difference texture samples. +vec4 grain(vec3 p) +{ + vec3 i=floor(p),t=fract(p); + vec3 f=t*t*t*(t*(t*6.0-15.0)+10.0); + vec3 df=30.0*t*t*(t-1.0)*(t-1.0); + float a=hash3(i),b=hash3(i+vec3(1,0,0)); + float c=hash3(i+vec3(0,1,0)),d=hash3(i+vec3(1,1,0)); + float e=hash3(i+vec3(0,0,1)),h=hash3(i+vec3(1,0,1)); + float j=hash3(i+vec3(0,1,1)),k=hash3(i+vec3(1,1,1)); + float ab=mix(a,b,f.x),cd=mix(c,d,f.x); + float eh=mix(e,h,f.x),jk=mix(j,k,f.x); + return vec4(mix(mix(ab,cd,f.y),mix(eh,jk,f.y),f.z), + df.x*mix(mix(b-a,d-c,f.y),mix(h-e,k-j,f.y),f.z), + df.y*mix(cd-ab,jk-eh,f.z),df.z*(mix(eh,jk,f.y)-mix(ab,cd,f.y))); +} +void main() +{ + vec3 local_normal=normalize(rock_normal); + vec4 strata=grain(rock*4.7+vec3(3.1,7.3,1.9)); + vec4 chips=grain(rock*14.3+vec3(17.0,2.0,9.0)); + vec4 grit=grain(rock*47.0+vec3(2.0,31.0,11.0)); + vec4 flecks=grain(rock*109.0+vec3(43.0,13.0,7.0)); + + // Interrupted fracture seams, coarse chipped relief and fine regolith. + // All scales remain attached to the body while it rotates. + float seam_distance=abs(strata.x-0.48); + float seam_width=0.025; + float seam_profile=exp(-seam_distance*seam_distance/(seam_width*seam_width)); + float seam_mask=smoothstep(0.24,0.56,chips.x); + float seam=seam_profile*seam_mask; + float seam_slope=-2.0*(strata.x-0.48)/(seam_width*seam_width)*seam_profile; + vec3 relief=strata.yzw*4.7*0.10+chips.yzw*14.3*0.018+ + grit.yzw*47.0*0.0028+flecks.yzw*109.0*0.00035- + strata.yzw*4.7*seam_slope*seam_mask*0.012; + relief-=local_normal*dot(local_normal,relief); + vec3 n=normalize((rotation*vec4(normalize(local_normal-relief),0.0)).xyz); + vec3 light=normalize(vec3(-0.45,-0.55,-0.80)); + + float mineral=0.58+strata.x*0.48+chips.x*0.32+grit.x*0.12-seam*0.30; + vec3 weathering=mix(vec3(0.82,0.85,0.90),vec3(1.07,1.04,0.97), + smoothstep(0.25,0.72,strata.x)); + float inclusions=smoothstep(0.69,0.88,flecks.x)*smoothstep(0.42,0.67,chips.x); + vec3 stone=albedo*mineral*weathering+vec3(0.12,0.115,0.10)*inclusions; + float key=max(0.0,dot(n,light)); + float bounce=max(0.0,dot(n,normalize(vec3(0.6,0.15,-0.2)))); + vec3 illumination=vec3(0.07)+vec3(0.96,0.88,0.78)*key*0.86+ + vec3(0.32,0.43,0.60)*bounce*0.10; + float sheen=pow(max(0.0,dot(n,normalize(light+normalize(eye)))),12.0)* + (0.012+inclusions*0.055)*key; + vec3 color=(stone*illumination+vec3(sheen))*brightness; + gl_FragColor=vec4(color*visibility,visibility); +} +)"; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-asteroids.tpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-asteroids.tpp new file mode 100644 index 000000000000..692441f4c33b --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-asteroids.tpp @@ -0,0 +1,191 @@ +#include "hyperspace-asteroid-shaders.tpp" + +// Optional solid encounters. Geometry, materials and scheduling live here, +// independent of stars/clouds; both compositor and lock host use this module. +// Wayfire loads plugins globally and older versions can remain resident after +// dlclose. Keep every mesh/material/trajectory method local to this version. +class __attribute__((visibility("hidden"))) ambient_asteroid_field +{ + struct vertex { glm::vec3 position, normal; }; + OpenGL::program_t program; + GLuint buffer=0,depth=0; + int mesh_level=-1,depth_width=0,depth_height=0,vertices_per_mesh=0; + uint32_t mesh_seed=0; + static constexpr int mesh_count=10; + // Short encounter fields separated by a longer stretch of open space. + // Both periods divide the bounded journey clock exactly. + static constexpr double period=384,world_period=4096*12; + + void meshes(uint32_t seed,int level) + { + if (buffer && mesh_seed==seed && mesh_level==level) return; + mesh_seed=seed;mesh_level=level; + std::vector<vertex> vertices; + const float t=(1+std::sqrt(5.f))/2; + const glm::vec3 points[]={{-1,t,0},{1,t,0},{-1,-t,0},{1,-t,0}, + {0,-1,t},{0,1,t},{0,-1,-t},{0,1,-t},{t,0,-1},{t,0,1},{-t,0,-1},{-t,0,1}}; + const int faces[][3]={{0,11,5},{0,5,1},{0,1,7},{0,7,10},{0,10,11}, + {1,5,9},{5,11,4},{11,10,2},{10,7,6},{7,1,8},{3,9,4},{3,4,2}, + {3,2,6},{3,6,8},{3,8,9},{4,9,5},{2,4,11},{6,2,10},{8,6,7},{9,8,1}}; + for (int mesh=0;mesh<mesh_count;mesh++) { + uint32_t base=seed^uint32_t(mesh*19139+0x7a231); + auto random=[base](unsigned i){return star_random(base+i*7183);}; + float phase=random(0)*6.2831853f; + glm::vec3 axes{.70f+random(1)*.55f,.65f+random(2)*.65f,.65f+random(3)*.65f}; + std::array<glm::vec3,6> craters; + for (unsigned i=0;i<craters.size();i++) + craters[i]=glm::normalize(glm::vec3{2*random(5+i*3)-1, + 2*random(6+i*3)-1,2*random(7+i*3)-1}); + std::array<glm::vec3,9> fractures; + for (unsigned i=0;i<fractures.size();i++) + fractures[i]=glm::normalize(glm::vec3{2*random(53+i*3)-1, + 2*random(54+i*3)-1,2*random(55+i*3)-1}); + auto point=[&](glm::vec3 n) { + n=glm::normalize(n); + float radial=.94f+.11f*std::sin(n.x*4.8f+phase)*std::cos(n.y*3.3f+phase*.7f)+ + .08f*std::sin(glm::dot(n,glm::vec3{5.3f,2.1f,-3.5f})+phase*3)+ + .025f*std::sin(glm::dot(n,glm::vec3{13.1f,-8.7f,9.3f})+phase); + for (unsigned i=0;i<craters.size();i++) { + float d=glm::length(n-craters[i])/(.18f+random(29+i)*.24f); + radial-=(.07f+random(41+i)*.07f)*std::exp(-d*d*3); + radial+=.02f*std::exp(-std::pow((d-.85f)*7,2.f)); + } + // Broken faces cut through the rounded parent body. A plane + // intersection gives genuine angular silhouettes and broad + // exposed facets, rather than only colouring a smooth lump. + for (unsigned i=0;i<fractures.size();i++) { + float facing=glm::dot(n,fractures[i]); + if (facing>0) + radial=std::min(radial,(.63f+random(83+i)*.27f)/facing); + } + return n*radial*axes; + }; + auto append=[&](glm::vec3 n) { + n=glm::normalize(n); + glm::vec3 a=glm::normalize(glm::cross(n,std::abs(n.y)<.9f ? + glm::vec3{0,1,0}:glm::vec3{1,0,0})),b=glm::cross(n,a); + glm::vec3 da=point(n+a*.012f)-point(n-a*.012f),db=point(n+b*.012f)-point(n-b*.012f); + vertices.push_back({point(n),glm::normalize(glm::cross(da,db))}); + }; + std::function<void(glm::vec3,glm::vec3,glm::vec3,int)> triangle; + triangle=[&](glm::vec3 a,glm::vec3 b,glm::vec3 c,int subdivisions) { + if (!subdivisions) { append(a);append(b);append(c);return; } + glm::vec3 ab=glm::normalize(a+b),bc=glm::normalize(b+c),ca=glm::normalize(c+a); + triangle(a,ab,ca,subdivisions-1);triangle(b,bc,ab,subdivisions-1); + triangle(c,ca,bc,subdivisions-1);triangle(ab,bc,ca,subdivisions-1); + }; + for (const auto& face:faces) + triangle(glm::normalize(points[face[0]]),glm::normalize(points[face[1]]), + glm::normalize(points[face[2]]),level==2 ? 3 : 2); + } + vertices_per_mesh=vertices.size()/mesh_count; + if (!buffer) GL_CALL(glGenBuffers(1,&buffer)); + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER,buffer)); + GL_CALL(glBufferData(GL_ARRAY_BUFFER,vertices.size()*sizeof(vertex),vertices.data(),GL_STATIC_DRAW)); + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER,0)); + } + + public: + ~ambient_asteroid_field() { + program.free_resources(); + if (buffer) GL_CALL(glDeleteBuffers(1,&buffer)); + if (depth) GL_CALL(glDeleteRenderbuffers(1,&depth)); + } + void render(const wf::render_target_t& target,const wf::geometry_t& box, + uint32_t seed,double clock,int level,double density,double brightness) + { + meshes(seed,level); + if (!program.get_program_id(wf::TEXTURE_TYPE_RGBA)) + program.set_simple(OpenGL::compile_program(hyperspace_asteroid_vertex,hyperspace_asteroid_fragment)); + // Only our cached backdrop owns this depth attachment. The destination + // output and Cube/lock buffers are never cleared or reconfigured here. + GLint viewport[4],old_renderbuffer,old_depth_func; + GLboolean old_depth_mask; + GL_CALL(glGetIntegerv(GL_VIEWPORT,viewport)); + GL_CALL(glGetIntegerv(GL_RENDERBUFFER_BINDING,&old_renderbuffer)); + GL_CALL(glGetIntegerv(GL_DEPTH_FUNC,&old_depth_func)); + GL_CALL(glGetBooleanv(GL_DEPTH_WRITEMASK,&old_depth_mask)); + if (!depth) GL_CALL(glGenRenderbuffers(1,&depth)); + GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER,depth)); + if (depth_width!=viewport[2] || depth_height!=viewport[3]) { + depth_width=viewport[2];depth_height=viewport[3]; + GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT16,depth_width,depth_height)); + } + GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,depth)); + GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER,old_renderbuffer)); + GL_CALL(glEnable(GL_DEPTH_TEST));GL_CALL(glDepthFunc(GL_LEQUAL));GL_CALL(glDepthMask(GL_TRUE)); + wf::gles::for_each_scissor_rect(target,box,[&]{GL_CALL(glClear(GL_DEPTH_BUFFER_BIT));}); + GL_CALL(glEnable(GL_BLEND));GL_CALL(glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA)); + program.use(wf::TEXTURE_TYPE_RGBA); + program.uniform2f("resolution",box.width,box.height); + program.uniform1f("brightness",brightness); + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER,buffer)); + program.attrib_pointer("position",3,sizeof(vertex),reinterpret_cast<void*>(offsetof(vertex,position))); + program.attrib_pointer("normal",3,sizeof(vertex),reinterpret_cast<void*>(offsetof(vertex,normal))); + const double aspect=box.width/box.height; + const int cycle=int(std::floor(clock/period)); + const int count=int(64*density); + for (int field=cycle;field<=cycle+1;field++) { + const unsigned wrapped=(field+unsigned(world_period/period))%unsigned(world_period/period); + uint32_t field_seed=seed^uint32_t(wrapped*0x9e3779b9u)^0x891723u; + for (int i=0;i<count;i++) { + uint32_t base=field_seed^uint32_t(i*15341+917); + auto random=[base](unsigned j){return star_random(base+j*19139);}; + // Keep encounter scheduling on the shared journey clock, but + // cross the visible depth range quickly. Multiplying the + // whole clock would also make fields arrive more frequently. + const double arrival=field*period+1+random(0)*90; + if ((arrival-clock)*64>140) continue; + // Independent, logarithmic size draws give fragments, rocks + // and boulders distinct scales. Two percent of encounters are + // huge bodies; field scheduling and density stay unchanged. + const float size_class=random(2); + float radius; + if (size_class<.50f) radius=.04f*std::pow(7.5f,random(1)); + else if (size_class<.82f) radius=.30f*std::pow(2.3f/.30f,random(1)); + else if (size_class<.98f) radius=2.3f*std::pow(7.f/2.3f,random(1)); + else radius=10.f*std::pow(2.f,random(13)); + // A huge silhouette takes longer to clear the viewport at + // the same world velocity. Give larger bodies extra travel + // speed so they still make brief, fast passes at the edges. + const float z=(arrival-clock)*(64+radius*4); + if (z>140) continue; + // Approach along the central flight corridor, then let real + // perspective carry the body past the camera and out of view. + // World coordinates keep arrivals centred on a panorama too. + float angle=random(3)*2*M_PI; + // A radius-only miss distance cancels apparent size: tiny and + // huge rocks then fill roughly the same part of the screen. + // Independent clearance preserves the visible size spread, + // while the radius term keeps even a huge body off the camera. + float impact=radius*1.55f+2.f+std::pow(random(4),.75f)*8.f; + float x=std::cos(angle)*impact; + float y=std::sin(angle)*impact; + // Frustum planes test the entire bounding sphere, including + // its depth. Testing only the centre depth can drop a partly + // visible close body before its last facets leave the frame. + const float bound=radius*1.5f; + if (z+bound<=.12f || + std::abs(x)>z*aspect+bound*std::sqrt(1+aspect*aspect) || + std::abs(y)>z+bound*std::sqrt(2.f)) continue; + double phase=clock/world_period*2*M_PI; + glm::mat4 rotation{1}; + rotation=glm::rotate(rotation,float(random(6)*2*M_PI+phase*(128+int(random(7)*576))),glm::vec3{0,1,0}); + rotation=glm::rotate(rotation,float(random(8)*2*M_PI-phase*(96+int(random(9)*480))),glm::vec3{1,0,0}); + program.uniformMatrix4f("rotation",rotation); + program.uniform3f("centre",x,y,z);program.uniform1f("radius",radius); + program.uniform1f("material_seed",random(10)*100); + const glm::vec3 tones[]={{.40f,.42f,.45f},{.29f,.30f,.32f},{.46f,.32f,.23f},{.48f,.53f,.57f}}; + float material=random(11);auto tone=tones[material<.48f ? 0 : material<.86f ? 1 : material<.94f ? 2 : 3]; + program.uniform3f("albedo",tone.r,tone.g,tone.b); + float fade=std::clamp((140-z)/25.f,0.f,1.f);fade=fade*fade*(3-2*fade); + program.uniform1f("visibility",fade); + int mesh=std::min(mesh_count-1,int(random(12)*mesh_count)); + wf::gles::for_each_scissor_rect(target,box,[&]{GL_CALL(glDrawArrays(GL_TRIANGLES,mesh*vertices_per_mesh,vertices_per_mesh));}); + } + } + program.deactivate();GL_CALL(glBindBuffer(GL_ARRAY_BUFFER,0)); + GL_CALL(glDisable(GL_BLEND));GL_CALL(glDisable(GL_DEPTH_TEST)); + GL_CALL(glDepthFunc(old_depth_func));GL_CALL(glDepthMask(old_depth_mask)); + } +}; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-background.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-background.cpp new file mode 100644 index 000000000000..2e6edc36bb64 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-background.cpp @@ -0,0 +1,400 @@ +#include "hyperspace-background.hpp" +#include "hyperspace-shaders.tpp" +#include "../nebula/nebula-field.tpp" +#include <string> +#include "hyperspace-anomalies.tpp" +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <vector> +#include <random> +#include <glm/gtc/matrix_transform.hpp> + +namespace +{ +struct hyperspace_star_t +{ + float seed[4]; + float corner[2]; +}; + +float star_random(uint32_t value) +{ + value ^= value >> 16; + value *= 0x7feb352du; + value ^= value >> 15; + value *= 0x846ca68bu; + value ^= value >> 16; + return (value & 0x00ffffffu) / 16777216.0f; +} +} + +#include "hyperspace-asteroids.tpp" +#include "hyperspace-galaxies.tpp" + +ambient_hyperspace_background::ambient_hyperspace_background(std::function<void()> changed) +{ + auto update = [this, changed] { cloud_dirty = backdrop_dirty = true; changed(); }; + speed.set_callback(update); clouds.set_callback(update); turbulence.set_callback(update); + ribbons.set_callback(update); glow.set_callback(update); brightness.set_callback(update); + surge.set_callback(update); density.set_callback(update); coloured.set_callback(update); + distant_density.set_callback(update); distant_drift.set_callback(update); + galaxies.set_callback(update); fractal_detail.set_callback(update); + dust_density.set_callback(update); variation.set_callback(update); + cloud_colours.set_callback(update); + pulsars.set_callback(update); anomalies.set_callback(update); asteroids.set_callback(update); seed_option.set_callback(update); + tint.set_callback(update); quality.set_callback(update); fps.set_callback(update); + last_frame = std::chrono::steady_clock::now(); +} + +ambient_hyperspace_background::~ambient_hyperspace_background() +{ + wf::gles::run_in_context_if_gles([&] + { + cloud_program.free_resources(); star_program.free_resources(); pulsar_program.free_resources(); + anomaly_program.free_resources(); + asteroid_field.reset(); + galaxy_field.reset(); + cloud_buffer.free(); backdrop_buffer.free(); + if (star_buffer) { GL_CALL(glDeleteBuffers(1, &star_buffer)); } + }); +} + +void ambient_hyperspace_background::set_active(bool enabled) +{ + if (active != enabled) last_frame = std::chrono::steady_clock::now(); + active = enabled; +} + +void ambient_hyperspace_background::set_transition(bool moving) +{ + transition = moving; +} + +bool ambient_hyperspace_background::animated() const { return active && double(speed) > 0; } + +unsigned ambient_hyperspace_background::frame_interval() const +{ + return unsigned(std::ceil(1000.0 / std::clamp(int(fps), 10, 120))); +} + +std::array<double,24> ambient_hyperspace_background::scene() const +{ + return {double(scene_seed),double(travel_epoch),double(cloud_epoch),travel,cloud_phase,drift_phase, + double(event_number),pulsar_age,pulsar_duration,pulsar_x,pulsar_y,pulsar_angle,pulsar_rotation, + pulsar_size,pulsar_projection,double(anomaly_number),anomaly_age,anomaly_duration, + anomaly_x,anomaly_y,anomaly_size,anomaly_angle,anomaly_shape,double(anomaly_kind)}; +} + +bool ambient_hyperspace_background::restore_scene(const std::array<double,24>& s) +{ + for (double v:s) if (!std::isfinite(v)) return false; + if (s[0]<0 || s[0]>UINT32_MAX || s[1]<0 || s[1]>=4096 || s[2]<0 || s[2]>=4096 || + s[3]<0 || s[3]>=12 || s[4]<0 || s[4]>=64 || s[5]<0 || s[5]>2*M_PI || + s[6]<0 || s[6]>UINT32_MAX || s[15]<0 || s[15]>UINT32_MAX || + s[7]<-100 || s[7]>100 || s[16]<-100 || s[16]>100 || + s[8]<1 || s[8]>100 || s[17]<1 || s[17]>100 || + s[9]<0 || s[9]>1 || s[10]<0 || s[10]>1 || s[18]<0 || s[18]>1 || s[19]<0 || s[19]>1 || + s[13]<0 || s[13]>1 || s[14]<0 || s[14]>1 || s[20]<0 || s[20]>1 || + std::abs(s[11])>100 || std::abs(s[12])>10 || std::abs(s[21])>100 || + s[22]<0 || s[22]>1 || (s[23]!=0 && s[23]!=1)) return false; + configured_seed=int(seed_option);scene_seed=s[0];travel_epoch=s[1];cloud_epoch=s[2]; + travel=s[3];cloud_phase=s[4];drift_phase=s[5];event_number=s[6]; + pulsar_age=s[7];pulsar_duration=s[8];pulsar_x=s[9];pulsar_y=s[10]; + pulsar_angle=s[11];pulsar_rotation=s[12];pulsar_size=s[13];pulsar_projection=s[14]; + anomaly_number=s[15];anomaly_age=s[16];anomaly_duration=s[17];anomaly_x=s[18];anomaly_y=s[19]; + anomaly_size=s[20];anomaly_angle=s[21];anomaly_shape=s[22];anomaly_kind=s[23]; + near_count=far_count=dust_count=middle_count=-1;invalidate(); + last_frame=std::chrono::steady_clock::now();return true; +} + +void ambient_hyperspace_background::load_programs() +{ + if (!cloud_program.get_program_id(wf::TEXTURE_TYPE_RGBA)) + { + std::string fragment=hyperspace_cloud_fragment; + fragment.insert(fragment.find("uniform vec2"),ambient_nebula_field_glsl); + cloud_program.set_simple(OpenGL::compile_program(hyperspace_vertex,fragment.c_str())); + } + if (!star_program.get_program_id(wf::TEXTURE_TYPE_RGBA)) + star_program.set_simple(OpenGL::compile_program(hyperspace_star_vertex, hyperspace_star_fragment)); + if (!pulsar_program.get_program_id(wf::TEXTURE_TYPE_RGBA)) + pulsar_program.set_simple(OpenGL::compile_program(hyperspace_vertex, hyperspace_pulsar_fragment)); + if (!anomaly_program.get_program_id(wf::TEXTURE_TYPE_RGBA)) + anomaly_program.set_simple(OpenGL::compile_program(hyperspace_vertex, hyperspace_anomaly_fragment)); +} + +void ambient_hyperspace_background::prepare_pulsar() +{ + uint32_t base = scene_seed ^ (++event_number * 0x9e3779b9u); + pulsar_x = 0.12f + 0.76f * star_random(base); + pulsar_y = 0.15f + 0.70f * star_random(base + 1); + pulsar_angle = star_random(base + 2) * 2 * M_PI; + pulsar_rotation = (0.10f + 0.23f * star_random(base + 3)) * + (star_random(base + 4) < 0.5f ? -1 : 1); + pulsar_size = 0.065f + 0.095f * star_random(base + 5); + pulsar_projection = 0.45f + 0.45f * star_random(base + 6); + pulsar_duration = 10 + 10 * star_random(base + 7); +} + +void ambient_hyperspace_background::prepare_anomaly() +{ + uint32_t base = scene_seed ^ 0xa6174b29u ^ (++anomaly_number * 0x9e3779b9u); + anomaly_kind = star_random(base) < 0.5f ? 0 : 1; + anomaly_x = 0.08f + 0.84f * star_random(base + 1); + anomaly_y = 0.10f + 0.80f * star_random(base + 2); + anomaly_size = (anomaly_kind == 0 ? 0.032f : 0.065f) * (0.7f + star_random(base + 3)); + anomaly_angle = star_random(base + 4) * 2 * M_PI; + anomaly_shape = star_random(base + 5); + anomaly_duration = 14 + 12 * star_random(base + 6); +} + +void ambient_hyperspace_background::update_particles() +{ + bool reseed = configured_seed != int(seed_option); + if (reseed) + { + configured_seed = int(seed_option); + scene_seed = configured_seed > 0 ? uint32_t(configured_seed) : std::random_device{}(); + near_count = far_count = dust_count = middle_count = -1; + event_number = travel_epoch = cloud_epoch = 0; + cloud_phase = 0; + prepare_pulsar(); pulsar_age = -3; + anomaly_number = 0; + prepare_anomaly(); + anomaly_age = -(14 + 12 * star_random(scene_seed ^ 0x87a3191u)); + } + const int near_counts[] = {768, 2048, 4096}, far_counts[] = {1024, 2560, 5120}; + // Preserve fine-dust coverage after filling the full frustum. + const int dust_counts[] = {5400, 13500, 27000}; + const int middle_counts[] = {1536, 4096, 8192}; + int level = std::clamp(int(quality), 0, 2); + int near = int(near_counts[level] * std::clamp(double(density), 0.0, 3.0)); + int far = int(far_counts[level] * std::clamp(double(distant_density), 0.0, 3.0)); + int dust = int(dust_counts[level] * std::clamp(double(dust_density), 0.0, 3.0)); + int middle = int(middle_counts[level] * std::clamp(double(density), 0.0, 3.0)); + if (near == near_count && far == far_count && dust == dust_count && middle == middle_count) return; + near_count = near; far_count = far; dust_count = dust; middle_count = middle; + std::vector<hyperspace_star_t> vertices; + vertices.reserve((near + far + dust + middle) * 6); + const float corners[6][2] = {{0,-1},{1,-1},{1,1},{0,-1},{1,1},{0,1}}; + // Independent seeds retain both populations when either density changes. + // Buffer order: distant stars, dust, passing stars, middle-distance stars. + // The fourth population bridges the journey to the distant background. + // Changing any density + // retains the seeds of the other layers, including their colour balance. + const int counts[] = {far, dust, near, middle}; + const uint32_t salts[] = {419, 19139, 7183, 15341}; + for (int layer = 0; layer < 4; ++layer) + for (int i = 0; i < counts[layer]; ++i) + { + uint32_t base = (i * 4 + salts[layer]) ^ (scene_seed * 0x9e3779b9u); + float seed[] = {2 * star_random(base) - 1, 2 * star_random(base + 1) - 1, + star_random(base + 2), star_random(base + 3)}; + for (const auto& corner : corners) + vertices.push_back({{seed[0],seed[1],seed[2],seed[3]},{corner[0],corner[1]}}); + } + if (!star_buffer) { GL_CALL(glGenBuffers(1, &star_buffer)); } + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, star_buffer)); + GL_CALL(glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(hyperspace_star_t), vertices.data(), GL_STATIC_DRAW)); + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0)); +} + +void ambient_hyperspace_background::render(const wf::render_target_t& target, + const wf::regionf_t& damage, wf::dimensions_t resolution, float opacity) +{ + const auto now = std::chrono::steady_clock::now(); + const double dt = std::clamp(std::chrono::duration<double>(now - last_frame).count(), 0.0, 0.25); + update_particles(); + rendered_speed = std::clamp(double(speed), 0.0, 5.0) * (1 + boost * std::clamp(double(surge), 0.0, 2.0) * 3); + if (animated()) + { + boost += ((transition ? 1.0 : 0.0) - boost) * (1 - std::exp(-dt / (transition ? 0.18 : 0.55))); + rendered_speed = std::clamp(double(speed), 0.0, 5.0) * (1 + boost * std::clamp(double(surge), 0.0, 2.0) * 3); + const double advance = dt * rendered_speed * 1.8; + const double next_travel = travel + advance; + travel_epoch = (travel_epoch + unsigned(next_travel / 12.0)) % 4096; + travel = std::fmod(next_travel, 12.0); + // Cloud centres share the starfield's travel direction but occupy a + // much deeper volume. Bound both clocks for long-running desktops. + const double next_cloud = cloud_phase + advance * 0.65; + cloud_epoch = (cloud_epoch + unsigned(next_cloud / 64.0)) % 4096; + cloud_phase = std::fmod(next_cloud, 64.0); + // Distant stars never inherit the jump surge or foreground velocity. + drift_phase = std::fmod(drift_phase + dt * double(speed) * + std::clamp(double(distant_drift), 0.0, 1.0) * 0.04, 2 * M_PI); + // Event motion ignores workspace surges. Hidden/paused effects never + // consume their event clock, and speed zero freezes jets as well. + pulsar_age += dt * std::min(double(speed), 2.0); + if (pulsar_age > pulsar_duration) + { + const double remainder = pulsar_age - pulsar_duration; + prepare_pulsar(); + pulsar_age = remainder - (20 + 25 * star_random(scene_seed ^ event_number)); + } + // Independent encounters: rare, smooth and unhurried even during a + // workspace surge. Pausing or hiding the effect pauses this clock. + anomaly_age += dt * std::min(double(speed), 2.0); + if (anomaly_age > anomaly_duration) + { + const double remainder = anomaly_age - anomaly_duration; + prepare_anomaly(); + anomaly_age = remainder - (35 + 35 * star_random(scene_seed ^ anomaly_number ^ 0x913u)); + } + cloud_dirty = backdrop_dirty = true; + } + last_frame = now; + load_programs(); + const int level = std::clamp(int(quality), 0, 2); + const float scales[] = {0.20f, 0.25f, 1.0f / 3}; + const float cloud_scale = scales[level] * target.scale; + if (cloud_buffer.allocate(resolution, cloud_scale) == wf::buffer_reallocation_result_t::REALLOCATED) + cloud_dirty = backdrop_dirty = true; + if (backdrop_buffer.allocate(resolution, target.scale) == wf::buffer_reallocation_result_t::REALLOCATED) + backdrop_dirty = true; + wf::geometry_t box{0,0,double(resolution.width),double(resolution.height)}; + const float width = resolution.width, height = resolution.height; + const GLfloat quad[] = {0,0,width,0,width,height,0,0,width,height,0,height}; + const auto color = wf::color_t(tint); + GL_CALL(glDisable(GL_DEPTH_TEST)); GL_CALL(glDisable(GL_CULL_FACE)); + if (cloud_dirty) + { + wf::render_target_t cloud_target{cloud_buffer}; cloud_target.geometry = box; + cloud_target.scale = cloud_scale; + wf::gles::bind_render_buffer(cloud_target); + GL_CALL(glDisable(GL_BLEND)); + cloud_program.use(wf::TEXTURE_TYPE_RGBA); + cloud_program.uniformMatrix4f("mvp", wf::gles::render_target_orthographic_projection(cloud_target)); + cloud_program.uniform2f("resolution", width, height); + cloud_program.uniform1f("phase", cloud_phase); + cloud_program.uniform1f("epoch", cloud_epoch); + cloud_program.uniform1f("cloud_density", std::clamp(double(clouds), 0.0, 2.0)); + cloud_program.uniform1f("turbulence", std::clamp(double(turbulence), 0.0, 2.0)); + cloud_program.uniform1f("variation", std::clamp(double(variation), 0.0, 2.0)); + cloud_program.uniform1f("fractal_detail",std::clamp(double(fractal_detail),0.0,1.0)); + cloud_program.uniform1f("colour_variation", std::clamp(double(cloud_colours), 0.0, 1.0)); + cloud_program.uniform3f("scene_offset", 64 * star_random(scene_seed), + 64 * star_random(scene_seed + 1), 64 * star_random(scene_seed + 2)); + cloud_program.uniform1f("ribbons", std::clamp(double(ribbons), 0.0, 2.0)); + cloud_program.uniform1f("glow", std::clamp(double(glow), 0.0, 2.0)); + cloud_program.uniform1f("brightness", std::clamp(double(brightness), 0.0, 2.0)); + cloud_program.uniform1f("energy", 1 + boost * double(surge) * 0.20); + cloud_program.uniform3f("tint", color.r, color.g, color.b); + cloud_program.uniform1i("detail", level); + cloud_program.attrib_pointer("position", 2, 0, quad); + wf::gles::for_each_scissor_rect(cloud_target, box, [&] { GL_CALL(glDrawArrays(GL_TRIANGLES,0,6)); }); + cloud_program.deactivate(); + cloud_dirty = false; + } + wf::render_target_t backdrop_target{backdrop_buffer}; + backdrop_target.geometry = box; backdrop_target.scale = target.scale; + auto draw_stars = [&] (int layer) + { + const int count = layer == 0 ? far_count : layer == 1 ? dust_count : layer == 2 ? near_count : middle_count; + if (!count) return; + star_program.use(wf::TEXTURE_TYPE_RGBA); + star_program.uniformMatrix4f("mvp", wf::gles::render_target_orthographic_projection(backdrop_target)); + star_program.uniform2f("resolution", width, height); + // Derive the deeper journey from the same bounded world clock. Its + // 64-unit period divides the full 4096*12-unit clock, including wrap. + const double deep_clock = travel_epoch * 12.0 + travel; + star_program.uniform1f("travel", layer == 3 ? std::fmod(deep_clock, 64.0) : travel); + star_program.uniform1f("speed", rendered_speed); + star_program.uniform1f("epoch", layer == 3 ? std::floor(deep_clock / 64.0) : travel_epoch); + star_program.uniform1f("drift", drift_phase); + star_program.uniform1f("brightness", std::clamp(double(brightness), 0.0, 2.0)); + star_program.uniform1f("coloured_stars", std::clamp(double(coloured), 0.0, 1.0)); + star_program.uniform1f("glow", std::clamp(double(glow), 0.0, 2.0)); + star_program.uniform1f("backdrop_opacity", 1.0); + star_program.uniform1i("layer", layer); + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, star_buffer)); + star_program.attrib_pointer("seed",4,sizeof(hyperspace_star_t),reinterpret_cast<void*>(offsetof(hyperspace_star_t,seed))); + star_program.attrib_pointer("corner",2,sizeof(hyperspace_star_t),reinterpret_cast<void*>(offsetof(hyperspace_star_t,corner))); + GL_CALL(glEnable(GL_BLEND)); GL_CALL(glBlendFunc(GL_ONE,GL_ONE)); + wf::gles::for_each_scissor_rect(backdrop_target,box,[&] + { GL_CALL(glDrawArrays(GL_TRIANGLES,layer == 0 ? 0 : layer == 1 ? far_count * 6 : + (far_count + dust_count + (layer == 3 ? near_count : 0)) * 6,count * 6)); }); + GL_CALL(glDisable(GL_BLEND)); star_program.deactivate(); GL_CALL(glBindBuffer(GL_ARRAY_BUFFER,0)); + }; + if (backdrop_dirty) + { + wf::gles::bind_render_buffer(backdrop_target); + wf::gles::for_each_scissor_rect(backdrop_target,box,[&] { OpenGL::clear({0,0,0,0}); }); + draw_stars(0); + if (double(galaxies)>0) { + if (!galaxy_field) galaxy_field=std::make_unique<ambient_galaxy_field>(); + galaxy_field->render(backdrop_target,box,scene_seed,drift_phase, + level,std::clamp(double(galaxies),0.0,2.0), + std::clamp(double(brightness),0.0,2.0)); + } + draw_stars(3); + if (double(pulsars) > 0 && pulsar_age >= 0) + { + const float size = pulsar_size * height * 1.4f; + const float x = pulsar_x * width, y = pulsar_y * height; + const GLfloat vertices[] = {x-size,y-size,x+size,y-size,x+size,y+size, + x-size,y-size,x+size,y+size,x-size,y+size}; + pulsar_program.use(wf::TEXTURE_TYPE_RGBA); + pulsar_program.uniformMatrix4f("mvp", wf::gles::render_target_orthographic_projection(backdrop_target)); + pulsar_program.uniform2f("resolution", width, height); + pulsar_program.uniform2f("centre", x, y); + pulsar_program.uniform1f("size", pulsar_size); + pulsar_program.uniform1f("projection", pulsar_projection); + pulsar_program.uniform1f("angle", pulsar_angle + pulsar_age * pulsar_rotation); + // Smooth fades and a quiet breathing core, never a strobe. + const double fade = std::min({1.0, pulsar_age / 2.5, (pulsar_duration - pulsar_age) / 3.5}); + pulsar_program.uniform1f("strength", std::clamp(double(pulsars), 0.0, 2.0) * + std::clamp(double(brightness), 0.0, 2.0) * fade * fade); + pulsar_program.attrib_pointer("position", 2, 0, vertices); + GL_CALL(glEnable(GL_BLEND)); GL_CALL(glBlendFunc(GL_ONE,GL_ONE)); + wf::gles::for_each_scissor_rect(backdrop_target,box,[&] { GL_CALL(glDrawArrays(GL_TRIANGLES,0,6)); }); + GL_CALL(glDisable(GL_BLEND)); pulsar_program.deactivate(); + } + if (double(anomalies) > 0 && anomaly_age >= 0) + { + const float size = anomaly_size * height * 1.7f; + const float x = anomaly_x * width, y = anomaly_y * height; + const GLfloat vertices[] = {x-size,y-size,x+size,y-size,x+size,y+size, + x-size,y-size,x+size,y+size,x-size,y+size}; + anomaly_program.use(wf::TEXTURE_TYPE_RGBA); + anomaly_program.uniformMatrix4f("mvp", wf::gles::render_target_orthographic_projection(backdrop_target)); + anomaly_program.uniform2f("resolution", width, height); + anomaly_program.uniform2f("centre", x, y); + anomaly_program.uniform1f("size", anomaly_size); + anomaly_program.uniform1f("angle", anomaly_angle); + anomaly_program.uniform1f("shape", anomaly_shape); + anomaly_program.uniform1f("age", anomaly_age); + anomaly_program.uniform1i("kind", anomaly_kind); + const double fade = std::min({1.0, anomaly_age / 4.0, (anomaly_duration - anomaly_age) / 5.0}); + const double envelope = std::clamp(fade, 0.0, 1.0); + anomaly_program.uniform1f("strength", std::clamp(double(anomalies), 0.0, 2.0) * + std::clamp(double(brightness), 0.0, 2.0) * envelope * envelope * (3 - 2 * envelope)); + anomaly_program.attrib_pointer("position", 2, 0, vertices); + GL_CALL(glEnable(GL_BLEND)); GL_CALL(glBlendFunc(GL_ONE,GL_ONE)); + wf::gles::for_each_scissor_rect(backdrop_target,box,[&] { GL_CALL(glDrawArrays(GL_TRIANGLES,0,6)); }); + GL_CALL(glDisable(GL_BLEND)); anomaly_program.deactivate(); + } + draw_stars(1); + auto clouds = wf::gles_texture_t::from_aux(cloud_buffer); + wf::gles::for_each_scissor_rect(backdrop_target,box,[&] + { OpenGL::render_texture(clouds,backdrop_target,box); }); + GL_CALL(glDisable(GL_BLEND)); + draw_stars(2); + if (double(asteroids)>0) { + if (!asteroid_field) asteroid_field=std::make_unique<ambient_asteroid_field>(); + asteroid_field->render(backdrop_target,box,scene_seed,travel_epoch*12.0+travel, + level,std::clamp(double(asteroids),0.0,2.0),std::clamp(double(brightness),0.0,2.0)); + } + backdrop_dirty = false; + } + // Composite once so 50% opacity really is half of the complete effect, + // including the stars behind the absorbing clouds. Reuse frozen frames. + wf::gles::bind_render_buffer(target); + auto texture = wf::gles_texture_t::from_aux(backdrop_buffer); + wf::gles::for_each_scissor_rect(target, damage, [&] + { + OpenGL::render_texture(texture,target,box,glm::vec4(1,1,1,opacity)); + }); + GL_CALL(glDisable(GL_BLEND)); +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-background.hpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-background.hpp new file mode 100644 index 000000000000..3055dfbcb4ea --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-background.hpp @@ -0,0 +1,84 @@ +#pragma once + +#ifdef AMBIENT_LOCK_RENDERER +#include "hyperspace-standalone.hpp" +#else +#include <wayfire/option-wrapper.hpp> +#include <wayfire/opengl.hpp> +#endif +#include <chrono> +#include <cstdint> +#include <functional> +#include <array> +#include <memory> + +class __attribute__((visibility("hidden"))) ambient_asteroid_field; +class __attribute__((visibility("hidden"))) ambient_galaxy_field; + +// A self-contained procedural backdrop: layered nebulae, independent dust, +// distant stars and passing stars, with occasional distant pulsar jets. +class __attribute__((visibility("hidden"))) ambient_hyperspace_background +{ + wf::option_wrapper_t<double> speed{"ambient-background/hyperspace_speed"}; + wf::option_wrapper_t<double> clouds{"ambient-background/hyperspace_cloud_density"}; + wf::option_wrapper_t<double> turbulence{"ambient-background/hyperspace_turbulence"}; + wf::option_wrapper_t<double> ribbons{"ambient-background/hyperspace_ribbon_strength"}; + wf::option_wrapper_t<double> glow{"ambient-background/hyperspace_glow"}; + wf::option_wrapper_t<double> brightness{"ambient-background/hyperspace_brightness"}; + wf::option_wrapper_t<double> surge{"ambient-background/hyperspace_surge"}; + wf::option_wrapper_t<double> density{"ambient-background/hyperspace_star_density"}; + wf::option_wrapper_t<double> coloured{"ambient-background/hyperspace_coloured_stars"}; + wf::option_wrapper_t<double> distant_density{"ambient-background/hyperspace_distant_stars"}; + wf::option_wrapper_t<double> distant_drift{"ambient-background/hyperspace_distant_drift"}; + wf::option_wrapper_t<double> fractal_detail{"ambient-background/hyperspace_fractal_detail"}; + wf::option_wrapper_t<double> galaxies{"ambient-background/hyperspace_galaxies"}; + wf::option_wrapper_t<double> dust_density{"ambient-background/hyperspace_dust_density"}; + wf::option_wrapper_t<double> variation{"ambient-background/hyperspace_cloud_variation"}; + wf::option_wrapper_t<double> cloud_colours{"ambient-background/hyperspace_cloud_colours"}; + wf::option_wrapper_t<double> pulsars{"ambient-background/hyperspace_pulsars"}; + wf::option_wrapper_t<double> anomalies{"ambient-background/hyperspace_anomalies"}; + wf::option_wrapper_t<double> asteroids{"ambient-background/hyperspace_asteroids"}; + wf::option_wrapper_t<int> seed_option{"ambient-background/hyperspace_seed"}; + wf::option_wrapper_t<wf::color_t> tint{"ambient-background/hyperspace_color"}; + wf::option_wrapper_t<int> quality{"ambient-background/hyperspace_quality"}; + wf::option_wrapper_t<int> fps{"ambient-background/hyperspace_fps"}; + OpenGL::program_t cloud_program, star_program, pulsar_program, anomaly_program; + std::unique_ptr<ambient_asteroid_field> asteroid_field; + std::unique_ptr<ambient_galaxy_field> galaxy_field; + wf::auxilliary_buffer_t cloud_buffer, backdrop_buffer; + GLuint star_buffer = 0; + int near_count = -1, far_count = -1, dust_count = -1, middle_count = -1; + int configured_seed = -1; + uint32_t scene_seed = 1, event_number = 0, anomaly_number = 0; + unsigned travel_epoch = 0, cloud_epoch = 0; + bool active = false, transition = false, cloud_dirty = true, backdrop_dirty = true; + double travel = 0, cloud_phase = 0, drift_phase = 0, boost = 0; + double rendered_speed = 0; + double pulsar_age = -3, pulsar_duration = 15; + float pulsar_x = 0.5f, pulsar_y = 0.3f, pulsar_angle = 0, pulsar_rotation = 0.2f; + float pulsar_size = 0.13f, pulsar_projection = 0.65f; + double anomaly_age = -15, anomaly_duration = 18; + float anomaly_x = 0, anomaly_y = 0, anomaly_size = 0.08f; + float anomaly_angle = 0, anomaly_shape = 0; + int anomaly_kind = 0; + std::chrono::steady_clock::time_point last_frame; + + void load_programs(); + void update_particles(); + void prepare_pulsar(); + void prepare_anomaly(); + + public: + explicit ambient_hyperspace_background(std::function<void()> changed); + ~ambient_hyperspace_background(); + void set_active(bool enabled); + void set_transition(bool moving); + bool animated() const; + unsigned frame_interval() const; + void invalidate() { cloud_dirty = backdrop_dirty = true; } + bool has_scene() const { return configured_seed >= 0; } + std::array<double,24> scene() const; + bool restore_scene(const std::array<double,24>& state); + void render(const wf::render_target_t& target, const wf::regionf_t& damage, + wf::dimensions_t resolution, float opacity = 1.0f); +}; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-galaxies.tpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-galaxies.tpp new file mode 100644 index 000000000000..6c3ba1340fb6 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-galaxies.tpp @@ -0,0 +1,54 @@ +#include "hyperspace-galaxy-shaders.tpp" + +// Own the deep layer separately; no exported methods can bind to a resident +// earlier plugin during a reload. The lock client uses the same implementation. +class __attribute__((visibility("hidden"))) ambient_galaxy_field +{ + OpenGL::program_t program; + public: + ~ambient_galaxy_field() { program.free_resources(); } + void render(const wf::render_target_t& target,const wf::geometry_t& box, + uint32_t seed,double drift,int detail,double density,double brightness) + { + if (!program.get_program_id(wf::TEXTURE_TYPE_RGBA)) + program.set_simple(OpenGL::compile_program(hyperspace_galaxy_vertex,hyperspace_galaxy_fragment)); + const double aspect=box.width/box.height; + const int count=std::clamp(int(std::ceil(4*density*aspect)),0,128); + const GLfloat corners[]={-1,-1,1,-1,1,1,-1,-1,1,1,-1,1}; + program.use(wf::TEXTURE_TYPE_RGBA); + program.uniformMatrix4f("mvp",wf::gles::render_target_orthographic_projection(target)); + program.uniform1i("detail",detail); + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER,0)); + program.attrib_pointer("corner",2,0,corners); + GL_CALL(glEnable(GL_BLEND));GL_CALL(glBlendFunc(GL_ONE,GL_ONE)); + for (int i=0;i<count;i++) + { + const uint32_t base=seed^uint32_t(i*31741+971)^0x731a29fu; + auto random=[base](unsigned j){return star_random(base+j*19139);}; + // Share the distant stars' slow, unboosted clock. Near/far fades + // hide recycling, and the 2*pi wrap preserves every visible body. + // Constant world centres and fixed textures give coherent radial + // parallax rather than a sliding image or rapidly changing fog. + const double z=96+std::fmod(random(0)*128-drift/(2*M_PI)*128+128,128); + const double world_x=(2*random(1)-1)*aspect*224; + const double world_y=(2*random(2)-1)*224; + const double world_radius=2.5+std::pow(random(3),2.f)*10.5; + const float radius=world_radius/z*box.height*.5; + const float x=box.width*.5+world_x/z*box.height*.5; + const float y=box.height*.5+world_y/z*box.height*.5; + if (x+radius<0 || x-radius>box.width || y+radius<0 || y-radius>box.height) continue; + const double fade=std::clamp((z-96)/18,0.0,1.0)*std::clamp((224-z)/18,0.0,1.0); + const int kind=random(4)<.52f ? 0 : random(4)<.75f ? 1 : 2; + const float projection=kind==1 ? .20f+random(5)*.13f : .50f+random(5)*.50f; + program.uniform2f("centre",x,y); + program.uniform1f("radius",radius); + program.uniform1f("angle",random(6)*2*M_PI); + program.uniform1f("projection",projection); + program.uniform1i("kind",kind); + program.uniform1f("shape_seed",random(7)*100); + program.uniform1f("strength",brightness*(.55+random(8)*.65)*fade*fade*(3-2*fade)); + wf::gles::for_each_scissor_rect(target,box,[&]{GL_CALL(glDrawArrays(GL_TRIANGLES,0,6));}); + } + GL_CALL(glDisable(GL_BLEND));program.deactivate(); + } +}; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-galaxy-shaders.tpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-galaxy-shaders.tpp new file mode 100644 index 000000000000..5b16f2712558 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-galaxy-shaders.tpp @@ -0,0 +1,86 @@ +static const char *hyperspace_galaxy_vertex=R"( +#version 100 +precision highp float; +attribute vec2 corner; +uniform mat4 mvp; +uniform vec2 centre; +uniform float radius,angle,projection; +varying vec2 local; +void main() +{ + local=corner; + vec2 p=corner*vec2(1.0,projection); + float c=cos(angle),s=sin(angle); + p=vec2(c*p.x-s*p.y,s*p.x+c*p.y); + gl_Position=mvp*vec4(centre+radius*p,0.0,1.0); +} +)"; + +static const char *hyperspace_galaxy_fragment=R"( +#version 100 +precision highp float; +varying vec2 local; +uniform float shape_seed,strength,radius; +uniform int kind,detail; +float hash(vec2 p) +{ + vec3 q=fract(vec3(p.xyx)*vec3(.1031,.1030,.0973)); + q+=dot(q,q.yzx+33.33); + return fract((q.x+q.y)*q.z); +} +float noise(vec2 p) +{ + vec2 i=floor(p),f=fract(p);f=f*f*(3.0-2.0*f); + return mix(mix(hash(i),hash(i+vec2(1,0)),f.x), + mix(hash(i+vec2(0,1)),hash(i+vec2(1)),f.x),f.y); +} +void main() +{ + vec2 p=local; + float r=length(p); + float edge=1.0-smoothstep(.68,1.0,r); + if (edge<=0.0) discard; + vec2 offset=vec2(shape_seed,shape_seed*.37); + float coarse=noise(p*9.0+offset); + float fine=noise(p*47.0+offset.yx); + float resolved=smoothstep(20.0,70.0,radius); + float patch=coarse*.80+mix(.5,fine,resolved)*.20; + float bulge=exp(-r*r*85.0); + float disk=exp(-r*4.6)*edge; + float stars=0.0,dust=0.0; + if (kind==0) + { + float arms=2.0+floor(mod(shape_seed,3.0)); + float theta=atan(p.y+.00001,p.x+.00001); + float phase=theta*arms-(5.5+mod(shape_seed,4.0))*log(r+.12) + +noise(p*4.0+offset)*1.6; + float spiral=pow(.5+.5*cos(phase),7.0); + stars=disk*(.18+spiral*(.6+patch*1.8))*smoothstep(.05,.18,r); + dust=pow(.5+.5*cos(phase+.7),12.0)*smoothstep(.12,.3,r)*.60; + } else if (kind==1) + { + // A thin tilted disc with a bulge and a dark, irregular dust lane. + stars=disk*(.38+patch*.70); + bulge=exp(-dot(p*vec2(5.0,2.3),p*vec2(5.0,2.3))); + float lane=p.y+.08*(coarse-.5); + dust=exp(-lane*lane*190.0)*(.55+patch*.30)*smoothstep(.03,.18,abs(p.x)); + } else + { + // Older stellar populations form warmer, smooth elliptical halos. + bulge=exp(-r*13.0); + stars=disk*(.26+patch*.08); + } + vec3 core=vec3(1.0,.84,.61); + vec3 arms_colour=mix(vec3(.58,.72,1.0),vec3(.83,.72,.89),mod(shape_seed*.73,1.0)); + if (kind==2) arms_colour=vec3(.88,.77,.64); + vec3 light=(core*bulge*.55+arms_colour*stars*.48)*(1.0-dust); + if (detail>0 && kind!=2) + { + // Sparse clusters stay attached to the arms, without temporal noise. + float clusters=pow(fine,12.0)*stars*resolved*(detail==2 ? .35 : .18); + light+=vec3(.75,.85,1.0)*clusters; + } + light*=edge*strength; + gl_FragColor=vec4(light,max(max(light.r,light.g),light.b)); +} +)"; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-lock.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-lock.cpp new file mode 100644 index 000000000000..bca6ae82730f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-lock.cpp @@ -0,0 +1,116 @@ +#include "hyperspace-background.hpp" +#include "../nebula/nebula-background.hpp" +#include "hyperspace-lock.h" +#include <memory> + +namespace ambient_lock_host { +thread_local json_object *parameters=nullptr; +thread_local OpenGL::program_t *texture_program=nullptr; +} +namespace { +struct compositor { + OpenGL::program_t texture;wf::auxilliary_buffer_t frame; + ~compositor() { texture.free_resources();frame.free(); } +}; +struct space { + compositor drawing; + json_object *parameters=nullptr; + std::unique_ptr<ambient_hyperspace_background> engine; + std::unique_ptr<ambient_nebula_background> nebula; + bool nebula_selected() const { + json_object *value=nullptr; + return json_object_object_get_ex(parameters,"_backend",&value) && + std::string(json_object_get_string(value))=="nebula"; + } + void choose() { + ambient_lock_host::parameters=parameters; + if (nebula_selected()) { + if (nebula) return; + engine.reset();nebula=std::make_unique<ambient_nebula_background>([]{}); + json_object *scene=nullptr; + if (json_object_object_get_ex(parameters,"_scene",&scene) && + json_object_is_type(scene,json_type_array) && json_object_array_length(scene)==2) + nebula->restore_scene({json_object_get_double(json_object_array_get_idx(scene,0)), + json_object_get_double(json_object_array_get_idx(scene,1))}); + return; + } + if (engine) return; + nebula.reset(); + engine=std::make_unique<ambient_hyperspace_background>([]{}); + json_object *scene=nullptr; + if (json_object_object_get_ex(parameters,"_scene",&scene) && + json_object_is_type(scene,json_type_array) && json_object_array_length(scene)==24) { + std::array<double,24> values; + for (unsigned i=0;i<values.size();++i) values[i]=json_object_get_double(json_object_array_get_idx(scene,i)); + engine->restore_scene(values); + } + } + explicit space(json_object *p):parameters(json_object_get(p)) { + choose(); + } + ~space() { engine.reset();nebula.reset();json_object_put(parameters); } +}; +wf::render_target_t target(int width,int height,GLuint framebuffer=0) { + wf::auxilliary_buffer_t buffer;buffer.width=width;buffer.height=height;buffer.framebuffer=framebuffer; + wf::render_target_t t(buffer);t.geometry={0,0,double(width),double(height)};return t; +} +template<class F> bool checked(F fn) { + try { fn();return true; } catch (const std::exception& e) { + fprintf(stderr,"%s\n",e.what());return false; + } +} +} +extern "C" void *ambient_space_create(json_object *settings) { + space *result=nullptr;checked([&]{result=new space(settings);});return result; +} +extern "C" void ambient_space_destroy(void *renderer) { delete static_cast<space*>(renderer); } +extern "C" bool ambient_space_update(void *renderer,json_object *settings) { + if (!renderer || !settings) return false; + auto s=static_cast<space*>(renderer); + // Mirror native option callbacks while retaining the running scene. + if (!json_object_equal(s->parameters,settings)) { + json_object_get(settings);json_object_put(s->parameters);s->parameters=settings; + s->choose(); + if (s->engine) s->engine->invalidate(); + if (s->nebula) s->nebula->invalidate(); + } + return true; +} +extern "C" bool ambient_space_draw(void *renderer,int width,int height,bool moving) { + if (!renderer) return false; + auto s=static_cast<space*>(renderer); + return checked([&]{ + ambient_lock_host::parameters=s->parameters;ambient_lock_host::texture_program=&s->drawing.texture; + GLint fb=0;glGetIntegerv(GL_FRAMEBUFFER_BINDING,&fb); + auto t=target(width,height,fb);glBindBuffer(GL_ARRAY_BUFFER,0); + if (s->nebula) { + s->nebula->set_active(moving);s->nebula->render(t,t.geometry,{width,height}); + } else { + s->engine->set_active(moving);s->engine->render(t,t.geometry,{width,height}); + } + }); +} +extern "C" void *ambient_lock_composite_create() { return new compositor; } +extern "C" void ambient_lock_composite_destroy(void *renderer) { delete static_cast<compositor*>(renderer); } +extern "C" bool ambient_lock_composite_begin(void *renderer,int width,int height) { + auto c=static_cast<compositor*>(renderer); + return checked([&]{c->frame.allocate({width,height},1);wf::gles::bind_render_buffer(target(width,height,c->frame.framebuffer));}); +} +extern "C" bool ambient_lock_composite_end(void *renderer,int width,int height,float brightness) { + auto c=static_cast<compositor*>(renderer); + return checked([&]{ + ambient_lock_host::texture_program=&c->texture;auto t=target(width,height); + wf::gles::bind_render_buffer(t);glDisable(GL_SCISSOR_TEST);OpenGL::clear({0,0,0,1}); + OpenGL::render_texture(wf::gles_texture_t::from_aux(c->frame),t,t.geometry,glm::vec4(brightness,brightness,brightness,1)); + glDisable(GL_BLEND); + }); +} +extern "C" bool ambient_lock_composite_overlay(void *renderer,int width,int height,float opacity) { + auto c=static_cast<compositor*>(renderer); + return checked([&]{ + ambient_lock_host::texture_program=&c->texture;auto t=target(width,height); + wf::gles::bind_render_buffer(t);glDisable(GL_SCISSOR_TEST); + OpenGL::render_texture(wf::gles_texture_t::from_aux(c->frame),t,t.geometry,glm::vec4(1,1,1,opacity)); + glDisable(GL_BLEND); + }); +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-lock.h b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-lock.h new file mode 100644 index 000000000000..484ffccd3715 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-lock.h @@ -0,0 +1,20 @@ +#pragma once +#include <json-c/json.h> +#include <stdbool.h> +#ifdef __cplusplus +extern "C" { +#endif +// Call with the output's EGL context current. All rendering stays inside the +// authenticated lock surface; no compositor objects cross this interface. +void *ambient_space_create(struct json_object *settings); +void ambient_space_destroy(void *renderer); +bool ambient_space_update(void *renderer, struct json_object *settings); +bool ambient_space_draw(void *renderer, int width, int height, bool moving); +void *ambient_lock_composite_create(void); +void ambient_lock_composite_destroy(void *renderer); +bool ambient_lock_composite_begin(void *renderer, int width, int height); +bool ambient_lock_composite_end(void *renderer, int width, int height, float brightness); +bool ambient_lock_composite_overlay(void *renderer, int width, int height, float opacity); +#ifdef __cplusplus +} +#endif diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-shaders.tpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-shaders.tpp new file mode 100644 index 000000000000..87dd9a9a710e --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-shaders.tpp @@ -0,0 +1,278 @@ +static const char *hyperspace_vertex = R"( +#version 100 +attribute vec2 position; +uniform mat4 mvp; +uniform vec2 resolution; +varying vec2 uv; +void main() { uv = position / resolution; gl_Position = mvp * vec4(position,0.0,1.0); } +)"; + +static const char *hyperspace_cloud_fragment = R"( +#version 100 +precision highp float; +uniform vec2 resolution; +uniform vec3 tint; +uniform float phase, epoch, cloud_density, turbulence, variation, ribbons, glow, brightness, energy; +uniform float colour_variation, fractal_detail; +uniform vec3 scene_offset; +uniform int detail; +varying vec2 uv; + +float hash3(vec3 p) +{ + // Repeat every 64 cells, including octave-scaled coordinates. The CPU + // clock can wrap without a seam or lost precision after hours of travel. + p = mod(p,64.0); + p = fract(p * vec3(0.1031,0.1030,0.0973)); + p += dot(p,p.yxz + 33.33); + return fract((p.x + p.y) * p.z); +} +float noise3(vec3 p) +{ + vec3 c = floor(p), f = fract(p); f = f*f*(3.0-2.0*f); + return mix(mix(mix(hash3(c),hash3(c+vec3(1,0,0)),f.x), + mix(hash3(c+vec3(0,1,0)),hash3(c+vec3(1,1,0)),f.x),f.y), + mix(mix(hash3(c+vec3(0,0,1)),hash3(c+vec3(1,0,1)),f.x), + mix(hash3(c+vec3(0,1,1)),hash3(c+vec3(1,1,1)),f.x),f.y),f.z); +} +float fbm(vec3 p) +{ + float result=0.0, amplitude=0.55; + for (int i=0;i<5;i++) + { + if (i < detail+3) result += noise3(p)*amplitude; + p=p*2.0+vec3(7,13,19); amplitude*=0.5; + } + return result; +} +float bank_random(vec3 p) +{ + p=fract(p*vec3(0.1031,0.1030,0.0973)); + p+=dot(p,p.yxz+33.33); + return fract((p.x+p.y)*p.z); +} +vec2 rotate_xy(vec2 p, vec2 axis) +{ + return vec2(dot(p,axis),dot(p,vec2(-axis.y,axis.x))); +} +void main() +{ + float aspect=resolution.x/resolution.y; + vec3 ray=vec3((uv-0.5)*vec2(aspect,1.0)*2.0,1.0); + vec3 emission=vec3(0.0); + float extinction=0.0; + int banks=10+int(min(6.0,max(0.0,aspect-1.5)*2.0)); + // Discrete, seeded volumes occupy real world positions. Their centres + // approach the camera, so silhouettes expand and move outwards instead + // of boiling on camera-relative sheets. Recycling happens only while a + // bank is invisible at the near/far limits. + for (int i=0;i<16;i++) + { + if (i < banks && cloud_density > 0.0) + { + vec3 slot=scene_offset+vec3(float(i)*19.0,7.0,23.0); + float initial=mod((float(i)+bank_random(slot))*64.0/float(banks),64.0); + float cycle=mod(epoch+step(initial,phase),4096.0); + vec3 key=slot+vec3(cycle*1.17,cycle*0.73,cycle*0.31); + // Each bank retains its spectral character while it travels. + // Colours belong to the volume, never to screen-space regions. + float hue=bank_random(key+277.0); + vec3 palette=hue<0.22 ? vec3(0.25,0.58,1.0) : + hue<0.46 ? vec3(0.65,0.30,1.0) : + hue<0.68 ? vec3(0.20,0.78,0.70) : + hue<0.86 ? vec3(0.91,0.35,0.58) : vec3(1.0,0.63,0.26); + vec3 bank_tint=mix(tint,palette,colour_variation); + vec3 random=vec3(bank_random(key),bank_random(key+17.0),bank_random(key+43.0)); + vec3 centre=vec3((random.xy*2.0-1.0)*vec2(aspect*23.0,18.0), + mod(initial-phase,64.0)+4.0); + float visibility=smoothstep(4.0,12.0,centre.z)*(1.0-smoothstep(54.0,68.0,centre.z)); + vec3 radius=vec3(7.0+random.z*10.0,2.5+bank_random(key+71.0)*5.0, + 4.0+bank_random(key+97.0)*5.0); + float angle=bank_random(key+131.0)*6.2831853; + vec2 axis=vec2(cos(angle),sin(angle)); + vec3 origin=vec3(rotate_xy(-centre.xy,axis),-centre.z)/radius; + vec3 direction=vec3(rotate_xy(ray.xy,axis),ray.z)/radius; + float a=dot(direction,direction),b=dot(origin,direction); + float discriminant=b*b-a*(dot(origin,origin)-1.0); + if (discriminant > 0.0 && visibility > 0.0) + { + float root=sqrt(discriminant); + float entry=max(3.0,(-b-root)/a),exit=(-b+root)/a; + if (exit > entry) + { + float samples=float(3+detail); + float step_size=(exit-entry)/samples; + vec3 offset=vec3(bank_random(key+157.0),bank_random(key+193.0), + bank_random(key+211.0))*32.0; + // Local structure stays attached to the bank, including + // its torn edges and dark holes. All three coordinates + // use the same scale; time never animates the noise. + for (int j=0;j<5;j++) + { + if (j < 3+detail) + { + float distance=entry+(float(j)+0.5)*step_size; + vec3 local=origin+direction*distance; + vec3 position=local*radius; + float envelope=1.0-smoothstep(0.35,1.0,dot(local,local)); + float cover=noise3(local*1.8+offset); + float bend=noise3(position*0.16+offset.yzx); + vec3 fine=position*0.48+offset.zxy; + fine+=vec3(bend-0.5,cover-0.5,bend-cover)*turbulence*1.8; + float structure=fbm(fine); + if (fractal_detail>0.0) { + float folded=nebula_field(position*0.08+offset*0.015,7.0,vec3(-0.5,-0.8,-1.1)); + structure=mix(structure,clamp(folded*0.42,0.0,1.0),fractal_detail); + } + float gaps=smoothstep(0.34+variation*0.055,0.72,cover); + float wisps=smoothstep(0.30+variation*0.025,0.68,structure); + float density=envelope*gaps*wisps*cloud_density*visibility; + float optical_depth=density*step_size*0.22; + float filament=pow(max(0.0,1.0-abs(structure*2.0-1.0)),8.0); + float highlights=pow(max(0.0,structure-0.30)*1.8,3.0); + vec3 silver=mix(vec3(0.60,0.65,0.72),bank_tint,0.70); + vec3 lit=mix(vec3(0.90,0.94,1.0),bank_tint,0.60); + vec3 light=silver*0.18+lit*(highlights*0.90+ + filament*ribbons*0.26)*(0.55+glow*0.55); + extinction+=optical_depth; + emission+=light*optical_depth; + } + } + } + } + } + } + // Glow illuminates the dust itself; there is no camera-fixed central fog + // or halo. One premultiplied layer preserves wallpaper and fade opacity. + float alpha=1.0-exp(-extinction); + vec3 color=emission*(alpha/max(extinction,0.00001))*brightness*energy; + gl_FragColor=vec4(color,alpha); +} +)"; + +static const char *hyperspace_star_vertex = R"( +#version 100 +precision highp float; +attribute vec4 seed; +attribute vec2 corner; +uniform mat4 mvp; +uniform vec2 resolution; +uniform float travel, speed, drift, brightness, coloured_stars, epoch; +uniform int layer; +varying vec2 local; +varying float trail_length, radius, luminosity; +varying vec3 star_color; +float random2(vec2 p) +{ + vec3 q=fract(vec3(p.xyx)*vec3(0.1031,0.1030,0.0973)); + q+=dot(q,q.yzx+33.33); + return fract((q.x+q.y)*q.z); +} +void main() +{ + vec2 head,tail; + float pixel_scale=clamp(resolution.y/1080.0,0.65,2.0); + float spectral_seed=seed.w; + if (layer==0) + { + vec2 offset=vec2(sin(drift),cos(drift)-1.0)*resolution.y*0.07; + head=mod((seed.xy*0.5+0.5)*resolution+offset,resolution)-resolution*0.5; + tail=head; + radius=(0.55+1.15*seed.w*seed.w)*pixel_scale; + luminosity=brightness*(0.14+0.76*pow(seed.w,3.0))*(0.94+0.06*sin(drift*8.0+seed.z*50.0)); + } else + { + float span=layer==3 ? 64.0 : 12.0; + float near_limit=layer==3 ? 8.0 : 0.45; + float far_limit=near_limit+span; + float z=mod(seed.z*span-travel,span)+near_limit; + // Resample only when a particle returns beyond the horizon. The + // epoch clock wraps coherently for particles still on this pass. + float cycle=mod(epoch+step(seed.z*span,travel),layer==3 ? 768.0 : 4096.0); + vec2 key=seed.xy*37.0+vec2(cycle,cycle*0.37); + vec2 position=vec2(random2(key),random2(key+vec2(17,43)))*2.0-1.0; + float size_seed=random2(key+vec2(91,29)); + spectral_seed=random2(key+vec2(7,61)); + // Populate the full viewing frustum at the horizon. A narrow fixed + // cylinder concentrates far particles into a small central cone, + // especially on panoramic outputs, leaving an artificial empty gap. + vec2 world=position*vec2(resolution.x/resolution.y,1.0)*far_limit*1.02; + head=world/z*resolution.y*0.5; + float exposure=layer==1 ? 0.065 : layer==3 ? 0.12 : 0.42; + tail=world/(z+speed*exposure)*resolution.y*0.5; + float fade=smoothstep(near_limit,near_limit+(layer==3 ? 5.0 : 0.65),z)* + (1.0-smoothstep(far_limit-(layer==3 ? 10.0 : 2.45),far_limit,z)); + if (layer==3) + { + radius=(0.55+1.10*size_seed*size_seed)*pixel_scale*(0.7+5.0/(z+2.0)); + luminosity=brightness*fade*(0.11+0.85*size_seed*size_seed); + } else if (layer==1) + { + radius=(0.38+0.80*size_seed*size_seed)*pixel_scale*(0.75+0.7/(z+0.5)); + luminosity=brightness*fade*(0.04+0.32*pow(size_seed,3.0)); + } else + { + radius=(0.8+2.2*pow(size_seed,4.0))*pixel_scale*(0.7+1.0/(z+0.5)); + luminosity=brightness*fade*(0.22+1.45*pow(size_seed,4.0)); + } + } + vec2 direction=normalize(head-tail+vec2(0.00001)),normal=vec2(-direction.y,direction.x); + trail_length=min(length(head-tail),resolution.y*(layer==1 ? 0.065 : layer==3 ? 0.035 : 0.35)); + tail=head-direction*trail_length; + local=vec2(corner.x*(trail_length+2.0*radius)-radius,corner.y*radius); + vec2 pixel=resolution*0.5+tail+direction*local.x+normal*local.y; + gl_Position=mvp*vec4(pixel,0.0,1.0); + float chance=fract(spectral_seed*31.37),spectral=fract(spectral_seed*17.0); + star_color=layer==1 ? vec3(0.74,0.78,0.82) : vec3(1.0); + if (layer!=1 && chance<coloured_stars) + star_color=spectral<0.12 ? vec3(1.0,0.50,0.32) : + (spectral<0.40 ? vec3(1.0,0.86,0.62) : vec3(0.60,0.78,1.0)); +} +)"; + +static const char *hyperspace_star_fragment = R"( +#version 100 +precision highp float; +uniform float glow,backdrop_opacity; +varying vec2 local; +varying float trail_length,radius,luminosity; +varying vec3 star_color; +void main() +{ + vec2 head=vec2(local.x-trail_length,local.y)/radius; + float along=clamp(local.x/max(trail_length,0.01),0.0,1.0); + float end=max(max(-local.x,local.x-trail_length),0.0); + float d=(local.y*local.y+end*end)/(radius*radius); + float core=exp(-dot(head,head)*18.0); + float trail=exp(-d*15.0)*(0.10+0.65*along); + float halo=exp(-d*4.0)*0.16*glow; + float edge=1.0-smoothstep(0.75,1.0,d); + gl_FragColor=vec4(star_color*luminosity*(core+trail+halo)*edge*backdrop_opacity,0.0); +} +)"; + + +static const char *hyperspace_pulsar_fragment = R"( +#version 100 +precision highp float; +uniform vec2 resolution, centre; +uniform float size, projection, angle, strength; +varying vec2 uv; +void main() +{ + vec2 p=(uv*resolution-centre)/resolution.y; + float c=cos(angle),s=sin(angle); + vec2 axis=vec2(c,s),normal=vec2(-s,c); + float along=abs(dot(p,axis))/size; + float across=dot(p,normal)/size; + float width=0.014+0.11*pow(along,1.25)*projection; + float cone=exp(-across*across/(width*width))*exp(-along*2.8); + cone*=1.0-smoothstep(0.72,1.15,along); + float distance=length(p)/size; + float core=exp(-distance*distance*4200.0); + float bloom=exp(-distance*distance*220.0)*0.18; + vec3 color=vec3(0.45,0.70,1.0)*cone*0.48+ + vec3(0.88,0.95,1.0)*(core*1.8+bloom); + gl_FragColor=vec4(color*strength,0.0); +} +)"; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-standalone.hpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-standalone.hpp new file mode 100644 index 000000000000..b542a01efbe2 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/hyperspace/hyperspace-standalone.hpp @@ -0,0 +1,160 @@ +#pragma once + +// The lock client hosts the same renderer and shaders in its own EGL context. +// This narrow adapter supplies the option, buffer and drawing operations used +// by that renderer; it never links the compositor or reads desktop surfaces. +#include <GLES2/gl2.h> +#include <glm/gtc/matrix_transform.hpp> +#include <glm/gtc/type_ptr.hpp> +#include <json-c/json.h> +#include <algorithm> +#include <cmath> +#include <cstdio> +#include <functional> +#include <stdexcept> +#include <string> +#include <vector> +#define GL_CALL(x) x + +namespace OpenGL { class program_t; } +namespace ambient_lock_host { +extern thread_local json_object *parameters; +extern thread_local OpenGL::program_t *texture_program; +} +namespace wf { +struct color_t { float r=0, g=0, b=0, a=1; }; +template<class T> class option_wrapper_t { + std::string name; + public: + explicit option_wrapper_t(const char *key) : name(std::string(key).substr(19)) {} + void set_callback(std::function<void()>) {} + operator T() const { + json_object *value=nullptr; + if (!ambient_lock_host::parameters || + !json_object_object_get_ex(ambient_lock_host::parameters,name.c_str(),&value)) return T{}; + return T(json_object_get_double(value)); + } +}; +template<> inline option_wrapper_t<color_t>::operator color_t() const { + json_object *value=nullptr; color_t result; + if (!ambient_lock_host::parameters || + !json_object_object_get_ex(ambient_lock_host::parameters,name.c_str(),&value)) return result; + const char *text=json_object_get_string(value); + if (text && text[0]=='#') { + unsigned r,g,b; + if (sscanf(text+1,"%2x%2x%2x",&r,&g,&b)==3) + result={r/255.f,g/255.f,b/255.f,1}; + } else if (text) sscanf(text,"%f %f %f %f",&result.r,&result.g,&result.b,&result.a); + return result; +} +struct dimensions_t { int width, height; }; +struct geometry_t { double x,y,width,height; }; +using regionf_t=geometry_t; +enum texture_type_t { TEXTURE_TYPE_RGBA }; +enum class buffer_reallocation_result_t { REALLOCATED, UNCHANGED }; +struct auxilliary_buffer_t { + GLuint texture=0, framebuffer=0; int width=0,height=0; + buffer_reallocation_result_t allocate(dimensions_t size,float scale) { + int w=std::max(1,int(std::ceil(size.width*scale))),h=std::max(1,int(std::ceil(size.height*scale))); + if (w==width && h==height && texture) return buffer_reallocation_result_t::UNCHANGED; + free(); width=w;height=h; + glGenTextures(1,&texture);glBindTexture(GL_TEXTURE_2D,texture); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,nullptr); + glGenFramebuffers(1,&framebuffer);glBindFramebuffer(GL_FRAMEBUFFER,framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,texture,0); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!=GL_FRAMEBUFFER_COMPLETE) + throw std::runtime_error("Ambient lock framebuffer allocation failed"); + return buffer_reallocation_result_t::REALLOCATED; + } + void free() { + if (texture) glDeleteTextures(1,&texture); + if (framebuffer) glDeleteFramebuffers(1,&framebuffer); + texture=framebuffer=0; width=height=0; + } +}; +struct render_target_t { + GLuint framebuffer=0;int width=0,height=0;float scale=1; + geometry_t geometry{0,0,0,0}; + explicit render_target_t(const auxilliary_buffer_t& b):framebuffer(b.framebuffer),width(b.width),height(b.height) {} +}; +struct gles_texture_t { + GLuint tex_id; + static gles_texture_t from_aux(const auxilliary_buffer_t& b) { return {b.texture}; } +}; +namespace gles { +template<class F> void run_in_context_if_gles(F&& fn) { fn(); } +inline void bind_render_buffer(const render_target_t& t) { + glBindFramebuffer(GL_FRAMEBUFFER,t.framebuffer);glViewport(0,0,t.width,t.height); +} +inline glm::mat4 render_target_orthographic_projection(const render_target_t& t) { + return glm::ortho(float(t.geometry.x),float(t.geometry.x+t.geometry.width), + float(t.geometry.y+t.geometry.height),float(t.geometry.y),-1.f,1.f); +} +template<class F> void for_each_scissor_rect(const render_target_t&,const regionf_t&,F&& fn) { + glDisable(GL_SCISSOR_TEST);fn(); +} +} +} +namespace OpenGL { +inline GLuint compile_shader(GLenum type,const char *source) { + GLuint shader=glCreateShader(type);glShaderSource(shader,1,&source,nullptr);glCompileShader(shader); + GLint ok=0;glGetShaderiv(shader,GL_COMPILE_STATUS,&ok); + if (!ok) { + char log[2048];glGetShaderInfoLog(shader,sizeof(log),nullptr,log); + glDeleteShader(shader);throw std::runtime_error(std::string("Ambient lock shader: ")+log); + } + return shader; +} +inline GLuint compile_program(const char *vertex,const char *fragment) { + GLuint vs=compile_shader(GL_VERTEX_SHADER,vertex),fs=0,program=0; + try { + fs=compile_shader(GL_FRAGMENT_SHADER,fragment);program=glCreateProgram(); + glAttachShader(program,vs);glAttachShader(program,fs);glLinkProgram(program); + GLint ok=0;glGetProgramiv(program,GL_LINK_STATUS,&ok); + if (!ok) throw std::runtime_error("Ambient lock program link failed"); + } catch (...) { + glDeleteShader(vs);if (fs) glDeleteShader(fs);if (program) glDeleteProgram(program);throw; + } + glDeleteShader(vs);glDeleteShader(fs);return program; +} +class program_t { + GLuint id=0;std::vector<GLuint> attributes; + public: + GLuint get_program_id(wf::texture_type_t) const { return id; } + void set_simple(GLuint value) { id=value; } + void free_resources() { if (id) glDeleteProgram(id);id=0; } + void use(wf::texture_type_t) { glUseProgram(id); } + void uniform1i(const char *key,int a) { glUniform1i(glGetUniformLocation(id,key),a); } + void uniform1f(const char *key,float a) { glUniform1f(glGetUniformLocation(id,key),a); } + void uniform2f(const char *key,float a,float b) { glUniform2f(glGetUniformLocation(id,key),a,b); } + void uniform3f(const char *key,float a,float b,float c) { glUniform3f(glGetUniformLocation(id,key),a,b,c); } + void uniformMatrix4f(const char *key,const glm::mat4& m) { glUniformMatrix4fv(glGetUniformLocation(id,key),1,GL_FALSE,glm::value_ptr(m)); } + void attrib_pointer(const char *key,int n,int stride,const void *data) { + GLint a=glGetAttribLocation(id,key);if (a<0) return; + glEnableVertexAttribArray(a);glVertexAttribPointer(a,n,GL_FLOAT,GL_FALSE,stride,data);attributes.push_back(a); + } + void deactivate() { for (auto a:attributes) glDisableVertexAttribArray(a);attributes.clear();glUseProgram(0); } +}; +inline void clear(const glm::vec4& c) { glClearColor(c.r,c.g,c.b,c.a);glClear(GL_COLOR_BUFFER_BIT); } +inline void render_texture(wf::gles_texture_t texture,const wf::render_target_t& target, + const wf::geometry_t& box,const glm::vec4& color=glm::vec4(1)) { + auto& p=*ambient_lock_host::texture_program; + if (!p.get_program_id(wf::TEXTURE_TYPE_RGBA)) p.set_simple(compile_program( + "attribute vec2 position;attribute vec2 texcoord;uniform mat4 mvp;varying vec2 uv;void main(){uv=texcoord;gl_Position=mvp*vec4(position,0.,1.);}", + "precision mediump float;varying vec2 uv;uniform sampler2D image;uniform vec3 tint;uniform float alpha;void main(){vec4 c=texture2D(image,uv);gl_FragColor=c*vec4(tint*alpha,alpha);}" + )); + float x=box.x,y=box.y,w=box.width,h=box.height; + GLfloat xy[]={x,y,x+w,y,x+w,y+h,x,y,x+w,y+h,x,y+h}; + GLfloat uv[]={0,1,1,1,1,0,0,1,1,0,0,0}; + glBindBuffer(GL_ARRAY_BUFFER,0);p.use(wf::TEXTURE_TYPE_RGBA); + p.uniformMatrix4f("mvp",wf::gles::render_target_orthographic_projection(target)); + p.uniform1i("image",0);p.uniform1f("alpha",color.a);p.uniform3f("tint",color.r,color.g,color.b); + glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D,texture.tex_id); + p.attrib_pointer("position",2,0,xy);p.attrib_pointer("texcoord",2,0,uv); + glEnable(GL_BLEND);glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);glDrawArrays(GL_TRIANGLES,0,6);p.deactivate(); +} +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/LICENSE b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/LICENSE new file mode 100644 index 000000000000..618286616836 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2018 Iliya Bozhinov +Copyright (c) 2025 Andrew Pliatsikas + +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. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/UPSTREAM b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/UPSTREAM new file mode 100644 index 000000000000..cc250f83f05c --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/UPSTREAM @@ -0,0 +1,5 @@ +Adapted from okcomput3/wayfire-v-kube +Commit: 7a9a584994d8a045a45ba239b6fefc0529ed7867 +Source: https://github.com/okcomput3/wayfire-v-kube/blob/7a9a584994d8a045a45ba239b6fefc0529ed7867/wf-v-kube/src/shader-background.cpp +License: MIT; retained in LICENSE +Adaptations: seeded and bounded smooth motion, independent emission layers, controls, premultiplied composition, shared Hyperspace detail and native lock integration. diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-background.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-background.cpp new file mode 100644 index 000000000000..4e3d9134fcfe --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-background.cpp @@ -0,0 +1,82 @@ +#include "nebula-background.hpp" +#include "nebula-shaders.tpp" +#include "nebula-field.tpp" +#include <algorithm> +#include <cmath> +#include <random> +#include <string> +namespace { +float seeded(uint32_t value) { + value^=value>>16;value*=0x7feb352du;value^=value>>15;value*=0x846ca68bu;value^=value>>16; + return (value&0x00ffffffu)/16777216.0f; +} +} +ambient_nebula_background::ambient_nebula_background(std::function<void()> changed) { + auto update=[this,changed]{dirty=true;changed();}; + speed.set_callback(update);brightness.set_callback(update);clouds.set_callback(update); + stars.set_callback(update);colours.set_callback(update);scale.set_callback(update); + tint.set_callback(update);seed_option.set_callback(update);quality.set_callback(update); + last_frame=std::chrono::steady_clock::now(); +} +ambient_nebula_background::~ambient_nebula_background() { + wf::gles::run_in_context_if_gles([&]{program.free_resources();buffer.free();}); +} +void ambient_nebula_background::set_active(bool enabled) { + if (active!=enabled) last_frame=std::chrono::steady_clock::now(); + active=enabled; +} +bool ambient_nebula_background::restore_scene(const std::array<double,2>& state) { + if (!std::isfinite(state[0]) || !std::isfinite(state[1]) || state[0]<0 || + state[0]>UINT32_MAX || state[1]<0 || state[1]>2*M_PI) return false; + configured_seed=int(seed_option);scene_seed=state[0];phase=state[1];dirty=true; + last_frame=std::chrono::steady_clock::now();return true; +} +void ambient_nebula_background::render(const wf::render_target_t& target, + const wf::regionf_t& damage,wf::dimensions_t resolution,float opacity) { + if (resolution.width<=0 || resolution.height<=0) return; + if (configured_seed!=int(seed_option)) { + configured_seed=int(seed_option);scene_seed=configured_seed ? configured_seed : std::random_device{}(); + phase=0;dirty=true; + } + auto now=std::chrono::steady_clock::now(); + double dt=std::clamp(std::chrono::duration<double>(now-last_frame).count(),0.0,0.1); + last_frame=now; + if (active && double(speed)>0) { + phase=std::fmod(phase+dt*std::clamp(double(speed),0.0,5.0)*0.012,2*M_PI);dirty=true; + } + float render_scale=std::clamp(double(scale),0.25,1.0)*target.scale; + if (buffer.allocate(resolution,render_scale)==wf::buffer_reallocation_result_t::REALLOCATED) dirty=true; + wf::geometry_t box{0,0,double(resolution.width),double(resolution.height)}; + if (dirty) { + if (!program.get_program_id(wf::TEXTURE_TYPE_RGBA)) { + auto fragment=std::string(nebula_fragment_head)+ambient_nebula_field_glsl+nebula_fragment_body; + program.set_simple(OpenGL::compile_program(nebula_vertex,fragment.c_str())); + } + wf::render_target_t offscreen{buffer};offscreen.geometry=box;offscreen.scale=render_scale; + wf::gles::bind_render_buffer(offscreen); + GL_CALL(glDisable(GL_DEPTH_TEST));GL_CALL(glDisable(GL_CULL_FACE));GL_CALL(glDisable(GL_BLEND)); + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER,0)); + program.use(wf::TEXTURE_TYPE_RGBA); + program.uniformMatrix4f("mvp",wf::gles::render_target_orthographic_projection(offscreen)); + program.uniform2f("resolution",resolution.width,resolution.height); + program.uniform1f("phase",phase); + program.uniform1f("cloud_density",std::clamp(double(clouds),0.0,2.0)); + program.uniform1f("star_density",std::clamp(double(stars),0.0,3.0)); + program.uniform1f("brightness",std::clamp(double(brightness),0.0,2.0)); + program.uniform1f("colour_variation",std::clamp(double(colours),0.0,1.0)); + auto colour=wf::color_t(tint);program.uniform3f("tint",colour.r,colour.g,colour.b); + program.uniform3f("scene_offset",seeded(scene_seed)*0.9,seeded(scene_seed+1)*0.9,seeded(scene_seed+2)*0.9); + program.uniform1i("detail",std::clamp(int(quality),0,2)); + const float width=resolution.width,height=resolution.height; + const GLfloat vertices[]={0,0,width,0,width,height,0,0,width,height,0,height}; + program.attrib_pointer("position",2,0,vertices); + wf::gles::for_each_scissor_rect(offscreen,box,[&]{GL_CALL(glDrawArrays(GL_TRIANGLES,0,6));}); + program.deactivate();dirty=false; + } + wf::gles::bind_render_buffer(target); + auto texture=wf::gles_texture_t::from_aux(buffer); + wf::gles::for_each_scissor_rect(target,damage,[&]{ + OpenGL::render_texture(texture,target,box,glm::vec4(1,1,1,std::clamp(opacity,0.0f,1.0f))); + }); + GL_CALL(glDisable(GL_BLEND)); +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-background.hpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-background.hpp new file mode 100644 index 000000000000..51df5eb78eb7 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-background.hpp @@ -0,0 +1,40 @@ +#pragma once +#ifdef AMBIENT_LOCK_RENDERER +#include "../hyperspace/hyperspace-standalone.hpp" +#else +#include <wayfire/option-wrapper.hpp> +#include <wayfire/opengl.hpp> +#endif +#include <array> +#include <chrono> +#include <functional> +#include <cstdint> + +class __attribute__((visibility("hidden"))) ambient_nebula_background { + wf::option_wrapper_t<double> speed{"ambient-background/nebula_speed"}; + wf::option_wrapper_t<double> brightness{"ambient-background/nebula_brightness"}; + wf::option_wrapper_t<double> clouds{"ambient-background/nebula_cloud_density"}; + wf::option_wrapper_t<double> stars{"ambient-background/nebula_star_density"}; + wf::option_wrapper_t<double> colours{"ambient-background/nebula_cloud_colours"}; + wf::option_wrapper_t<double> scale{"ambient-background/nebula_resolution_scale"}; + wf::option_wrapper_t<wf::color_t> tint{"ambient-background/nebula_color"}; + wf::option_wrapper_t<int> seed_option{"ambient-background/nebula_seed"}; + wf::option_wrapper_t<int> quality{"ambient-background/nebula_quality"}; + OpenGL::program_t program; + wf::auxilliary_buffer_t buffer; + bool active=false, dirty=true; + int configured_seed=-1; + uint32_t scene_seed=1; + double phase=0; + std::chrono::steady_clock::time_point last_frame; + public: + explicit ambient_nebula_background(std::function<void()> changed); + ~ambient_nebula_background(); + void set_active(bool enabled); + void invalidate() { dirty=true; } + bool has_scene() const { return configured_seed>=0; } + std::array<double,2> scene() const { return {double(scene_seed),phase}; } + bool restore_scene(const std::array<double,2>& state); + void render(const wf::render_target_t& target,const wf::regionf_t& damage, + wf::dimensions_t resolution,float opacity=1); +}; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-field.tpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-field.tpp new file mode 100644 index 000000000000..f5be4e27675e --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-field.tpp @@ -0,0 +1,16 @@ +// Fractal fold adapted from wayfire-v-kube. See LICENSE and UPSTREAM. +// Object-space inputs stay fixed: the camera moves through the field. +static const char *ambient_nebula_field_glsl = R"( +float nebula_field(vec3 p, float strength, vec3 offset) { + float accum=0.0, previous=0.0, weight_sum=0.0; + for (int i=0;i<6;i++) { + float magnitude=max(dot(p,p),0.0001); + p=abs(p)/magnitude+offset; + float weight=exp(-float(i)/7.0); + accum+=weight*exp(-strength*pow(abs(magnitude-previous),2.3)); + weight_sum+=weight; + previous=magnitude; + } + return max(0.0,5.0*accum/weight_sum-0.7); +} +)"; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-shaders.tpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-shaders.tpp new file mode 100644 index 000000000000..d2d84b84ea2e --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/effects/nebula/nebula-shaders.tpp @@ -0,0 +1,61 @@ +// Adapted from okcomput3/wayfire-v-kube (MIT), with stable seeded motion. +static const char *nebula_vertex = R"( +#version 100 +attribute vec2 position; +uniform mat4 mvp; +uniform vec2 resolution; +varying vec2 uv; +void main() { uv=position/resolution;gl_Position=mvp*vec4(position,0.0,1.0); } +)"; +static const char *nebula_fragment_head = R"( +#version 100 +precision highp float; +uniform vec2 resolution; +uniform float phase, cloud_density, star_density, brightness, colour_variation; +uniform vec3 scene_offset, tint; +uniform int detail; +varying vec2 uv; +)"; +static const char *nebula_fragment_body = R"( +void main() { + vec2 plane=(uv*2.0-1.0)*resolution/max(resolution.x,resolution.y); + float xy=0.9+phase; + mat2 rot_xy=mat2(cos(xy),sin(xy),-sin(xy),cos(xy)); + mat2 rot_xz=mat2(cos(0.9),sin(0.9),-sin(0.9),cos(0.9)); + mat2 rot_yz=mat2(cos(-0.6),sin(-0.6),-sin(-0.6),cos(-0.6)); + vec3 ray=vec3(plane*3.9,1.0); + vec3 origin=vec3(-2.5+1.1*cos(phase),-2.5+1.1*sin(phase),0.45*sin(phase)); + origin+=scene_offset; + ray.xy*=rot_xy;ray.xz*=rot_xz;ray.yz*=rot_yz; + origin.xy*=-rot_xy;origin.xz*=rot_xz;origin.yz*=rot_yz; + vec3 stars=vec3(0.0), clouds=vec3(0.0); + int steps=6+detail*3; + for (int r=0;r<12;r++) { + if (r<steps) { + float distance=0.24+float(r)*0.19; + vec3 p=origin+distance*ray; + vec3 q=(origin+(distance+0.095)*ray)*(1.9/3.9); + p=abs(vec3(0.45)-mod(p,vec3(0.9))); + q=abs(vec3(0.45)-mod(q,vec3(0.9))); + float previous=0.0, structure=0.0; + for (int i=0;i<4;i++) { + p=abs(p)/max(dot(p,p),0.0001)-0.89; + if (i==2) previous=length(p); + if (i==3) structure=abs(length(p)-previous); + } + structure=min(structure*structure*structure,5000.0); + float fade=pow(0.56,float(r)); + stars+=vec3(distance,distance*distance,pow(distance,4.0))*structure*0.002*fade; + float f=nebula_field(q,7.0,vec3(-0.5,-0.8,-1.1)); + clouds+=vec3(0.20*f*f*f,0.4*f*f,f*0.7)*fade*0.12; + } + } + stars=mix(vec3(length(stars)),stars,0.4)*star_density; + vec3 coloured=mix(tint,vec3(0.45,0.28,0.95),0.35+0.25*sin(phase)); + vec3 cloud_colour=mix(tint,vec3(0.32,0.56,1.0),colour_variation); + cloud_colour=mix(cloud_colour,coloured,colour_variation*0.4); + vec3 emission=(stars+clouds*cloud_density*cloud_colour*2.0)*brightness; + // Emit light over any selected backdrop; no baked-in colour or image. + gl_FragColor=vec4(1.0-exp(-max(emission,vec3(0.0))),0.0); +} +)"; diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/include/ambient-api.hpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/include/ambient-api.hpp new file mode 100644 index 000000000000..17f912921c80 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/include/ambient-api.hpp @@ -0,0 +1,14 @@ +#pragma once +#include <wayfire/signal-provider.hpp> +#include <wayfire/opengl.hpp> +namespace wf { +struct ambient_query_signal { wf::output_t *output; bool enabled = false; }; +struct ambient_render_signal { + wf::output_t *output; + const wf::render_target_t *target; + const wf::regionf_t *damage; + bool handled = false; +}; +struct ambient_transition_signal { wf::output_t *output; bool moving; }; +struct ambient_changed_signal {}; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/include/effect-settings.inc b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/include/effect-settings.inc new file mode 100644 index 000000000000..df78556402c2 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/include/effect-settings.inc @@ -0,0 +1,71 @@ + register_setting<double>("background_black_delay", 600); + register_setting<double>("effect_black_delay", 600); + register_setting<double>("black_fade_duration", 0.667); + register_setting<std::string>("hyperspace_background", std::string("none")); + register_setting<double>("hyperspace_background_opacity", 1); + register_setting<double>("hyperspace_speed", 0.65); + register_setting<double>("hyperspace_cloud_density", 0.85); + register_setting<double>("hyperspace_turbulence", 0.65); + register_setting<double>("hyperspace_ribbon_strength", 0.75); + register_setting<double>("hyperspace_glow", 0.65); + register_setting<double>("hyperspace_brightness", 0.85); + register_setting<double>("hyperspace_surge", 0.7); + register_setting<double>("hyperspace_star_density", 0.65); + register_setting<double>("hyperspace_coloured_stars", 0.12); + register_setting<double>("hyperspace_distant_stars", 1); + register_setting<double>("hyperspace_distant_drift", 0.1); + register_setting<double>("hyperspace_galaxies", 0.7); + register_setting<double>("hyperspace_dust_density", 0.9); + register_setting<double>("hyperspace_cloud_variation", 1.15); + register_setting<double>("hyperspace_cloud_colours", 0.75); + register_setting<double>("hyperspace_pulsars", 0.65); + register_setting<double>("hyperspace_anomalies", 0.65); + register_setting<double>("hyperspace_asteroids", 0.65); + register_setting<int>("hyperspace_seed", 0); + register_setting<wf::color_t>("hyperspace_color", wf::option_type::from_string<wf::color_t>("0.20 0.52 1.0 1.0").value()); + register_setting<int>("hyperspace_quality", 1); + register_setting<int>("hyperspace_fps", 30); + register_setting<std::string>("matrix_background", std::string("none")); + register_setting<double>("matrix_background_opacity", 1); + register_setting<std::string>("matrix_color", std::string("green")); + register_setting<int>("matrix_font_size", 12); + register_setting<int>("matrix_depth_layers", 4); + register_setting<double>("matrix_min_speed", 0.4); + register_setting<double>("matrix_max_speed", 2); + register_setting<double>("matrix_avg_speed", 0.8); + register_setting<int>("matrix_min_trail", 4); + register_setting<int>("matrix_max_trail", 28); + register_setting<double>("matrix_min_density", 0.25); + register_setting<double>("matrix_max_density", 0.45); + register_setting<double>("matrix_layer_near_density", 0.15); + register_setting<double>("matrix_layer_far_density", 0.35); + register_setting<int>("matrix_head_hold_frames", 5); + register_setting<double>("matrix_head_mutation_chance", 0.15); + register_setting<double>("matrix_head_white_min", 0.75); + register_setting<double>("matrix_head_white_max", 1); + register_setting<bool>("matrix_monitor_colours", true); + register_setting<std::string>("lock_background", std::string("inherit")); + register_setting<double>("lock_speed", 1); + register_setting<double>("lock_brightness", .65); + register_setting<bool>("lock_colour_override", false); + register_setting<wf::color_t>("lock_colour", wf::option_type::from_string<wf::color_t>("0.20 0.52 1.0 1.0").value()); + register_setting<bool>("lock_show_clock", true); + register_setting<bool>("lock_show_date", true); + register_setting<std::string>("lock_output", std::string("all")); + register_setting<bool>("lock_ui_on_interaction", true); + register_setting<double>("lock_ui_hide_delay", 15); + register_setting<double>("lock_ui_fade_duration", .25); + register_setting<double>("lock_black_delay", 600); + register_setting<double>("lock_fade_duration", 2); + register_setting<std::string>("nebula_background", std::string("none")); + register_setting<double>("nebula_background_opacity", 1); + register_setting<double>("nebula_speed", 0.65); + register_setting<double>("nebula_brightness", 0.85); + register_setting<double>("nebula_cloud_density", 0.85); + register_setting<double>("nebula_star_density", 1); + register_setting<double>("nebula_cloud_colours", 0.75); + register_setting<double>("nebula_resolution_scale", 1.0); + register_setting<wf::color_t>("nebula_color", wf::option_type::from_string<wf::color_t>("0.30 0.45 1.0 1.0").value()); + register_setting<int>("nebula_seed", 0); + register_setting<int>("nebula_quality", 2); + register_setting<double>("hyperspace_fractal_detail", 0); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/metadata/ambient-background.xml b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/metadata/ambient-background.xml new file mode 100644 index 000000000000..719a25f044f3 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/metadata/ambient-background.xml @@ -0,0 +1,641 @@ +<?xml version='1.0' encoding='utf-8'?> +<wayfire> + <plugin name="ambient-background"> + <_short>Ambient Background</_short> + <_long>Live wallpapers with Hyperspace, Nebula and Digital Rain, independent monitor fading, and optional idle black transitions.</_long> + <category>Desktop</category> + <group> + <_short>Desktop and idle</_short> + <option name="enabled" type="bool"> + <_short>Enable ambient background</_short> + <default>false</default> + </option> + <option name="backend" type="string"> + <_short>Live wallpaper</_short> + <default>hyperspace</default> + <desc> + <value>hyperspace</value> + <_name>Hyperspace</_name> + </desc> + <desc> + <value>matrix</value> + <_name>Digital Rain</_name> + </desc> + <_long>Choose an animated wallpaper. Each effect has its own appearance and background controls.</_long> + <desc> + <value>nebula</value> + <_name>Nebula</_name> + </desc> + </option> + <option name="toggle_background" type="activator"> + <_short>Toggle ambient background</_short> + <default><super> <alt> KEY_B</default> + <_long>Stop ambient GPU rendering on all monitors and show the normal desktop wallpaper. Press again to resume. Effect settings are preserved, and idle management and session locking continue. The pause lasts until you resume, change Enable ambient background, or restart the session.</_long> + </option> + <option name="focused_delay" type="double"> + <_short>Focused monitor idle delay</_short> + <default>240</default> + <min>0</min> + <max>86400</max> + </option> + <option name="inactive_delay" type="double"> + <_short>Inactive monitor idle delay</_short> + <default>120</default> + <min>0</min> + <max>86400</max> + </option> + <option name="fade_duration" type="double"> + <_short>Desktop fade duration</_short> + <default>20</default> + <min>0.1</min> + <max>120</max> + </option> + <option name="restore_duration" type="double"> + <_short>Desktop restore duration</_short> + <default>.25</default> + <min>0.05</min> + <max>10</max> + </option> + <option name="minimum_opacity" type="double"> + <_short>Minimum desktop opacity</_short> + <default>0</default> + <min>0</min> + <max>1</max> + </option> + <option name="manual_duration" type="double"> + <_short>Ambient-only transition duration</_short> + <default>.7</default> + <min>0.1</min> + <max>10</max> + </option> + <option name="fade_to_black" type="bool"> + <_short>Fade to black when idle</_short> + <default>true</default> + <_long>Keep the background and animation black transitions during automatic idle. Manual ambient-only mode keeps the animation visible.</_long> + </option> + <option name="toggle" type="activator"> + <_short>Toggle ambient-only mode</_short> + <default>none</default> + <_long>Hide windows, panels, launchers and docks on all monitors. No default shortcut; choose an unused binding. Repeat the shortcut or press Escape to restore.</_long> + </option> + <option name="restore" type="activator"> + <_short>Restore desktop</_short> + <default>KEY_ESC</default> + </option> + <option name="background_black_delay" type="double"> + <_short>Background black delay</_short> + <_long>Seconds after idle activation before an optional wallpaper fades to black. Zero disables this stage.</_long> + <default>600</default> + <min>0</min> + <max>86400</max> + </option> + <option name="effect_black_delay" type="double"> + <_short>Animation black delay</_short> + <_long>Additional seconds after the background becomes black before the animation fades away. Zero keeps the animation visible.</_long> + <default>600</default> + <min>0</min> + <max>86400</max> + </option> + <option name="black_fade_duration" type="double"> + <_short>Black transition duration</_short> + <default>0.667</default> + <min>0.05</min> + <max>10</max> + </option> + </group> + <group> + <_short>Hyperspace</_short> + <option name="hyperspace_background" type="string"> + <_short>Hyperspace background</_short> + <_long>None shows only the animated effect. Desktop wallpaper adds your existing wallpaper underneath it.</_long> + <default>none</default> + <desc> + <value>none</value> + <_name>None</_name> + </desc> + <desc> + <value>wallpaper</value> + <_name>Desktop wallpaper</_name> + </desc> + </option> + <option name="hyperspace_background_opacity" type="double"> + <_short>Hyperspace wallpaper opacity</_short> + <default>1</default> + <min>0</min> + <max>1</max> + </option> + <option name="hyperspace_speed" type="double"> + <_short>Hyperspace speed</_short> + <_long>Continuous forward travel speed. Zero freezes clouds and both star layers.</_long> + <default>0.65</default> + <min>0</min> + <max>5</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_cloud_density" type="double"> + <_short>Cloud density</_short> + <_long>Strength and absorption of irregular dust banks at different distances. Banks approach and pass the camera with perspective motion. Zero removes clouds.</_long> + <default>0.85</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_turbulence" type="double"> + <_short>Cloud turbulence</_short> + <_long>Amount of structural distortion in the dust wisps and filaments. Their shapes remain attached to the cloud banks as you travel.</_long> + <default>0.65</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_ribbon_strength" type="double"> + <_short>Light ribbons</_short> + <_long>Brightness of irregular filaments that follow the cloud field.</_long> + <default>0.75</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_glow" type="double"> + <_short>Dust and star glow</_short> + <_long>Light scattered by dust filaments and halos around stars.</_long> + <default>0.65</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_brightness" type="double"> + <_short>Hyperspace brightness</_short> + <_long>Brightness of the complete Hyperspace effect.</_long> + <default>0.85</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_surge" type="double"> + <_short>Jump surge</_short> + <_long>Smooth acceleration during workspace transitions, easing back to continuous travel afterward. Zero keeps a steady speed.</_long> + <default>0.7</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_star_density" type="double"> + <_short>Passing stars</_short> + <_long>Density of stars along the journey, from small middle-distance points to fast nearby trails. Stars fill the viewing frustum, including panoramic outputs, and bridge the foreground to the distant starfield.</_long> + <default>0.65</default> + <min>0</min> + <max>3</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_coloured_stars" type="double"> + <_short>Coloured stars</_short> + <_long>Fraction of warm or blue stars in both layers. Most stars are white at the default 12%. Zero makes all stars white; one colours every star.</_long> + <default>0.12</default> + <min>0</min> + <max>1</max> + <precision>0.01</precision> + </option> + <option name="hyperspace_distant_stars" type="double"> + <_short>Distant stars</_short> + <_long>Density of the faint distant stars behind the clouds. They remain points and never inherit the foreground jump surge.</_long> + <default>1</default> + <min>0</min> + <max>3</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_distant_drift" type="double"> + <_short>Distant star drift</_short> + <_long>Very slow drift of the distant starfield and perspective travel of distant galaxies. Zero keeps both stationary while nearby stars continue moving. Galaxies never inherit jump surges.</_long> + <default>0.1</default> + <min>0</min> + <max>1</max> + <precision>0.01</precision> + </option> + <option name="hyperspace_galaxies" type="double"> + <_short>Distant galaxy density</_short> + <_long>Faint spiral, inclined disc and elliptical galaxies near the distant starfield. Seeded shapes, colours and sizes move slowly outwards with depth, behind passing stars and nebulae. Zero disables galaxies without resetting the journey.</_long> + <default>0.7</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_dust_density" type="double"> + <_short>Space dust</_short> + <_long>Density of faint particles at different depths. Nearby dust makes short streaks; distant dust stays fine. Zero removes dust.</_long> + <default>0.9</default> + <min>0</min> + <max>3</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_cloud_variation" type="double"> + <_short>Cloud variation</_short> + <_long>Amount of broken cloud coverage, dark gaps and filament contrast in the seeded nebula field.</_long> + <default>1.15</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_cloud_colours" type="double"> + <_short>Cloud colour variation</_short> + <_long>Blend the Hyperspace colour with seeded blue, violet, teal, rose and occasional amber dust banks. Each bank keeps its colour while it travels. Zero uses only the chosen Hyperspace colour; one uses the full varied palette. Star colours remain independent.</_long> + <default>0.75</default> + <min>0</min> + <max>1</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_pulsars" type="double"> + <_short>Pulsar strength</_short> + <_long>Brightness of occasional distant blue-white cores and slowly rotating jets. Events fade smoothly with varied positions, sizes and timing. Zero removes pulsars.</_long> + <default>0.65</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_anomalies" type="double"> + <_short>Rare anomalies</_short> + <_long>Intensity of occasional distant binary stars and broken glowing remnants. Encounters have varied positions, sizes, shapes and quiet intervals; they fade gradually. Independent of pulsars. Zero disables anomalies.</_long> + <default>0.65</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_asteroids" type="double"> + <_short>Asteroid field density</_short> + <_long>Sparse encounters with solid, cratered rocks and fractured mineral surfaces. Sizes range from small fragments to rare large asteroids. They approach near the vanishing point and sweep quickly past the camera, with long gaps between fields. Zero disables asteroids.</_long> + <default>0.35</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="hyperspace_seed" type="int"> + <_short>Journey seed</_short> + <_long>Choose repeatable clouds, particles, encounters and asteroid fields. Zero chooses a new random journey when this effect loads. Objects vary on each pass without changing abruptly in view.</_long> + <default>0</default> + <min>0</min> + <max>65535</max> + </option> + <option name="hyperspace_color" type="color"> + <_short>Hyperspace colour</_short> + <_long>Tint of the clouds and light ribbons. White stars retain their neutral colour.</_long> + <default>0.20 0.52 1.0 1.0</default> + </option> + <option name="hyperspace_quality" type="int"> + <_short>Hyperspace quality</_short> + <_long>Cloud resolution and volume detail, plus star and dust counts. Particles always render at full output resolution.</_long> + <default>1</default> + <min>0</min> + <max>2</max> + <desc> + <value>0</value> + <_name>Low</_name> + </desc> + <desc> + <value>1</value> + <_name>Balanced</_name> + </desc> + <desc> + <value>2</value> + <_name>Detailed</_name> + </desc> + </option> + <option name="hyperspace_fps" type="int"> + <_short>Hyperspace frame rate</_short> + <_long>Frame rate while the desktop is idle. Workspace transitions keep their normal refresh rate.</_long> + <default>30</default> + <min>10</min> + <max>120</max> + </option> + <option name="hyperspace_fractal_detail" type="double"> + <_short>Fractal cloud detail</_short> + <_long>Blend Nebula fractal filaments into the existing moving cloud banks. Structure stays attached to each bank. Zero retains the original clouds; this does not alter stars, galaxies or encounters.</_long> + <default>0</default> + <min>0</min> + <max>1</max> + <precision>0.05</precision> + </option> + </group> + <group> + <_short>Nebula</_short> + <option name="nebula_background" type="string"> + <_short>Nebula background</_short> + <_long>Optional static wallpaper behind the emitted nebula and stars. None removes the image.</_long> + <default>none</default> + <desc> + <value>none</value> + <_name>None</_name> + </desc> + <desc> + <value>wallpaper</value> + <_name>Desktop wallpaper</_name> + </desc> + </option> + <option name="nebula_background_opacity" type="double"> + <_short>Nebula background opacity</_short> + <default>1</default> + <min>0</min> + <max>1</max> + <precision>0.05</precision> + </option> + <option name="nebula_speed" type="double"> + <_short>Nebula speed</_short> + <_long>Continuous drift and rotation through the fractal field. Zero freezes it; Cube movement does not accelerate it.</_long> + <default>0.65</default> + <min>0</min> + <max>5</max> + <precision>0.05</precision> + </option> + <option name="nebula_brightness" type="double"> + <_short>Nebula brightness</_short> + <default>0.85</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="nebula_cloud_density" type="double"> + <_short>Nebula clouds</_short> + <default>0.85</default> + <min>0</min> + <max>2</max> + <precision>0.05</precision> + </option> + <option name="nebula_star_density" type="double"> + <_short>Nebula stars</_short> + <default>1</default> + <min>0</min> + <max>3</max> + <precision>0.05</precision> + </option> + <option name="nebula_cloud_colours" type="double"> + <_short>Nebula colour variation</_short> + <default>0.75</default> + <min>0</min> + <max>1</max> + <precision>0.05</precision> + </option> + <option name="nebula_color" type="color"> + <_short>Nebula base colour</_short> + <default>0.30 0.45 1.0 1.0</default> + </option> + <option name="nebula_resolution_scale" type="double"> + <_short>Nebula render resolution</_short> + <_long>Fraction of output resolution for the procedural field, smoothly upscaled. Lower values reduce GPU work; other desktop elements retain full resolution.</_long> + <default>1</default> + <min>0.25</min> + <max>1</max> + <precision>0.05</precision> + </option> + <option name="nebula_seed" type="int"> + <_short>Nebula scene seed</_short> + <_long>Fixed seeds reproduce a scene. Zero generates a new scene when the renderer is created.</_long> + <default>0</default> + <min>0</min> + <max>2147483647</max> + </option> + <option name="nebula_quality" type="int"> + <_short>Nebula detail</_short> + <default>2</default> + <min>0</min> + <max>2</max> + <desc> + <value>0</value> + <_name>Low</_name> + </desc> + <desc> + <value>1</value> + <_name>Balanced</_name> + </desc> + <desc> + <value>2</value> + <_name>High</_name> + </desc> + </option> + </group> + <group> + <_short>Digital Rain</_short> + <option name="matrix_background" type="string"> + <_short>Digital Rain background</_short> + <_long>None shows only the animated effect. Desktop wallpaper adds your existing wallpaper underneath it.</_long> + <default>none</default> + <desc> + <value>none</value> + <_name>None</_name> + </desc> + <desc> + <value>wallpaper</value> + <_name>Desktop wallpaper</_name> + </desc> + </option> + <option name="matrix_background_opacity" type="double"> + <_short>Digital Rain wallpaper opacity</_short> + <default>1</default> + <min>0</min> + <max>1</max> + </option> + <option name="matrix_color" type="string"> + <_short>Rain colour</_short> + <_long>A named colour, #RRGGBB, random_trail_named, random_trail_hex, or an adaptive colour mode.</_long> + <default>green</default> + </option> + <option name="matrix_font_size" type="int"> + <_short>Glyph size</_short> + <default>12</default> + <min>6</min> + <max>72</max> + </option> + <option name="matrix_depth_layers" type="int"> + <_short>Depth layers</_short> + <default>4</default> + <min>1</min> + <max>8</max> + </option> + <option name="matrix_min_speed" type="double"> + <_short>Minimum fall speed</_short> + <default>0.4</default> + <min>0.001</min> + <max>100</max> + </option> + <option name="matrix_max_speed" type="double"> + <_short>Maximum fall speed</_short> + <default>2</default> + <min>0.001</min> + <max>100</max> + </option> + <option name="matrix_avg_speed" type="double"> + <_short>Fall speed bias</_short> + <default>0.8</default> + <min>0.001</min> + <max>100</max> + </option> + <option name="matrix_min_trail" type="int"> + <_short>Minimum trail lifetime</_short> + <default>4</default> + <min>1</min> + <max>10000</max> + </option> + <option name="matrix_max_trail" type="int"> + <_short>Maximum trail lifetime</_short> + <default>28</default> + <min>1</min> + <max>10000</max> + </option> + <option name="matrix_min_density" type="double"> + <_short>Minimum single-layer density</_short> + <default>0.25</default> + <min>0</min> + <max>1</max> + </option> + <option name="matrix_max_density" type="double"> + <_short>Maximum single-layer density</_short> + <default>0.45</default> + <min>0</min> + <max>1</max> + </option> + <option name="matrix_layer_near_density" type="double"> + <_short>Near-layer density</_short> + <default>0.15</default> + <min>0</min> + <max>2</max> + </option> + <option name="matrix_layer_far_density" type="double"> + <_short>Far-layer density</_short> + <default>0.35</default> + <min>0</min> + <max>2</max> + </option> + <option name="matrix_head_hold_frames" type="int"> + <_short>Head glyph hold frames</_short> + <default>5</default> + <min>0</min> + <max>120</max> + </option> + <option name="matrix_head_mutation_chance" type="double"> + <_short>Head mutation chance</_short> + <default>0.15</default> + <min>0</min> + <max>1</max> + </option> + <option name="matrix_head_white_min" type="double"> + <_short>Minimum head whiteness</_short> + <default>0.75</default> + <min>0</min> + <max>1</max> + </option> + <option name="matrix_head_white_max" type="double"> + <_short>Maximum head whiteness</_short> + <default>1</default> + <min>0</min> + <max>1</max> + </option> + <option name="matrix_monitor_colours" type="bool"> + <_short>Use monitor colour overrides</_short> + <_long>Use the per-monitor colours configured in ambient-background.toml.</_long> + <default>true</default> + </option> + </group> + <group> + <_short>Lock screen</_short> + <option name="lock_background" type="string"> + <_short>Lock-screen background</_short> + <_long>Choose the Ambient effect, Hyperspace, Nebula, Digital Rain, or a plain black lock screen.</_long> + <default>inherit</default> + <desc> + <value>inherit</value> + <_name>Same as Ambient</_name> + </desc> + <desc> + <value>hyperspace</value> + <_name>Hyperspace</_name> + </desc> + <desc> + <value>digitalrain</value> + <_name>Digital Rain</_name> + </desc> + <desc> + <value>black</value> + <_name>Black</_name> + </desc> + <desc> + <value>nebula</value> + <_name>Nebula</_name> + </desc> + </option> + <option name="lock_speed" type="double"> + <_short>Lock animation speed</_short> + <_long>Multiplier for the selected effect while locked. Zero freezes the animation.</_long> + <default>1</default> + <min>0</min> + <max>3</max> + </option> + <option name="lock_brightness" type="double"> + <_short>Lock animation brightness</_short> + <_long>Brightness multiplier for the locked background.</_long> + <default>.65</default> + <min>0</min> + <max>2</max> + </option> + <option name="lock_colour_override" type="bool"> + <_short>Override lock colours</_short> + <_long>Use the lock colour instead of the selected effect’s base colour.</_long> + <default>false</default> + </option> + <option name="lock_colour" type="color"> + <_short>Lock colour</_short> + <_long>Base colour for the lock background when the override is enabled.</_long> + <default>0.20 0.52 1.0 1.0</default> + </option> + <option name="lock_output" type="string"> + <_short>Lock controls monitor</_short> + <_long>Show the clock and unlock controls on one physical monitor or all monitors. If the selected monitor is disconnected, controls appear on an available monitor. The background and session protection cover every monitor.</_long> + <default>all</default> + <desc> + <value>all</value> + <_name>All monitors</_name> + </desc> + </option> + <option name="lock_ui_on_interaction" type="bool"> + <_short>Reveal lock controls on interaction</_short> + <_long>Keep the clock, date and unlock controls hidden until keyboard or mouse interaction.</_long> + <default>true</default> + </option> + <option name="lock_ui_hide_delay" type="double"> + <_short>Lock controls hide delay</_short> + <_long>Seconds without input before hiding the unlock controls and clearing the password. Zero keeps them visible after interaction. Authentication in progress remains visible.</_long> + <default>15</default> + <min>0</min> + <max>600</max> + </option> + <option name="lock_ui_fade_duration" type="double"> + <_short>Lock controls fade duration</_short> + <_long>Seconds for the clock and unlock controls to appear or disappear.</_long> + <default>0.25</default> + <min>0.05</min> + <max>3</max> + </option> + <option name="lock_show_clock" type="bool"> + <_short>Show lock clock</_short> + <_long>Show the time on the selected lock-controls monitor or monitors.</_long> + <default>true</default> + </option> + <option name="lock_show_date" type="bool"> + <_short>Show lock date</_short> + <_long>Show the date below the lock clock.</_long> + <default>true</default> + </option> + <option name="lock_black_delay" type="double"> + <_short>Lock fade-to-black delay</_short> + <_long>Seconds without lock-screen input before the animation fades to black. Zero keeps it visible.</_long> + <default>600</default> + <min>0</min> + <max>86400</max> + </option> + <option name="lock_fade_duration" type="double"> + <_short>Lock fade duration</_short> + <_long>Seconds for the lock background to fade to black and return on input.</_long> + <default>2</default> + <min>0.1</min> + <max>30</max> + </option> + </group> + </plugin> +</wayfire> diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-4k.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-4k.py new file mode 100755 index 000000000000..0cbc6d9dfb1f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-4k.py @@ -0,0 +1,40 @@ +from pathlib import Path +import sys,time +from PIL import Image,ImageChops +stage=Path(sys.argv[1]);sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=''' +[core] +vwidth=4 +vheight=3 +[cube] +render_at_rest=true +mouse_overview=true +hyperspace_speed=1.5 +[ambient-background] +enabled=true +hyperspace_quality=2 +hyperspace_speed=1.5 +controller= +manual_duration=.1 +''' +for i in range(3):extra+=f'\n[output:HEADLESS-{i+1}]\nmode=3840x2160@60\nposition={i*3840},0\n' +extra+='\n[output:HEADLESS-4]\nposition=11520,0\n' +with PrivateSession(stage,hotplug=True,extra_plugins=' ambient-background cube render-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + def call(m,d=None): + r=s.call(m,d);assert not isinstance(r,dict) or not r.get('error'),r + return r + def snap(name): + p=s.root/(name+'.png');s.run(['grim','-o','VIRTUAL-1',str(p)],timeout=25);return Image.open(p).convert('RGB') + s.wait(lambda:len(call('ambient/status')['outputs'])==4) + call('stipc/move_cursor',{'x':11530,'y':10});time.sleep(.4) + a=snap('4k-first');assert a.size==(11520,2160) + time.sleep(.3);assert ImageChops.difference(a,snap('4k-next')).getbbox() + call('test/stats');time.sleep(1);frames=call('test/stats')['passes'];assert 10<=frames<=90,frames + call('test/cube',{'output':'VIRTUAL-1','angle':.65,'zoom':.55});time.sleep(.3) + assert call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + call('test/cube',{'output':'VIRTUAL-1','stop':True});time.sleep(.3) + logs=(s.root/'wayfire.log').read_text(errors='replace') + assert 'GL_INVALID' not in logs and 'Shader compilation failed' not in logs + print('PASS 3x4K continuous Hyperspace, Cube delegation and partial damage; frame passes',frames,flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-autostart.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-autostart.py new file mode 100755 index 000000000000..84ac12a00684 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-autostart.py @@ -0,0 +1,48 @@ +from pathlib import Path +import json,os,signal,socket,struct,sys,time +stage=Path(sys.argv[1]);sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=f''' +[ambient-background] +enabled=true +backend=matrix +controller={stage}/ambient-background --daemon --renderer {stage}/digitalrain +focused_delay=86400 +inactive_delay=86400 +''' +s=PrivateSession(stage,hotplug=True,extra_plugins=' ambient-background digitalrain-cursor digitalrain-idle digitalrain-sampler',config_extra=extra) +config=s.root/'ambient-background.toml';config.write_text('''schema_version=1 +[global] +TIMEOUT=86400 +FOCUSED_TIMEOUT=86400 +LOCK_TIMEOUT=0 +LOG_FILE="stderr" +''') +s.env['AMBIENT_BACKGROUND_CONFIG']=str(config) +manager_pid=None;helper_pid=None +try: + with s: + print('SESSION',s.root,flush=True) + control=s.root/'digitalrain/control.sock';s.wait(control.exists) + with socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET) as conn: + conn.connect(str(control));manager_pid,uid,_=struct.unpack('3i',conn.getsockopt(socket.SOL_SOCKET,socket.SO_PEERCRED,12));assert uid==os.geteuid() + conn.send(b'{"version":1,"method":"status"}');status=json.loads(conn.recv(1024*1024));assert status['config_file']==str(config) + s.wait(lambda:sum(v.get('app-id')=='ambient-matrix' for v in s.call('window-rules/list-views'))==4) + print('PASS enabled plugin starts its controller, idle manager and all Matrix renderers without separate autostart',flush=True) + for entry in Path('/proc').iterdir(): + if not entry.name.isdigit():continue + try: + command=entry.joinpath('cmdline').read_bytes() + env=entry.joinpath('environ').read_bytes() + if b'ambient-background\0--daemon' in command and ('XDG_RUNTIME_DIR='+str(s.root)).encode()+b'\0' in env: + helper_pid=int(entry.name) + except OSError:pass +finally: + for pid in [helper_pid,manager_pid]: + if pid is None:continue + try: + proc=Path('/proc')/str(pid) + assert proc.stat().st_uid==os.geteuid() + assert ('XDG_RUNTIME_DIR='+str(s.root)).encode()+b'\0' in proc.joinpath('environ').read_bytes() + os.kill(pid,signal.SIGTERM) + except FileNotFoundError:pass diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-controller.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-controller.py new file mode 100755 index 000000000000..ead91775ee85 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-controller.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +from pathlib import Path +import json,os,signal,socket,sys,time +from PIL import Image,ImageChops +stage=Path(sys.argv[1]).resolve();sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=f''' +[core] +vwidth=4 +vheight=3 +[cube] +render_at_rest=true +hyperspace_speed=1.5 +hyperspace_seed=31415 +[ambient-background] +enabled=true +controller= +focused_delay=2 +inactive_delay=1 +fade_duration=0.3 +restore_duration=0.05 +manual_duration=0.1 +backend=matrix +background_black_delay=2 +effect_black_delay=2 +black_fade_duration=.2 +''' +with PrivateSession(stage,hotplug=True,extra_plugins=' ambient-background digitalrain-cursor digitalrain-idle digitalrain-sampler render-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + def call(m,d=None): + r=s.call(m,d);assert not isinstance(r,dict) or not r.get('error'),r + return r + config=s.root/'dr.toml';config.write_text('''schema_version=1 +[global] +TIMEOUT=86400 +FOCUSED_TIMEOUT=86400 +CHECK_INTERVAL=1 +LOCK_TIMEOUT=0 +LOG_FILE="stderr" +[matrix] +COLOR="green" +FONT_SIZE=14 +DEPTH_LAYERS=4 +TRANSPARENT=true +TRANSPARENT_TIMEOUT=2 +BLACK_RAIN_TIMEOUT=2 +FADE_FRAMES=6 +POWER_OFF=false +HIDE_DESKTOP=false +''') + s.env['DIGITALRAIN_CONFIG']=str(config) + dr=s.spawn('digitalrain',[str(stage/'digitalrain')]) + control=s.root/'digitalrain/control.sock';s.wait(control.exists) + def request(m,**data): + stream=socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET);stream.settimeout(3);stream.connect(str(control)) + stream.send(json.dumps({'version':1,'method':m,**data}).encode());r=json.loads(stream.recv(1024*1024));assert not r.get('error'),r + return stream,r + def status(): + conn,r=request('status');conn.close();return r + s.wait(lambda:len(status()['outputs'])==4) + owner,r=request('ambient',enabled=True) + assert r['ambient_active'] + time.sleep(.4);older=status();assert max(o['idle_seconds'] for o in older['outputs'])>.2,'Ambient ownership reset activity' + other=socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET);other.connect(str(control));other.send(b'{"version":1,"method":"ambient","enabled":true}') + assert json.loads(other.recv(1024*1024)).get('error');other.close() + owner.close();s.wait(lambda:not status()['ambient_active']) + print('PASS bounded ownership, disconnect release and preserved per-monitor idle clocks',flush=True) + s.spawn('wallpaper',['python3',str(stage/'tests/ambient-layer.py'),'wallpaper']) + s.spawn('panel',['python3',str(stage/'tests/ambient-layer.py')]) + s.wait(lambda:any(v.get('app-id')=='ambient-fixture-panel' for v in call('window-rules/list-views'))) + conn,_=request('wake');conn.close() + controller=s.spawn('ambient-controller',[str(stage/'ambient-background'),'--daemon','--digitalrain',str(stage/'digitalrain')]) + s.wait(lambda:status()['ambient_active']) + s.wait(lambda:sum(v.get('app-id')=='ambient-matrix' for v in call('window-rules/list-views'))==4) + def snap(name): + p=s.root/(name+'.png');s.run(['grim','-o','VIRTUAL-1',str(p)]);return Image.open(p).convert('RGB') + time.sleep(.5);a=snap('rain');time.sleep(.3);b=snap('rain-next') + assert ImageChops.difference(a,b).getbbox(),'Matrix background not animated' + assert a.getpixel((10,5))[1]>150,'Panel hidden before idle' + s.wait(lambda:any(z['opacity']<.05 for z in call('ambient/status')['outputs']),timeout=6) + time.sleep(1);a=snap('idle-rain') + assert any(a.getpixel((x,100))[1]>20 for x in range(0,1920,10)), 'Glyphs absent under hidden desktop' + s.wait(lambda:all(o['idle_seconds']-(2 if o['focused'] else 1)>4.6 for o in status()['outputs']),timeout=10) + time.sleep(.3);black=snap('black') + assert max(max(black.getpixel((x,100))) for x in range(0,1920,40))<=1,'DR final black phase lost' + print('PASS always-running Matrix, real panel fade, and inherited DR black phases',flush=True) + assert call('ambient/toggle')['manual'];time.sleep(.35);a=snap('manual-rain') + assert ImageChops.difference(a,black).getbbox(),'Manual mode kept idle black phase' + call('stipc/move_cursor',{'x':900,'y':450});time.sleep(.4);assert call('ambient/status')['manual'] + call('ambient/restore');time.sleep(.4) + assert all(o['idle_seconds']<1.5 for o in status()['outputs']),'Exit did not reset idle timers' + assert all(z['opacity']>.95 for z in call('ambient/status')['outputs']) + conn,r=request('inhibit',output='HEADLESS-1',seconds=8,reason='private fixture');conn.close();time.sleep(2.5) + alphas={z['name']:z['opacity'] for z in call('ambient/status')['outputs']} + assert alphas['HEADLESS-1']>.95 and any(alphas[n]<.05 for n in ('HEADLESS-2','HEADLESS-3')),alphas + print('PASS manual persistence, idle reset and monitor-specific presentation inhibition',flush=True) + call('ambient/toggle');time.sleep(.2) + os.kill(controller.pid,signal.SIGKILL);controller.wait();s.processes.remove(controller) + s.wait(lambda:not status()['ambient_active']);time.sleep(3.4) + assert not call('ambient/status')['manual'] + assert all(z['opacity']>.95 for z in call('ambient/status')['outputs']) + assert not any(v.get('app-id')=='ambient-matrix' for v in call('window-rules/list-views')) + print('PASS controller crash releases ownership, kills only its renderers and restores desktop',flush=True) + print('ALL AMBIENT CONTROLLER CHECKS PASSED',flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-frames.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-frames.cpp new file mode 100644 index 000000000000..04d61928fbde --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-frames.cpp @@ -0,0 +1,48 @@ +// Private test instrumentation: count repaints without requesting any frames. +#include <wayfire/core.hpp> +#include <wayfire/output.hpp> +#include <wayfire/output-layout.hpp> +#include <wayfire/plugin.hpp> +#include <wayfire/render-manager.hpp> +#include <wayfire/signal-definitions.hpp> +#include <wayfire/plugins/common/shared-core-data.hpp> +#include <wayfire/plugins/ipc/ipc-method-repository.hpp> +#include <map> + +class ambient_frames_t : public wf::plugin_interface_t { + struct counter_t { + int frames = 0; + wf::effect_hook_t hook = [this] { ++frames; }; + }; + std::map<wf::output_t*, counter_t> counters; + wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> repo; + void add(wf::output_t *output) { + if (counters.count(output)) return; + output->render->add_effect(&counters[output].hook, wf::OUTPUT_EFFECT_PRE); + } + wf::signal::connection_t<wf::output_added_signal> added = [this] (auto ev) { add(ev->output); }; + wf::signal::connection_t<wf::output_pre_remove_signal> removed = [this] (auto ev) { + auto it = counters.find(ev->output); + if (it != counters.end()) { + ev->output->render->rem_effect(&it->second.hook); + counters.erase(it); + } + }; + public: + void init() override { + for (auto output : wf::get_core().output_layout->get_outputs()) add(output); + wf::get_core().output_layout->connect(&added); + wf::get_core().output_layout->connect(&removed); + repo->register_method("test/ambient-frames", [this] (wf::json_t) { + auto result = wf::ipc::json_ok(); + for (auto& [output, counter] : counters) result[output->to_string()] = counter.frames; + return result; + }); + } + void fini() override { + added.disconnect(); removed.disconnect(); + for (auto& [output, counter] : counters) output->render->rem_effect(&counter.hook); + repo->unregister_method("test/ambient-frames"); + } +}; +DECLARE_WAYFIRE_PLUGIN(ambient_frames_t); diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-toggle-background.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-toggle-background.py new file mode 100644 index 000000000000..1b7575ca369c --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-toggle-background.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +"""Exercise the shortcut and verify that paused desktop GPU repaints stop.""" +from pathlib import Path +import json +import socket +import sys +import time +from PIL import Image + +stage = Path(sys.argv[1]).resolve() +sys.path.insert(0, str(stage / 'tests')) +from private_session import PrivateSession + +extra = ''' +[core] +vwidth=4 +vheight=3 +[cube] +render_at_rest=true +background_mode=hyperspace +hyperspace_animate_at_rest=true +[ambient-background] +enabled=true +controller= +focused_delay=86400 +inactive_delay=86400 +restore_duration=.05 +manual_duration=.1 +hyperspace_quality=0 +nebula_quality=0 +nebula_resolution_scale=.25 +''' +with PrivateSession(stage, hotplug=True, + extra_plugins=' ambient-background cube digitalrain-cursor digitalrain-idle digitalrain-sampler ambient-frames', + config_extra=extra) as s: + print('SESSION', s.root, flush=True) + def call(method, data=None): + result = s.call(method, data) + assert not isinstance(result, dict) or not result.get('error'), result + return result + def shortcut(key='KEY_B'): + keys = ['KEY_LEFTMETA', 'KEY_LEFTALT', key] + for item in keys: call('stipc/feed_key', {'key': item, 'state': True}) + for item in reversed(keys): call('stipc/feed_key', {'key': item, 'state': False}) + def frames(): + return {k: v for k, v in call('test/ambient-frames').items() if isinstance(v, int)} + def paused(): + assert call('ambient/status')['suspended'] + time.sleep(.6) + before = frames() + scenes = call('ambient/status') + time.sleep(.5) + assert frames() == before, ('Paused background still repaints', before, frames()) + after = call('ambient/status') + assert after['scenes'] == scenes['scenes'] + assert after['nebula_scenes'] == scenes['nebula_scenes'] + assert all(z['opacity'] == 1 for z in after['outputs']) + image = s.root / 'paused-wallpaper.png' + s.run(['grim', '-o', 'VIRTUAL-1', str(image)]) + assert Image.open(image).convert('RGB').getpixel((500, 300)) == (48, 64, 128) + s.spawn('wallpaper', ['python3', str(stage/'tests/ambient-layer.py'), 'wallpaper']) + s.wait(lambda: any(v.get('app-id') == 'ambient-fixture-wallpaper' for v in call('window-rules/list-views'))) + config = s.root / 'dr.toml' + config.write_text('''schema_version=1 +[global] +TIMEOUT=1 +FOCUSED_TIMEOUT=1 +CHECK_INTERVAL=1 +LOCK_TIMEOUT=0 +LOG_FILE="stderr" +[matrix] +POWER_OFF=false +HIDE_DESKTOP=false +''') + s.env['DIGITALRAIN_CONFIG'] = str(config) + s.spawn('idle', [str(stage/'digitalrain')]) + control = s.root / 'digitalrain/control.sock' + s.wait(control.exists) + s.spawn('controller', [str(stage/'ambient-background'), '--daemon', '--renderer', str(stage/'digitalrain')]) + def idle_status(): + with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as stream: + stream.settimeout(3); stream.connect(str(control)) + stream.send(b'{"version":1,"method":"status"}') + return json.loads(stream.recv(1024*1024)) + s.wait(lambda: idle_status()['ambient_active']) + for backend in ('hyperspace', 'nebula', 'matrix'): + call('wayfire/set-config-options', {'ambient-background/backend': backend}) + if backend == 'matrix': + s.wait(lambda: any(v.get('app-id') == 'ambient-matrix' for v in call('window-rules/list-views'))) + before = frames(); time.sleep(.4) + assert any(frames()[k] > v+3 for k, v in before.items()), 'Enabled animation did not repaint' + shortcut(); paused() + assert not call('ambient/toggle')['manual'], 'Manual mode bypassed suspension' + call('wayfire/set-config-options', {'ambient-background/hyperspace_brightness': .65}) + assert call('ambient/status')['suspended'], 'Appearance change resumed GPU rendering' + assert idle_status()['ambient_active'], 'Pausing released the idle ownership lease' + if backend == 'matrix': + s.wait(lambda: not any(v.get('app-id') == 'ambient-matrix' for v in call('window-rules/list-views'))) + time.sleep(1.2) + assert idle_status()['ambient_active'] + assert not any(v.get('app-id') == 'ambient-matrix' for v in call('window-rules/list-views')) + shortcut() + assert not call('ambient/status')['suspended'] + if backend == 'matrix': + s.wait(lambda: any(v.get('app-id') == 'ambient-matrix' for v in call('window-rules/list-views'))) + before = frames(); time.sleep(.4) + assert any(frames()[k] > v+3 for k, v in before.items()), 'Resume failed to repaint' + print('PASS', backend, 'shortcut, static wallpaper, zero paused repaints, resume and idle ownership', flush=True) + # Rebinding the shortcut works, and the old binding stops toggling it. + call('wayfire/set-config-options', {'ambient-background/toggle_background': '<super> <alt> KEY_Y'}) + shortcut(); assert not call('ambient/status')['suspended'] + shortcut('KEY_Y'); paused(); shortcut('KEY_Y') + call('ambient/toggle'); assert call('ambient/status')['manual'] + shortcut('KEY_Y'); assert not call('ambient/status')['manual']; paused() + call('wayfire/set-config-options', {'ambient-background/enabled': False}) + assert not call('ambient/status')['suspended'] + shortcut('KEY_Y'); assert not call('ambient/status')['suspended'] + call('wayfire/set-config-options', {'ambient-background/enabled': True}) + assert not call('ambient/status')['suspended'] + print('PASS live rebinding, manual-mode restoration and WCM enable reset', flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-wcm.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-wcm.py new file mode 100755 index 000000000000..065895ac2d74 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient-wcm.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python3 +"""Discover and edit Ambient Background through WCM in an isolated GPU session.""" +import configparser +import json +import os +from pathlib import Path +import socket +import struct +import subprocess +import tempfile +import time +import gi +gi.require_version('Atspi', '2.0') +from gi.repository import Atspi + +assert os.geteuid()!=0 +stage=Path(os.environ['AMBIENT_STAGE']) +root=Path(tempfile.mkdtemp(prefix='wcm-ambient-mainpage-')) +env=os.environ.copy() +for key in ('WAYLAND_DISPLAY','DISPLAY','WAYFIRE_SOCKET','_WAYFIRE_SOCKET','WAYFIRE_CONFIG_FILE', + 'WAYFIRE_DEFAULT_CONFIG_BACKEND','NO_AT_BRIDGE'): + env.pop(key,None) +env.update(XDG_RUNTIME_DIR=str(root),XDG_CONFIG_HOME=str(root/'config'),XDG_DATA_HOME=str(root/'data'), + XDG_CACHE_HOME=str(root/'cache'),XDG_STATE_HOME=str(root/'state'),WLR_BACKENDS='headless', + WLR_HEADLESS_OUTPUTS='3',WLR_RENDERER='gles2',WLR_RENDER_DRM_DEVICE='/dev/dri/renderD128', + WAYFIRE_PLUGIN_PATH=str(stage/'plugins'),WAYFIRE_PLUGIN_XML_PATH=str(stage/'metadata'), + GTK_USE_PORTAL='0',GIO_USE_VFS='local',GTK_MODULES='atk-bridge', + XDG_DATA_DIRS='/usr/local/share:/usr/share') +config=root/'wayfire.ini' +config.write_text(f'''# Keep unrelated preferences. +[core] +plugins = ipc stipc ipc-rules {stage}/plugins/libcube.so {stage}/plugins/libambient-background.so place grid +xwayland = false +vwidth = 4 +vheight = 3 +stitch_enabled = true +stitch_outputs = HEADLESS-1 HEADLESS-2 HEADLESS-3 +[ambient-background] +enabled=false +controller= +[cube] +activate = <ctrl> <alt> BTN_LEFT +dim = 0.25 +[grid] +toggle_scope = <super> KEY_ENTER +[output:HEADLESS-1] +mode = 1400x1000@60 +position = 0,0 +[output:HEADLESS-2] +mode = 1400x1000@60 +position = 1400,0 +[output:HEADLESS-3] +mode = 1400x1000@60 +position = 2800,0 +''') +original=config.read_text() +shell=root/'shell.ini';shell.write_text('') +compositor=subprocess.Popen([str(stage/'wayfire'),'-c',str(config)],env=env, + stdout=(root/'wayfire.log').open('w'),stderr=subprocess.STDOUT) +ui=None + +def wait(fn,timeout=15): + until=time.monotonic()+timeout + while time.monotonic()<until: + assert compositor.poll() is None,f'Compositor exited; {root}' + if ui is not None:assert ui.poll() is None,f'WCM exited; {root}' + value=fn() + if value:return value + time.sleep(.05) + raise TimeoutError(str(root)) + +def ipc(method,data=None): + with socket.socket(socket.AF_UNIX) as stream: + stream.settimeout(5);stream.connect(env['WAYFIRE_SOCKET']) + assert struct.unpack('3i',stream.getsockopt(socket.SOL_SOCKET,socket.SO_PEERCRED,12))[:2]==(compositor.pid,os.geteuid()) + payload=json.dumps({'method':method,'data':data or {}}).encode() + stream.sendall(struct.pack('<I',len(payload))+payload) + def read(n): + value=b'' + while len(value)<n: + block=stream.recv(n-len(value));assert block;value+=block + return value + result=json.loads(read(struct.unpack('<I',read(4))[0])) + assert not isinstance(result,dict) or not result.get('error'),result + return result + +def nodes(): + stack=[Atspi.get_desktop(0)] + while stack: + node=stack.pop() + if node is None:continue + yield node + stack.extend(node.get_child_at_index(i) for i in range(node.get_child_count())) + +def find(name,role): + return next((n for n in nodes() if n.get_name()==name and n.get_role()==role),None) + +def showing(node): + return node is not None and node.get_state_set().contains(Atspi.StateType.SHOWING) + +def overview_check(name='Show all workspace rows'): + # Generic WCM options place a separate label beside an unnamed checkbox. + label=find(name,Atspi.Role.LABEL) + if label is None:return None + container=label.get_parent() + for _ in range(4): + stack=[container];checks=[] + while stack: + node=stack.pop() + if node.get_role()==Atspi.Role.CHECK_BOX:checks.append(node) + stack.extend(node.get_child_at_index(i) for i in range(node.get_child_count())) + if len(checks)==1:return checks[0] + container=container.get_parent() + return None + +def value(): + parsed=configparser.ConfigParser(interpolation=None,strict=False) + parsed.read(config) + return parsed + +try: + env['WAYFIRE_SOCKET']=str(wait(lambda:next(root.glob('wayfire-*.socket'),None))) + env['WAYLAND_DISPLAY']=ipc('stipc/get_display')['wayland'] + ui=subprocess.Popen([str(stage/'wcm'),'--config',str(config),'--shell-config',str(shell)], + env=env,stdout=(root/'wcm.log').open('w'),stderr=subprocess.STDOUT) + page=wait(lambda:find('Ambient Background',Atspi.Role.PUSH_BUTTON)) + page.get_component_iface().scroll_to(Atspi.ScrollType.ANYWHERE) + wait(lambda:showing(page)) + assert page.get_action_iface().do_action(0) + wait(lambda:find('Live wallpaper',Atspi.Role.LABEL)) + assert config.read_text()==original,'Opening Ambient settings changed preferences' + def control(label_name,role): + label=find(label_name,Atspi.Role.LABEL) + if label is None:return None + parent=label.get_parent() + for _ in range(4): + stack=[parent];found=[] + while stack: + node=stack.pop() + if node.get_role()==role:found.append(node) + stack.extend(node.get_child_at_index(i) for i in range(node.get_child_count())) + if len(found)==1:return found[0] + parent=parent.get_parent() + return None + def tab(name): + item=wait(lambda:find(name,Atspi.Role.PAGE_TAB)) + assert item.get_parent().get_selection_iface().select_child(item.get_index_in_parent()) + def choose(label,item): + combo=wait(lambda:control(label,Atspi.Role.COMBO_BOX)) + wait(lambda:showing(combo)) + assert combo.get_action_iface().do_action(0) + choice=wait(lambda:next((n for n in nodes() if n.get_name()==item and n.get_role()==Atspi.Role.MENU_ITEM and showing(n)),None)) + assert choice.get_action_iface().do_action(0) + + choose('Live wallpaper','Nebula') + wait(lambda:ipc('ambient/status')['backend']=='nebula') + assert value()['ambient-background']['backend']=='nebula' + tab('Nebula') + choose('Nebula background','Desktop wallpaper') + wait(lambda:ipc('wayfire/get-config-option',{'option':'ambient-background/nebula_background'})['value']=='wallpaper') + choose('Nebula background','None') + nebula_speed=wait(lambda:control('Nebula speed',Atspi.Role.SPIN_BUTTON)) + assert nebula_speed.get_value_iface().set_current_value(1.2) + wait(lambda:float(ipc('ambient/status')['nebula']['nebula_speed'])==1.2) + assert float(value()['ambient-background']['nebula_speed'])==1.2 + tab('Desktop and idle') + choose('Live wallpaper','Digital Rain') + wait(lambda:ipc('wayfire/get-config-option',{'option':'ambient-background/backend'})['value']=='matrix') + assert value()['ambient-background']['backend']=='matrix' + choose('Live wallpaper','Hyperspace') + wait(lambda:ipc('wayfire/get-config-option',{'option':'ambient-background/backend'})['value']=='hyperspace') + delay=wait(lambda:control('Focused monitor idle delay',Atspi.Role.SPIN_BUTTON)) + assert delay.get_value_iface().set_current_value(300) + wait(lambda:float(ipc('wayfire/get-config-option',{'option':'ambient-background/focused_delay'})['value'])==300) + assert float(value()['ambient-background']['focused_delay'])==300 + for label in ['Enable ambient background','Inactive monitor idle delay','Desktop fade duration', + 'Desktop restore duration','Minimum desktop opacity','Ambient-only transition duration', + 'Fade to black when idle','Toggle ambient-only mode','Toggle ambient background','Restore desktop']: + assert find(label,Atspi.Role.LABEL),label + assert ipc('wayfire/get-config-option',{'option':'ambient-background/toggle'})['value']=='none' + background_binding=ipc('wayfire/get-config-option',{'option':'ambient-background/toggle_background'})['value'] + assert set(background_binding.split())=={'<super>','<alt>','KEY_B'},background_binding + assert value()['grid']['toggle_scope']=='<super> KEY_ENTER' + assert 'libambient-background.so' in value()['core']['plugins'] + tab('Hyperspace') + fractal=wait(lambda:control('Fractal cloud detail',Atspi.Role.SPIN_BUTTON)) + assert fractal.get_value_iface().set_current_value(.35) + wait(lambda:float(ipc('ambient/status')['hyperspace']['hyperspace_fractal_detail'])==.35) + assert float(value()['ambient-background']['hyperspace_fractal_detail'])==.35 + choose('Hyperspace background','Desktop wallpaper') + wait(lambda:ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_background'})['value']=='wallpaper') + choose('Hyperspace background','None') + hyper_speed=wait(lambda:control('Hyperspace speed',Atspi.Role.SPIN_BUTTON)) + cube_speed=ipc('wayfire/get-config-option',{'option':'cube/hyperspace_speed'})['value'] + assert hyper_speed.get_value_iface().set_current_value(1.25) + wait(lambda:float(ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_speed'})['value'])==1.25) + assert ipc('wayfire/get-config-option',{'option':'cube/hyperspace_speed'})['value']==cube_speed + rare=wait(lambda:control('Rare anomalies',Atspi.Role.SPIN_BUTTON)) + pulsars=ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_pulsars'})['value'] + assert rare.get_value_iface().set_current_value(.45) + wait(lambda:float(ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_anomalies'})['value'])==.45) + assert ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_pulsars'})['value']==pulsars + assert float(value()['ambient-background']['hyperspace_anomalies'])==.45 + print('PASS WCM Rare anomalies saves and updates independently of pulsars',flush=True) + asteroids=wait(lambda:control('Asteroid field density',Atspi.Role.SPIN_BUTTON)) + old_stars=ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_star_density'})['value'] + assert asteroids.get_value_iface().set_current_value(.8) + wait(lambda:float(ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_asteroids'})['value'])==.8) + assert float(value()['ambient-background']['hyperspace_asteroids'])==.8 + assert ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_star_density'})['value']==old_stars + assert ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_pulsars'})['value']==pulsars + print('PASS WCM Asteroid field density saves independently of stars and pulsars',flush=True) + colours=wait(lambda:control('Cloud colour variation',Atspi.Role.SPIN_BUTTON)) + stars=ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_coloured_stars'})['value'] + assert colours.get_value_iface().set_current_value(.85) + wait(lambda:float(ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_cloud_colours'})['value'])==.85) + assert ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_coloured_stars'})['value']==stars + assert float(value()['ambient-background']['hyperspace_cloud_colours'])==.85 + print('PASS WCM Cloud colour variation saves independently of star colours',flush=True) + galaxy=wait(lambda:control('Distant galaxy density',Atspi.Role.SPIN_BUTTON)) + old_drift=ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_distant_drift'})['value'] + assert galaxy.get_value_iface().set_current_value(1.1) + wait(lambda:float(ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_galaxies'})['value'])==1.1) + assert float(value()['ambient-background']['hyperspace_galaxies'])==1.1 + assert ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_distant_drift'})['value']==old_drift + assert ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_star_density'})['value']==old_stars + print('PASS WCM Distant galaxy density saves independently of drift and stars',flush=True) + tab('Digital Rain') + choose('Digital Rain background','Desktop wallpaper') + wait(lambda:ipc('wayfire/get-config-option',{'option':'ambient-background/matrix_background'})['value']=='wallpaper') + choose('Digital Rain background','None') + glyphs=wait(lambda:control('Glyph size',Atspi.Role.SPIN_BUTTON)) + assert glyphs.get_value_iface().set_current_value(18) + wait(lambda:ipc('ambient/status')['matrix']['FONT_SIZE']==18) + tab('Lock screen') + choose('Lock controls monitor','HEADLESS-2') + wait(lambda:ipc('wayfire/get-config-option',{'option':'ambient-background/lock_output'})['value']=='HEADLESS-2') + assert value()['ambient-background']['lock_output']=='HEADLESS-2' + choose('Lock controls monitor','All monitors') + wait(lambda:ipc('wayfire/get-config-option',{'option':'ambient-background/lock_output'})['value']=='all') + assert value()['ambient-background']['lock_output']=='all' + assert ipc('wayfire/get-config-option',{'option':'ambient-background/lock_ui_on_interaction'})['value']=='true' + controls_delay=wait(lambda:control('Lock controls hide delay',Atspi.Role.SPIN_BUTTON)) + assert controls_delay.get_value_iface().set_current_value(20) + wait(lambda:float(ipc('wayfire/get-config-option',{'option':'ambient-background/lock_ui_hide_delay'})['value'])==20) + assert float(value()['ambient-background']['lock_ui_hide_delay'])==20 + print('PASS physical lock monitor/All selector and input-only UI settings save and update live',flush=True) + choose('Lock-screen background','Nebula') + wait(lambda:ipc('wayfire/get-config-option',{'option':'ambient-background/lock_background'})['value']=='nebula') + assert value()['ambient-background']['lock_background']=='nebula' + choose('Lock-screen background','Digital Rain') + wait(lambda:ipc('wayfire/get-config-option',{'option':'ambient-background/lock_background'})['value']=='digitalrain') + desktop_speed=ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_speed'})['value'] + lock_speed=wait(lambda:control('Lock animation speed',Atspi.Role.SPIN_BUTTON)) + assert lock_speed.get_value_iface().set_current_value(.5) + wait(lambda:float(ipc('wayfire/get-config-option',{'option':'ambient-background/lock_speed'})['value'])==.5) + assert ipc('wayfire/get-config-option',{'option':'ambient-background/hyperspace_speed'})['value']==desktop_speed + assert value()['ambient-background']['lock_background']=='digitalrain' + assert float(value()['ambient-background']['lock_speed'])==.5 + for label in ['Lock animation brightness','Override lock colours','Lock colour','Show lock clock', + 'Show lock date','Lock fade-to-black delay','Lock fade duration', + 'Reveal lock controls on interaction','Lock controls fade duration']: + assert find(label,Atspi.Role.LABEL),label + choose('Lock-screen background','Same as Ambient') + print('PASS WCM Lock screen controls save separately from Ambient appearance',flush=True) + tab('Integration') + assert find('Edit idle, locking, alerts and audio settings',Atspi.Role.PUSH_BUTTON) + assert not find('Digital Rain',Atspi.Role.PUSH_BUTTON), 'Separate legacy page remains' + print('PASS independent appearance and background controls plus unified integration',flush=True) + print('PASS WCM main-screen discovery, effect choices, live idle-delay control, optional shortcut and retained preferences',flush=True) +finally: + if ui is not None: + ui.terminate() + try:ui.wait(timeout=5) + except subprocess.TimeoutExpired:ui.kill();ui.wait(timeout=5) + compositor.terminate() + try:compositor.wait(timeout=5) + except subprocess.TimeoutExpired:compositor.kill();compositor.wait(timeout=5) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient.py new file mode 100755 index 000000000000..d70b8ddf6d6b --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/ambient.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +from pathlib import Path +import json,sys,time +from PIL import Image,ImageChops +stage=Path(sys.argv[1]).resolve();sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=''' +[core] +vwidth=4 +vheight=3 +[cube] +render_at_rest=true +hyperspace_speed=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_cloud_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_dust_density=0 +[ambient-background] +hyperspace_speed=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_cloud_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_dust_density=0 +enabled=true +controller= +manual_duration=0.1 +restore_duration=0.05 +toggle=<super> <alt> KEY_A +''' +with PrivateSession(stage,plugins=False,hotplug=True,extra_plugins=' ambient-background render-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + def call(m,d=None): + result=s.call(m,d);assert not isinstance(result,dict) or not result.get('error'),result + return result + def snap(name): + path=s.root/(name+'.png');s.run(['grim','-o','VIRTUAL-1',str(path)]) + return Image.open(path).convert('RGB') + def press(key): + keys=['KEY_LEFTMETA','KEY_LEFTALT','KEY_A'] if key.startswith('<') else [key] + for k in keys: call('stipc/feed_key',{'key':k,'state':True}) + for k in reversed(keys): call('stipc/feed_key',{'key':k,'state':False}) + def sync(values): + call('ambient/sync',{'outputs':[{'name':'HEADLESS-'+str(i),'opacity':a,'black':0,'effect':1} for i,a in enumerate(values,1)]});time.sleep(.18) + s.wait(lambda:len(call('ambient/status')['outputs'])==4) + call('stipc/move_cursor',{'x':1930,'y':10}) + s.spawn('seam-app',['alacritty','--config-file','/dev/null','--class','ambient-seam','-o','colors.primary.background="#D76F2E"','-e','sleep','180']) + view=s.wait(lambda:next((v for v in call('window-rules/list-views') if v.get('app-id')=='ambient-seam' and v.get('mapped')),None)) + time.sleep(.5) + call('window-rules/set-window-scope',{'id':view['id'],'scope':'virtual'}) + call('window-rules/configure-view',{'id':view['id'],'tiled-edges':0,'geometry':{'x':500,'y':360,'width':800,'height':200}}) + time.sleep(.5) + normal=snap('normal');assert normal.getpixel((550,450))==(215,111,46),normal.getpixel((550,450)) + sync([0,1,1,1]);split=snap('split') + assert split.getpixel((550,450))==(0,0,0),split.getpixel((550,450)) + assert split.getpixel((900,450))==(215,111,46),split.getpixel((900,450)) + sync([.5,1,1,1]);half=snap('half');assert max(abs(a-b) for a,b in zip(half.getpixel((550,450)),(108,56,23)))<=2,half.getpixel((550,450)) + print('PASS per-physical-monitor alpha, seam-spanning app and partial transparency',flush=True) + sync([1,1,1,1]);before=call('window-rules/list-views') + assert call('ambient/toggle')['manual'];time.sleep(.25) + assert all(z['opacity']==0 for z in call('ambient/status')['outputs']) + assert snap('manual').getpixel((900,450))==(0,0,0) + call('stipc/move_cursor',{'x':900,'y':450});time.sleep(.12) + assert call('ambient/status')['manual'],'Mouse dismissed manual mode' + press('KEY_A');assert call('ambient/status')['manual'] + press('KEY_ESC');time.sleep(.15) + assert not call('ambient/status')['manual'],'Escape failed to restore' + assert all(z['opacity']==1 for z in call('ambient/status')['outputs']) + call('stipc/move_cursor',{'x':1930,'y':10}) + assert snap('restored').getpixel((900,450))==(215,111,46) + press('<super> <alt> KEY_A');time.sleep(.15) + assert call('ambient/status')['manual'],'Configured toggle failed' + press('<super> <alt> KEY_A');time.sleep(.15) + assert not call('ambient/status')['manual'] + after=call('window-rules/list-views');assert [(v['id'],v.get('geometry')) for v in before]==[(v['id'],v.get('geometry')) for v in after] + print('PASS explicit toggle, Escape, mouse persistence, restore and unchanged window geometry',flush=True) + call('wayfire/set-config-options',{'ambient-background/hyperspace_speed':1.5,'ambient-background/hyperspace_dust_density':.9}) + call('ambient/toggle');time.sleep(.2) + a=snap('moving');time.sleep(.25);assert ImageChops.difference(a,snap('moving-next')).getbbox() + call('ambient/restore');time.sleep(.2) + plugins=call('wayfire/get-config-option',{'option':'core/plugins'})['value'] + call('wayfire/set-config-options',{'core/plugins':plugins+' cube'});call('wayfire/reload-plugins');time.sleep(.2) + call('test/cube',{'output':'VIRTUAL-1','angle':.65,'zoom':.55});time.sleep(.2) + a=snap('cube');time.sleep(.2);assert ImageChops.difference(a,snap('cube-next')).getbbox() + assert call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + call('test/cube',{'output':'VIRTUAL-1','stop':True});time.sleep(.3) + print('PASS continuously animated desktop and Cube backdrop; clipped damage',flush=True) + time.sleep(1.1);sync([0,1,1,1]);time.sleep(3.15) + assert all(z['opacity']==1 for z in call('ambient/status')['outputs']),'Watchdog left UI hidden' + print('PASS independent compositor watchdog restores UI',flush=True) + call('ambient/toggle');time.sleep(.2) + plugins=call('wayfire/get-config-option',{'option':'core/plugins'})['value'] + call('wayfire/set-config-options',{'core/plugins':' '.join(p for p in plugins.split() if p!='ambient-background')});call('wayfire/reload-plugins');time.sleep(.2) + assert any(v['id']==view['id'] for v in call('window-rules/list-views')) + print('PASS plugin unload restores desktop; ALL AMBIENT GPU CHECKS PASSED',flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/anomalies.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/anomalies.py new file mode 100755 index 000000000000..c5cc7ba72c01 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/anomalies.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Rare encounters: gradual arrival, independent controls, freeze and restore.""" +from pathlib import Path +import sys,time +import numpy as np +from PIL import Image +stage=Path(sys.argv[1]).resolve();sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=""" +[ambient-background] +enabled=true +controller= +focused_delay=86400 +inactive_delay=86400 +hyperspace_speed=5 +hyperspace_seed=31415 +hyperspace_cloud_density=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_dust_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=1 +hyperspace_asteroids=0 +hyperspace_galaxies=0 +""" +with PrivateSession(stage,hotplug=True,extra_plugins=' ambient-background render-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + s.wait(lambda:len(s.call('ambient/status')['outputs'])==4) + assert s.call('ambient/status')['enabled'] + s.call('test/solid',{'key':'desktop','output':'VIRTUAL-1','layer':3,'x':0,'y':0,'w':40,'h':40,'r':0,'g':1,'b':0,'a':1}) + s.call('stipc/move_cursor',{'x':1930,'y':10}) + def snapshot(name): + s.call('ambient/sync',{'outputs':[{'name':'HEADLESS-'+str(i),'opacity':0,'black':0,'effect':1} for i in range(1,5)]}) + s.wait(lambda:all(z["opacity"]==0 for z in s.call("ambient/status")["outputs"])) + time.sleep(.08) + p=s.root/(name+'.png');s.run(['grim','-o','VIRTUAL-1',str(p)]) + return np.asarray(Image.open(p).convert('RGB')) + def settings(**options): + s.call('wayfire/set-config-options',{'ambient-background/'+k:v for k,v in options.items()});time.sleep(.15) + assert snapshot('quiet-start').max()==0, 'Encounter arrived without its quiet interval' + deadline=time.monotonic()+20 + while True: + frame=snapshot('arrival') + if frame.max()>12:break + assert time.monotonic()<deadline,'No rare encounter appeared' + time.sleep(.6) + time.sleep(2) + settings(hyperspace_speed=0) + frozen=snapshot('frozen');time.sleep(.3) + assert np.array_equal(frozen,snapshot('still-frozen')),'Speed zero moved encounter' + settings(hyperspace_anomalies=0) + assert snapshot('disabled').max()==0,'Encounter survived its zero-intensity control' + settings(hyperspace_anomalies=1) + assert np.array_equal(frozen,snapshot('restored')),'Changing intensity reset the encounter' + assert float(s.call('wayfire/get-config-option',{'option':'ambient-background/hyperspace_pulsars'})['value'])==0 + assert s.call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + print('PASS quiet interval, live encounter, independent intensity, frozen clock, restoration and partial damage',flush=True) + log=(s.root/'wayfire.log').read_text(errors='replace') + assert 'GL_INVALID' not in log and 'Shader compilation failed' not in log diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-flyby.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-flyby.py new file mode 100755 index 000000000000..1905f11d3ceb --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-flyby.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +"""Track an opaque close pass all the way to natural viewport clipping.""" +from pathlib import Path +import argparse, json, os, subprocess, tempfile +import numpy as np +from PIL import Image + +p = argparse.ArgumentParser() +p.add_argument('--renderer', type=Path, required=True) +a = p.parse_args() +assert os.geteuid() != 0 +root = Path(tempfile.mkdtemp(prefix='ambient-asteroid-flyby-')) +env = {'PATH': '/usr/bin:/bin', 'EGL_PLATFORM': 'surfaceless', 'HOME': str(root)} +results = [] +encounters = [(13, 6.325440168, False), (85, 74.757486343, True), + (370, 90.816016555, True)] +for seed, arrival, huge in encounters: + for width, height in [(960, 540), (2880, 540), (540, 960)]: + frames = [] + # Include ordinary and near-maximum huge bodies; track the entire pass. + for clock in np.arange(arrival-140/64-.04, arrival+.75, .04): + path = root / 'frame.rgba' + subprocess.run([str(a.renderer), str(path), str(seed), str(clock), str(width), str(height), + str(1 / 64), '2'], env=env, check=True, capture_output=True) + frame = np.frombuffer(path.read_bytes(), dtype=np.uint8).reshape(height, width, 4)[::-1].copy() + path.unlink() + mask = frame[:, :, :3].max(axis=2) > 8 + y, x = np.nonzero(mask) + if len(x) < 6: + continue + centre = (float(x.mean()) * 2 / width - 1, float(y.mean()) * 2 / height - 1) + touches_edge = bool(x.min()==0 or x.max()==width-1 or y.min()==0 or y.max()==height-1) + frames.append({'clock': float(clock), 'centre': centre, 'pixels': int(len(x)), + 'touches_edge': touches_edge, 'frame': frame}) + assert len(frames) > 15, ('Missing flyby', width, height) + first, last = frames[0], frames[-1] + first_offset = float(np.hypot(first['centre'][0]*width/height, first['centre'][1])) + last_offset = max(map(abs, last['centre'])) + visible_travel = last['clock'] - first['clock'] + # Large bodies need greater clearance but still enter inside the central + # portion of the viewport. Height units make this independent of aspect. + assert first_offset < (.4 if huge else .12), ('Arrival outside central flight corridor', first_offset, seed, width, height) + assert max(f['pixels'] for f in frames) > first['pixels']*3, ('No close approach', width, height) + assert last['touches_edge'], ('Body disappeared in front of the camera', last_offset, width, height) + assert last_offset > .65, ('Pass did not leave the central approach', last_offset, width, height) + assert visible_travel/3.6 < .7, ('Asteroid approaches too slowly at speed 2', visible_travel, seed) + edge_travel = .04*sum(max(map(abs,f['centre'])) > .65 for f in frames) + assert edge_travel/3.6 < .2, ('Rock lingered in the periphery at speed 2', edge_travel, seed, width, height) + # The final partly clipped facets must stay opaque: changing the colour + # behind them must not show through a central-area fade or early cutoff. + path = root/'white.rgba' + subprocess.run([str(a.renderer), str(path), str(seed), str(last['clock']), str(width), str(height), + str(1/64), '2', '0', '1'], env=env, check=True, capture_output=True) + white = np.frombuffer(path.read_bytes(), dtype=np.uint8).reshape(height,width,4)[::-1].copy() + body = last['frame'][:,:,:3].max(axis=2)>8 + assert np.abs(white[:,:,:3].astype(int)-last['frame'][:,:,:3].astype(int))[body].max() <= 1, \ + ('Body faded before completing its pass', width, height) + path.unlink() + for name, f in [('arrival', first), ('approach', frames[len(frames)//2]), ('pass', last)]: + Image.fromarray(f['frame'][:, :, :3]).save(root / f'{seed}-{width}x{height}-{name}.png') + results.append({'seed': seed, 'huge': huge, 'size': [width, height], 'first_offset': first_offset, 'last_offset': last_offset, + 'visible_travel': visible_travel, 'seconds_at_speed_2': visible_travel/3.6, + 'exit_touches_edge': last['touches_edge'], 'edge_seconds_at_speed_2': edge_travel/3.6, + 'close_body_opaque': True}) +(root / 'tracking.json').write_text(json.dumps(results, indent=2)) +print('PASS ordinary/huge central arrival, full opaque close pass, natural clipped exit and brief edge transit on wide/portrait outputs', flush=True) +print(json.dumps(results), flush=True) +print('ARTIFACTS', root, flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-native.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-native.py new file mode 100755 index 000000000000..536121baba35 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-native.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Private compositor: solid encounters, paused travel, Cube and partial damage.""" +from pathlib import Path +import sys,time +import numpy as np +from PIL import Image +stage=Path(sys.argv[1]).resolve();sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=''' +[core] +vwidth=4 +vheight=3 +[cube] +render_at_rest=true +mouse_overview=true +[ambient-background] +enabled=true +controller= +focused_delay=86400 +inactive_delay=86400 +hyperspace_speed=0 +hyperspace_seed=129 +hyperspace_cloud_density=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_dust_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_galaxies=0 +hyperspace_asteroids=1 +''' +with PrivateSession(stage,hotplug=True,extra_plugins=' ambient-background cube render-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + def call(method,data=None): + r=s.call(method,data);assert not isinstance(r,dict) or not r.get('error'),r;return r + def snapshot(name): + call('ambient/sync',{'outputs':[{'name':'HEADLESS-'+str(i),'opacity':0,'black':0,'effect':1} for i in range(1,5)]}) + time.sleep(.08);p=s.root/(name+'.png');s.run(['grim','-o','VIRTUAL-1',str(p)]) + return np.asarray(Image.open(p).convert('RGB')) + def settings(**options): + call('wayfire/set-config-options',{'ambient-background/'+k:v for k,v in options.items()});time.sleep(.12) + s.wait(lambda:len(call('ambient/status')['outputs'])==4) + call('stipc/move_cursor',{'x':1930,'y':10}) + first=snapshot('frozen-asteroids');assert first.max()>20 + time.sleep(.25);assert np.array_equal(first,snapshot('still-frozen')) + settings(hyperspace_asteroids=0);assert snapshot('disabled').max()==0 + settings(hyperspace_asteroids=1);assert np.array_equal(first,snapshot('restored')) + settings(hyperspace_speed=1.5);time.sleep(.4) + moved=snapshot('approaching');assert not np.array_equal(first,moved) + settings(hyperspace_speed=0) + frozen=snapshot('stopped');time.sleep(.25);assert np.array_equal(frozen,snapshot('still-stopped')) + assert call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + call('test/cube',{'output':'VIRTUAL-1','angle':.7,'zoom':.55});time.sleep(.25) + assert snapshot('cube-with-asteroids').max()>20 + assert call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + call('test/cube',{'output':'VIRTUAL-1','stop':True}) + log=(s.root/'wayfire.log').read_text(errors='replace') + assert 'GL_INVALID' not in log and 'GL_OUT_OF_MEMORY' not in log and 'Shader compilation failed' not in log + print('PASS native asteroid motion/freeze, density disable/restore, shared Cube rendering and clipped partial damage',flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-reload.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-reload.py new file mode 100755 index 000000000000..0c13c92e7bbf --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-reload.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Reload after a globally loaded old renderer; match a fresh candidate frame.""" +from pathlib import Path +import subprocess, sys, time +import numpy as np +from PIL import Image + +stage = Path(sys.argv[1]).resolve() +sys.path.insert(0, str(stage/'tests')) +from private_session import PrivateSession + +candidate = stage/'plugins/libambient-background.so' +old = stage/'plugins/old-r8/libambient-background.so' +exports = subprocess.check_output(['nm', '-D', '-C', str(candidate)], text=True) +assert 'ambient_asteroid_field' not in exports, 'Renderer internals are interposable' +config = ''' +[cube] +render_at_rest=true +[ambient-background] +enabled=true +controller= +focused_delay=86400 +inactive_delay=86400 +hyperspace_speed=0 +hyperspace_seed=129 +hyperspace_cloud_density=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_dust_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_galaxies=0 +hyperspace_asteroids=1 +''' + +def capture(s, name): + s.call('ambient/sync', {'outputs': [ + {'name': 'HEADLESS-'+str(i), 'opacity': 0, 'black': 0, 'effect': 1} + for i in range(1, 5)]}) + s.wait(lambda: all(z['opacity'] == 0 for z in s.call('ambient/status')['outputs'])) + s.wait(lambda: bool((s.call('ambient/status').get('scenes') or {}).get('VIRTUAL-1'))) + time.sleep(.08) + p = s.root/(name+'.png') + s.run(['grim', '-o', 'VIRTUAL-1', str(p)]) + return np.asarray(Image.open(p).convert('RGB')) + +def checked(s, method, data=None): + r = s.call(method, data) + assert not isinstance(r, dict) or not r.get('error'), r + return r + +with PrivateSession(stage, hotplug=True, extra_plugins=' ambient-background cube render-test', + config_extra=config) as s: + print('FRESH', s.root, flush=True) + checked(s, 'stipc/move_cursor', {'x': 1930, 'y': 10}) + expected = capture(s, 'fresh-renderer') + assert expected.max() > 20 and expected.min() == 0 + checked(s, 'test/cube', {'output': 'VIRTUAL-1', 'angle': .7, 'zoom': .55}) + expected_cube = capture(s, 'fresh-cube') + +with PrivateSession(stage, hotplug=True, extra_plugins=f' {old} cube render-test', + config_extra=config) as s: + print('RELOADED', s.root, flush=True) + checked(s, 'stipc/move_cursor', {'x': 1930, 'y': 10}) + previous = capture(s, 'old-renderer') + assert not np.array_equal(previous, expected), 'Fixture does not distinguish old/new rendering' + plugins = checked(s, 'wayfire/get-config-option', {'option': 'core/plugins'})['value'] + checked(s, 'wayfire/set-config-options', {'core/plugins': plugins.replace(str(old), str(candidate))}) + checked(s, 'wayfire/reload-plugins') + actual = capture(s, 'reloaded-renderer') + assert np.array_equal(actual, expected), 'Reload still renders using the resident old version' + checked(s, 'test/cube', {'output': 'VIRTUAL-1', 'angle': .7, 'zoom': .55}) + actual_cube = capture(s, 'reloaded-cube') + assert np.array_equal(actual_cube, expected_cube), 'Cube reload uses a different renderer' + checked(s, 'test/cube', {'output': 'VIRTUAL-1', 'stop': True}) + checked(s, 'wayfire/set-config-options', {'ambient-background/hyperspace_speed': 2.0}) + time.sleep(.15) + moved = capture(s, 'fast-pass') + assert not np.array_equal(moved, actual), 'Reloaded motion is frozen' + log = (s.root/'wayfire.log').read_text(errors='replace') + assert 'GL_INVALID' not in log and 'Shader compilation failed' not in log +print('PASS old-to-new native reload matches fresh geometry/material/trajectory, including Cube delegation', flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-render.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-render.cpp new file mode 100644 index 000000000000..4f12333b8360 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-render.cpp @@ -0,0 +1,54 @@ +// Private, deterministic EGL fixture for the shared compositor/lock renderer. +#include "hyperspace-background.hpp" +#include "hyperspace-lock.h" +#include <EGL/egl.h> +#include <cstdio> +#include <cstdlib> +#include <vector> +static void check(bool ok,const char *what) { + if (!ok) { fprintf(stderr,"%s EGL=%x GL=%x\n",what,eglGetError(),glGetError());std::exit(1); } +} +int main(int argc,char **argv) { + check(argc>=8,"args output seed distance width height density quality [stars]"); + int seed=atoi(argv[2]),width=atoi(argv[4]),height=atoi(argv[5]),quality=atoi(argv[7]); + double distance=atof(argv[3]),density=atof(argv[6]); + check(eglBindAPI(EGL_OPENGL_ES_API),"bind API"); + EGLDisplay display=eglGetDisplay(EGL_DEFAULT_DISPLAY);check(eglInitialize(display,nullptr,nullptr),"display"); + const EGLint attrs[]={EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE,8,EGL_GREEN_SIZE,8,EGL_BLUE_SIZE,8,EGL_ALPHA_SIZE,8,EGL_NONE}; + EGLConfig config;EGLint count;check(eglChooseConfig(display,attrs,&config,1,&count)&&count,"config"); + const EGLint size[]={EGL_WIDTH,width,EGL_HEIGHT,height,EGL_NONE}; + EGLSurface surface=eglCreatePbufferSurface(display,config,size); + const EGLint ctxattrs[]={EGL_CONTEXT_CLIENT_VERSION,2,EGL_NONE}; + EGLContext context=eglCreateContext(display,config,EGL_NO_CONTEXT,ctxattrs); + check(eglMakeCurrent(display,surface,surface,context),"current"); + json_object *settings=json_object_new_object(); + json_object_object_add(settings,"hyperspace_seed",json_object_new_int(seed)); + json_object_object_add(settings,"hyperspace_quality",json_object_new_int(quality)); + json_object_object_add(settings,"hyperspace_asteroids",json_object_new_double(density)); + json_object_object_add(settings,"hyperspace_brightness",json_object_new_double(1)); + json_object_object_add(settings,"hyperspace_distant_stars",json_object_new_double(argc>8 ? atof(argv[8]) : 0)); + ambient_lock_host::parameters=settings; + OpenGL::program_t texture; + ambient_lock_host::texture_program=&texture; + { + ambient_hyperspace_background engine([]{}); + std::array<double,24> state{double(seed),std::floor(distance/12),0,std::fmod(distance,12),0,0, + 1,-3,15,.5,.3,0,.2,.1,.65,1,-15,18,.4,.4,.08,0,.3,0}; + check(engine.restore_scene(state),"restore scene"); + wf::auxilliary_buffer_t destination;destination.width=width;destination.height=height; + wf::render_target_t target(destination);target.geometry={0,0,double(width),double(height)}; + float background=argc>9 ? atof(argv[9]) : 0; + glClearColor(background,background,background,1);glClear(GL_COLOR_BUFFER_BIT); + engine.render(target,target.geometry,{width,height}); + std::vector<unsigned char> pixels(width*height*4); + glReadPixels(0,0,width,height,GL_RGBA,GL_UNSIGNED_BYTE,pixels.data()); + check(glGetError()==GL_NO_ERROR,"render"); + FILE *file=fopen(argv[1],"wb");check(file,"output");fwrite(pixels.data(),pixels.size(),1,file);fclose(file); + fprintf(stderr,"GPU %s; %dx%d\n",glGetString(GL_RENDERER),width,height); + } + json_object_put(settings); + texture.free_resources(); + eglMakeCurrent(display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT); + eglDestroySurface(display,surface);eglDestroyContext(display,context);eglTerminate(display); +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-sizes.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-sizes.py new file mode 100755 index 000000000000..f2a5f805014f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroid-sizes.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""Compare actual GPU silhouettes at the same apparent pass position.""" +from pathlib import Path +import argparse, json, os, subprocess, tempfile +import numpy as np +from PIL import Image + +p = argparse.ArgumentParser() +p.add_argument('--renderer', type=Path, required=True) +p.add_argument('--before', type=Path) +a = p.parse_args() +assert os.geteuid() != 0 +root = Path(tempfile.mkdtemp(prefix='ambient-asteroid-sizes-')) +env = {'PATH': '/usr/bin:/bin', 'EGL_PLATFORM': 'surfaceless', 'HOME': str(root)} +width, height = 1920, 1080 +# Known isolated encounters; arrival times are independent of body size. +encounters = [ + ('fragment', 3, 49.632767797), ('fragment', 4, 77.714514494), ('fragment', 6, 40.783897400), + ('rock', 2, 48.280719876), ('rock', 8, 50.549680948), ('rock', 9, 19.262689114), + ('boulder', 13, 6.325440168), ('boulder', 15, 4.058984280), ('boulder', 30, 9.716809154), + ('huge', 1, 89.392536044), ('huge', 14, 35.469819069), ('huge', 85, 74.757486343)] + +def measure(renderer, seed, clock): + path = root/'frame.rgba' + subprocess.run([str(renderer), str(path), str(seed), str(clock), str(width), str(height), + str(1/64), '2'], env=env, check=True, capture_output=True) + frame = np.frombuffer(path.read_bytes(), dtype=np.uint8).reshape(height, width, 4)[::-1].copy() + path.unlink() + y, x = np.nonzero(frame[:, :, :3].max(axis=2) > 8) + if not len(x): + return None + offset = float(np.hypot((x.mean()-width/2)*2/height, (y.mean()-height/2)*2/height)) + return {'offset': offset, 'diameter': int(max(np.ptp(x), np.ptp(y))+1), + 'area': int(len(x)), 'frame': frame} + +def sample(renderer, label): + results = [] + for category, seed, arrival in encounters: + lo, hi = arrival-115/64, arrival + # At the desired screen position, every body is well inside the frame. + # Empty early samples can be subpixel fragments; empty late samples + # have already completed their pass. + for _ in range(16): + clock = (lo+hi)/2 + m = measure(renderer, seed, clock) + if m is None: + if arrival-clock > .5: + lo = clock + else: + hi = clock + elif m['offset'] < .30: + lo = clock + else: + hi = clock + m = measure(renderer, seed, (lo+hi)/2) + assert m is not None and abs(m['offset']-.30) < .015, (label, seed, m) + Image.fromarray(m.pop('frame')[:,:,:3]).save(root/f'{label}-{category}-{seed}.png') + results.append({'category': category, 'seed': seed, **m}) + return results + +after = sample(a.renderer, 'after') +span = max(v['diameter'] for v in after)/min(v['diameter'] for v in after) +medians = {category: float(np.median([v['diameter'] for v in after if v['category']==category])) + for category in ['fragment', 'rock', 'boulder', 'huge']} +assert span >= 20, ('Insufficient visible size spread', span) +assert medians['rock'] > medians['fragment']*2, medians +assert medians['boulder'] > medians['fragment']*8, medians +assert medians['huge'] > medians['boulder']*1.3, medians +report = {'after': after, 'diameter_span': span, 'median_diameters': medians} +if a.before: + before = sample(a.before, 'before') + old_span = max(v['diameter'] for v in before)/min(v['diameter'] for v in before) + assert span > old_span*3, ('Flight clearance still cancels the size spread', old_span, span) + report.update(before=before, previous_diameter_span=old_span) +(root/'sizes.json').write_text(json.dumps(report, indent=2)) +print('PASS actual silhouette sizes at matched screen position, including rare huge bodies', flush=True) +print(json.dumps(report), flush=True) +print('ARTIFACTS', root, flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroids.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroids.py new file mode 100755 index 000000000000..0114780b81fe --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/asteroids.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Solid asteroid coverage, repeatability, world-clock seams and field gaps.""" +from pathlib import Path +import argparse,os,subprocess,tempfile +import numpy as np +from PIL import Image +p=argparse.ArgumentParser();p.add_argument('--renderer',type=Path,required=True) +a=p.parse_args();assert os.geteuid()!=0 +root=Path(tempfile.mkdtemp(prefix='ambient-asteroid-geometry-')) +env={'PATH':'/usr/bin:/bin','EGL_PLATFORM':'surfaceless','HOME':str(root)} +def render(name,seed=31415,distance=48,density=.65,quality=2,width=960,height=540,stars=0,background=0): + path=root/(name+'.rgba') + subprocess.run([str(a.renderer),str(path),str(seed),str(distance),str(width),str(height), + str(density),str(quality),str(stars),str(background)],env=env,check=True,capture_output=True) + frame=np.frombuffer(path.read_bytes(),dtype=np.uint8).reshape(height,width,4).copy()[::-1] + Image.fromarray(frame[:,:,:3]).save(root/(name+'.png'));return frame +first=render('active-field') +assert first[:,:,:3].max()>25 and (first[:,:,:3].max(axis=2)>8).sum()>100 +assert np.array_equal(first,render('repeatable')) +assert not np.array_equal(first,render('other-seed',seed=27182)) +assert not render('disabled',density=0)[:,:,:3].any() +denser=render('denser',seed=85,distance=74.0,density=1.3) +sparse=render('sparser-default',seed=85,distance=74.0,density=.35) +assert 0 < (sparse[:,:,:3].max(axis=2)>8).sum() < (denser[:,:,:3].max(axis=2)>8).sum() +for quality in [0,1,2]:assert render('quality-'+str(quality),quality=quality)[:,:,:3].max()>20 +for distance in [4,8,12,24,48,72,110,150,180,220,256]:render('rocks-'+str(distance),distance=distance) +quiet=render('quiet-space',distance=112) +assert (quiet[:,:,:3].max(axis=2)>8).sum() < (first[:,:,:3].max(axis=2)>8).sum() +assert not render('long-open-stretch',distance=180)[:,:,:3].any() +for distance in [12,256,384,49152]: + before=render('before-'+str(distance),distance=distance-.001) + after=render('after-'+str(distance),distance=0 if distance==49152 else distance) + assert np.quantile(np.abs(before.astype(int)-after.astype(int)),.999)<4,('Clock wrap popped',distance) +wide=render('panorama',width=2880,height=540,density=1) +assert (wide[:,960:1920,:3].max(axis=2)>8).sum()>10 +assert (wide[:,:,:3].max(axis=2)>8).sum()>100 +stars=render('rocks-stars',stars=2);empty=render('stars-only',density=0,stars=2) +body=first[:,:,:3].max(axis=2)>8 +opaque=render('opaque-silhouettes',background=1) +assert (opaque[:,:,:3].max(axis=2)[body]<150).sum()>100, 'Rocks do not occlude background light' +assert (empty[:,:,:3].max(axis=2)>80).any() +assert np.abs(stars[:,:,:3].astype(int)-empty[:,:,:3].astype(int))[body].max()>15 +print('PASS solid seeded shapes/materials, disabled/density/quality controls, field gaps, panoramic coverage and seamless bounded travel',flush=True) +print('ARTIFACTS',root,flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/backgrounds.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/backgrounds.py new file mode 100755 index 000000000000..a4e9f758ea7f --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/backgrounds.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Check independent backdrop choices on real GPU output with synthetic wallpaper.""" +from pathlib import Path +import json, sys, time +from PIL import Image +stage=Path(sys.argv[1]).resolve();sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=''' +[core] +vwidth=4 +vheight=3 +[cube] +render_at_rest=true +[ambient-background] +enabled=true +controller= +backend=hyperspace +focused_delay=86400 +inactive_delay=86400 +hyperspace_speed=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_cloud_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_dust_density=0 +''' +with PrivateSession(stage,hotplug=True,extra_plugins=' cube ambient-background digitalrain-cursor digitalrain-idle digitalrain-sampler render-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + def call(method,data=None): + result=s.call(method,data);assert not isinstance(result,dict) or not result.get('error'),result + return result + cfg=s.root/'wayfire.ini';original=cfg.read_text() + choices={'backend':'hyperspace','hyperspace_background':'none','matrix_background':'none', + 'hyperspace_background_opacity':'1','matrix_background_opacity':'1'} + def settings(**values): + choices.update({k:str(v) for k,v in values.items()}) + cfg.write_text(original+'\n[ambient-background]\n'+''.join(k+'='+v+'\n' for k,v in choices.items())) + s.wait(lambda:all(call('wayfire/get-config-option',{'option':'ambient-background/'+k})['value']==v + or (k.endswith('_opacity') and float(call('wayfire/get-config-option',{'option':'ambient-background/'+k})['value'])==float(v)) for k,v in choices.items())) + time.sleep(.2) + def snapshot(name): + p=s.root/(name+'.png');s.run(['grim','-o','VIRTUAL-1',str(p)]) + image=Image.open(p).convert('RGB');assert image.getpixel((10,5))==(0,255,255),'Panel changed' + return image + s.spawn('wallpaper',['python3',str(stage/'tests/ambient-layer.py'),'wallpaper']) + s.spawn('panel',['python3',str(stage/'tests/ambient-layer.py')]) + s.wait(lambda:any(v.get('app-id')=='ambient-fixture-wallpaper' for v in call('window-rules/list-views'))) + time.sleep(.3) + assert snapshot('hyper-none').getpixel((100,100))==(0,0,0) + settings(hyperspace_background='wallpaper') + assert snapshot('hyper-wallpaper').getpixel((100,100))==(48,64,128) + settings(hyperspace_background_opacity=.5) + assert snapshot('hyper-half').getpixel((100,100))==(24,32,64) + settings(hyperspace_background='none',hyperspace_background_opacity=1) + assert snapshot('hyper-none-restored').getpixel((100,100))==(0,0,0) + print('PASS Hyperspace None, wallpaper, half opacity and immediate restoration',flush=True) + drcfg=s.root/'ambient-background.toml';drcfg.write_text('''schema_version=1 +[global] +TIMEOUT=86400 +FOCUSED_TIMEOUT=86400 +LOCK_TIMEOUT=0 +LOG_FILE="stderr" +[matrix] +TRANSPARENT_TIMEOUT=600 +BLACK_RAIN_TIMEOUT=600 +''') + s.env['AMBIENT_BACKGROUND_CONFIG']=str(drcfg) + s.spawn('idle-manager',[str(stage/'digitalrain')]) + s.wait(lambda:(s.root/'digitalrain/control.sock').exists()) + s.spawn('controller',[str(stage/'ambient-background'),'--daemon','--renderer',str(stage/'digitalrain')]) + settings(backend='matrix') + s.wait(lambda:sum(v.get('app-id')=='ambient-matrix' for v in call('window-rules/list-views'))==4) + time.sleep(.5) + def wallpaper_samples(image): + return sum(image.getpixel((x,y))[2]>image.getpixel((x,y))[1]+20 + for y in range(50,700,13) for x in range(20,1900,17)) + assert wallpaper_samples(snapshot('matrix-none'))==0,'Matrix retained wallpaper' + settings(matrix_background='wallpaper') + assert wallpaper_samples(snapshot('matrix-wallpaper'))>3000,'Matrix omitted selected wallpaper' + settings(matrix_background_opacity=.5) + image=snapshot('matrix-half') + assert sum(image.getpixel((x,100))==(24,32,64) for x in range(20,1900,17))>30 + settings(matrix_background='none',matrix_background_opacity=1) + assert wallpaper_samples(snapshot('matrix-none-restored'))==0 + call('test/cube',{'output':'VIRTUAL-1','angle':.65,'zoom':.55});time.sleep(.3) + assert snapshot('cube-matrix-none').getpixel((80,80))[2]<=snapshot('cube-matrix-none-2').getpixel((80,80))[1]+20 + assert call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + call('test/cube',{'output':'VIRTUAL-1','stop':True}) + assert choices['hyperspace_background']=='none' + print('PASS independent Matrix backdrop, opacity, Cube delegation and partial damage',flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/cloud-depth.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/cloud-depth.py new file mode 100755 index 000000000000..72764f029193 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/cloud-depth.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Controlled GPU images: cloud silhouettes travel radially, without a fog sheet.""" +from pathlib import Path +import os, shlex, subprocess, sys, tempfile +import numpy as np +from PIL import Image +from scipy.ndimage import uniform_filter +from scipy.signal import correlate2d +assert os.geteuid() != 0 +stage = Path(sys.argv[1]).resolve() +root = Path(tempfile.mkdtemp(prefix='ambient-cloud-depth-')) +shader = Path(os.environ.get('CLOUD_SHADER', str(stage/'effects/hyperspace/hyperspace-shaders.tpp'))) +subprocess.run(['c++','-O2','-std=c++17',f'-DSHADER_PATH="{shader}"', + f'-DANOMALY_PATH="{stage}/effects/hyperspace/hyperspace-anomalies.tpp"', + str(stage/'tests/cloud-shader.cpp'),'-o',str(root/'renderer'), + *shlex.split(subprocess.check_output(['pkg-config','--cflags','--libs','egl','glesv2'],text=True))],check=True) +env={'PATH':'/usr/bin:/bin','EGL_PLATFORM':'surfaceless','HOME':str(root)} +def render(name,phase=0,epoch=0,seed=31415,detail=2,density=.85,kind=None,age=8,colours=.75): + args=[str(root/'renderer'),str(root/(name+'.rgba')),str(phase),str(epoch),str(seed),'960','540',str(detail),str(density)] + args += [str(kind if kind is not None else -1),str(age),str(colours)] + subprocess.run(args,env=env,check=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL) + rgba=np.frombuffer((root/(name+'.rgba')).read_bytes(),dtype=np.uint8).reshape(540,960,4).copy() + Image.fromarray(rgba[:,:,:3][::-1]).save(root/(name+'.png')) + return rgba +first=render('clouds-start');next_frame=render('clouds-next',phase=1) +coverage=(first[:,:,:3].max(axis=2)>3).mean() +assert .05 < coverage < .55, ('Clouds became an all-screen fog sheet',coverage) +assert not render('clouds-disabled',density=0).any(), 'Cloud density zero left a fixed central glow' +assert np.array_equal(first,render('repeatable')), 'Seeded cloud field changed without travel' +assert not np.array_equal(first,render('other-seed',seed=27182)), 'Journey seed ignored' +# Track texture patches externally. A forward journey moves persistent +# silhouettes away from the vanishing point; boiling noise has no such flow. +a=first[:,:,:3].astype(float).mean(axis=2);b=next_frame[:,:,:3].astype(float).mean(axis=2) +flow=[] +for y in range(40,500,25): + for x in range(40,920,25): + patch=a[y-6:y+7,x-6:x+7];std=patch.std() + if patch.mean()<4 or std<1.5:continue + area=b[y-26:y+27,x-26:x+27] + means=uniform_filter(area,size=13)[6:-6,6:-6] + sigma=np.sqrt(np.maximum(1e-5,uniform_filter(area*area,size=13)[6:-6,6:-6]-means*means)) + corr=correlate2d(area,patch-patch.mean(),mode='valid')/(169*std*sigma) + yy,xx=np.unravel_index(np.argmax(corr),corr.shape);dy=yy-20;dx=xx-20 + distance=np.hypot(x-480,y-270) + if corr[yy,xx]<.9 or abs(dx)>=20 or abs(dy)>=20 or distance<60:continue + radial=((x-480)*dx+(y-270)*dy)/distance + lateral=abs((x-480)*dy-(y-270)*dx)/distance + flow.append((radial,lateral)) +flow=np.array(flow) +assert len(flow)>35, ('Cloud silhouettes lost coherent structure',len(flow)) +assert (flow[:,0]>0).mean()>.9, 'Cloud motion does not approach the camera' +assert np.median(flow[:,0])>2 and np.median(flow[:,1])<1.5, 'Clouds boil or slide sideways instead of travelling' +tinted=render('chosen-tint',colours=0);palette=render('full-palette',colours=1) +assert np.array_equal(tinted[:,:,3],palette[:,:,3]), 'Colour variation changed cloud absorption' +assert np.abs(tinted[:,:,:3].astype(int)-palette[:,:,:3].astype(int)).max()>10, 'Cloud palette has no visible effect' +bright=palette[:,:,:3].max(axis=2)>8 +cold=(palette[:,:,2].astype(int)>palette[:,:,0].astype(int)+3)&bright +warm=(palette[:,:,0].astype(int)>palette[:,:,2].astype(int)+3)&bright +assert cold.sum()>100 and warm.sum()>100, ('No varied warm/cool bank colours',int(cold.sum()),int(warm.sum())) +print('PASS seeded cloud colours, global-tint mode, warm/cool variation and unchanged absorption',flush=True) +print('PASS open space and coherent perspective travel;',len(flow),'tracked patches; radial',round(float(np.median(flow[:,0])),2),'pixels',flush=True) +for epoch in [0,4095]: + before=render('before-wrap-'+str(epoch),phase=63.999,epoch=epoch) + after=render('after-wrap-'+str(epoch),phase=0,epoch=(epoch+1)%4096) + assert np.quantile(np.abs(before.astype(int)-after.astype(int)),.999)<3, 'Cloud recycling popped at clock wrap' +for detail in [0,1,2]: + assert render('quality-'+str(detail),detail=detail).max()>10 +binary=render('binary-stars',kind=0);remnant=render('broken-remnant',kind=1) +assert (binary[:,:,:3].max(axis=2)>10).sum()>25 +assert (remnant[:,:,:3].max(axis=2)>3).sum()>400 +assert not np.array_equal(binary,remnant), 'Encounter kinds rendered identically' +assert not np.array_equal(binary,render('binary-next',kind=0,age=12)), 'Binary orbit is frozen' +print('PASS seam-free recycling, bounded clock, quality levels and distinct encounter shaders',flush=True) +print('ARTIFACTS',root,flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/cloud-shader.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/cloud-shader.cpp new file mode 100644 index 000000000000..ddd6b653266a --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/cloud-shader.cpp @@ -0,0 +1,48 @@ +#include <EGL/egl.h> +#include <GLES2/gl2.h> +#include <cstdio> +#include <cstdlib> +#include <cstdint> +#include <vector> +#include <string> +#include SHADER_PATH +#include ANOMALY_PATH +void check(bool ok,const char *what) {if(!ok){fprintf(stderr,"%s EGL=%x GL=%x\n",what,eglGetError(),glGetError());exit(1);}} +GLuint shader(GLenum type,const char *source) { + GLuint id=glCreateShader(type);glShaderSource(id,1,&source,nullptr);glCompileShader(id); + GLint compiled;glGetShaderiv(id,GL_COMPILE_STATUS,&compiled); + if(!compiled){char log[8192];glGetShaderInfoLog(id,sizeof(log),nullptr,log);fprintf(stderr,"%s\n",log);}check(compiled,"compile");return id; +} +float random(uint32_t v){v^=v>>16;v*=0x7feb352du;v^=v>>15;v*=0x846ca68bu;v^=v>>16;return(v&0xffffffu)/16777216.f;} +int main(int argc,char **argv) { + check(argc>=7,"args output phase epoch seed width height [detail density]"); + int width=atoi(argv[5]),height=atoi(argv[6]),seed=atoi(argv[4]); + check(eglBindAPI(EGL_OPENGL_ES_API),"bind API"); + EGLDisplay display=eglGetDisplay(EGL_DEFAULT_DISPLAY);check(eglInitialize(display,nullptr,nullptr),"display"); + const EGLint attrs[]={EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,EGL_RED_SIZE,8,EGL_GREEN_SIZE,8,EGL_BLUE_SIZE,8,EGL_ALPHA_SIZE,8,EGL_NONE}; + EGLConfig config;EGLint count;check(eglChooseConfig(display,attrs,&config,1,&count)&&count,"config"); + const EGLint size[]={EGL_WIDTH,width,EGL_HEIGHT,height,EGL_NONE};EGLSurface surface=eglCreatePbufferSurface(display,config,size); + const EGLint ctxattrs[]={EGL_CONTEXT_CLIENT_VERSION,2,EGL_NONE};EGLContext context=eglCreateContext(display,config,EGL_NO_CONTEXT,ctxattrs); + check(eglMakeCurrent(display,surface,surface,context),"current"); + GLuint program=glCreateProgram();glAttachShader(program,shader(GL_VERTEX_SHADER,hyperspace_vertex));glAttachShader(program,shader(GL_FRAGMENT_SHADER,(argc>9 && atoi(argv[9])>=0)?hyperspace_anomaly_fragment:hyperspace_cloud_fragment));glLinkProgram(program); + GLint linked;glGetProgramiv(program,GL_LINK_STATUS,&linked);check(linked,"link");glUseProgram(program); + auto uniform=[&](const char *n,float f){glUniform1f(glGetUniformLocation(program,n),f);}; + glUniform2f(glGetUniformLocation(program,"resolution"),width,height); + const GLfloat mvp[]={2.f/width,0,0,0,0,2.f/height,0,0,0,0,1,0,-1,-1,0,1}; + glUniformMatrix4fv(glGetUniformLocation(program,"mvp"),1,GL_FALSE,mvp); + glUniform3f(glGetUniformLocation(program,"tint"),.2,.5215686,1); + glUniform3f(glGetUniformLocation(program,"scene_offset"),64*random(seed),64*random(seed+1),64*random(seed+2)); + glUniform1i(glGetUniformLocation(program,"detail"),argc>7?atoi(argv[7]):2); + uniform("colour_variation",argc>11?atof(argv[11]):.75); + uniform("phase",atof(argv[2]));uniform("epoch",atof(argv[3]));uniform("cloud_density",argc>8?atof(argv[8]):.85); + uniform("turbulence",.65);uniform("variation",1.15);uniform("ribbons",.75);uniform("glow",.65);uniform("brightness",.85);uniform("energy",1); + glUniform2f(glGetUniformLocation(program,"centre"),width*.42,height*.44); + uniform("size",.1);uniform("angle",.4);uniform("shape",.35);uniform("age",argc>10?atof(argv[10]):8);uniform("strength",1); + glUniform1i(glGetUniformLocation(program,"kind"),argc>9?atoi(argv[9]):0); + const GLfloat quad[]={0,0,(float)width,0,(float)width,(float)height,0,0,(float)width,(float)height,0,(float)height}; + GLuint attr=glGetAttribLocation(program,"position");glEnableVertexAttribArray(attr);glVertexAttribPointer(attr,2,GL_FLOAT,GL_FALSE,0,quad); + glViewport(0,0,width,height);glDrawArrays(GL_TRIANGLES,0,6);std::vector<unsigned char> pixels(width*height*4);glReadPixels(0,0,width,height,GL_RGBA,GL_UNSIGNED_BYTE,pixels.data());check(glGetError()==GL_NO_ERROR,"render"); + FILE *file=fopen(argv[1],"wb");check(file,"output");fwrite(pixels.data(),pixels.size(),1,file);fclose(file); + fprintf(stderr,"GPU %s; %dx%d\n",glGetString(GL_RENDERER),width,height); + eglMakeCurrent(display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT);eglDestroySurface(display,surface);eglDestroyContext(display,context);eglTerminate(display);return 0; +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxies-native.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxies-native.py new file mode 100755 index 000000000000..13cbe2f72b1c --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxies-native.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +"""Private compositor: solid encounters, paused travel, Cube and partial damage.""" +from pathlib import Path +import sys,time +import numpy as np +from PIL import Image +stage=Path(sys.argv[1]).resolve();sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=''' +[core] +vwidth=4 +vheight=3 +[cube] +render_at_rest=true +mouse_overview=true +[ambient-background] +enabled=true +controller= +focused_delay=86400 +inactive_delay=86400 +hyperspace_speed=0 +hyperspace_seed=129 +hyperspace_cloud_density=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_dust_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_galaxies=1.5 +hyperspace_distant_drift=1 +hyperspace_asteroids=0 +''' +with PrivateSession(stage,hotplug=True,extra_plugins=' ambient-background cube render-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + def call(method,data=None): + r=s.call(method,data);assert not isinstance(r,dict) or not r.get('error'),r;return r + def snapshot(name): + call('ambient/sync',{'outputs':[{'name':'HEADLESS-'+str(i),'opacity':0,'black':0,'effect':1} for i in range(1,5)]}) + time.sleep(.08);p=s.root/(name+'.png');s.run(['grim','-o','VIRTUAL-1',str(p)]) + return np.asarray(Image.open(p).convert('RGB')) + def settings(**options): + call('wayfire/set-config-options',{'ambient-background/'+k:v for k,v in options.items()});time.sleep(.12) + s.wait(lambda:len(call('ambient/status')['outputs'])==4) + call('stipc/move_cursor',{'x':1930,'y':10}) + first=snapshot('frozen-galaxies');assert first.max()>20 + time.sleep(.25);assert np.array_equal(first,snapshot('still-frozen')) + settings(hyperspace_galaxies=0);assert snapshot('disabled').max()==0 + settings(hyperspace_galaxies=1.5);assert np.array_equal(first,snapshot('restored')) + settings(hyperspace_speed=1.5);time.sleep(.4) + moved=snapshot('approaching');assert not np.array_equal(first,moved) + settings(hyperspace_speed=0) + frozen=snapshot('stopped');time.sleep(.25);assert np.array_equal(frozen,snapshot('still-stopped')) + assert call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + call('test/cube',{'output':'VIRTUAL-1','angle':.7,'zoom':.55});time.sleep(.25) + assert snapshot('cube-with-galaxies').max()>20 + assert call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + call('test/cube',{'output':'VIRTUAL-1','stop':True}) + log=(s.root/'wayfire.log').read_text(errors='replace') + assert 'GL_INVALID' not in log and 'GL_OUT_OF_MEMORY' not in log and 'Shader compilation failed' not in log + print('PASS native galaxy motion/freeze, density disable/restore, shared Cube rendering and clipped partial damage',flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxies-reload.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxies-reload.py new file mode 100755 index 000000000000..45dded8e0e4d --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxies-reload.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""Reload after a globally loaded old renderer; match a fresh candidate frame.""" +from pathlib import Path +import subprocess, sys, time +import numpy as np +from PIL import Image + +stage = Path(sys.argv[1]).resolve() +sys.path.insert(0, str(stage/'tests')) +from private_session import PrivateSession + +candidate = stage/'plugins/libambient-background.so' +old = stage/'plugins/old-r14/libambient-background.so' +exports = subprocess.check_output(['nm', '-D', '-C', str(candidate)], text=True) +assert 'ambient_galaxy_field' not in exports, 'Renderer internals are interposable' +config = ''' +[cube] +render_at_rest=true +[ambient-background] +enabled=true +controller= +focused_delay=86400 +inactive_delay=86400 +hyperspace_speed=0 +hyperspace_seed=129 +hyperspace_cloud_density=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_dust_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_galaxies=1.5 +hyperspace_distant_drift=1 +hyperspace_asteroids=0 +''' + +def capture(s, name): + s.call('ambient/sync', {'outputs': [ + {'name': 'HEADLESS-'+str(i), 'opacity': 0, 'black': 0, 'effect': 1} + for i in range(1, 5)]}) + s.wait(lambda: all(z['opacity'] == 0 for z in s.call('ambient/status')['outputs'])) + s.wait(lambda: bool((s.call('ambient/status').get('scenes') or {}).get('VIRTUAL-1'))) + time.sleep(.08) + p = s.root/(name+'.png') + s.run(['grim', '-o', 'VIRTUAL-1', str(p)]) + return np.asarray(Image.open(p).convert('RGB')) + +def checked(s, method, data=None): + r = s.call(method, data) + assert not isinstance(r, dict) or not r.get('error'), r + return r + +with PrivateSession(stage, hotplug=True, extra_plugins=' ambient-background cube render-test', + config_extra=config) as s: + print('FRESH', s.root, flush=True) + checked(s, 'stipc/move_cursor', {'x': 1930, 'y': 10}) + expected = capture(s, 'fresh-renderer') + assert expected.max() > 20 and expected.min() == 0 + checked(s, 'test/cube', {'output': 'VIRTUAL-1', 'angle': .7, 'zoom': .55}) + expected_cube = capture(s, 'fresh-cube') + +with PrivateSession(stage, hotplug=True, extra_plugins=f' {old} cube render-test', + config_extra=config) as s: + print('RELOADED', s.root, flush=True) + checked(s, 'stipc/move_cursor', {'x': 1930, 'y': 10}) + previous = capture(s, 'old-renderer') + assert not np.array_equal(previous, expected), 'Fixture does not distinguish old/new rendering' + plugins = checked(s, 'wayfire/get-config-option', {'option': 'core/plugins'})['value'] + checked(s, 'wayfire/set-config-options', {'core/plugins': plugins.replace(str(old), str(candidate))}) + checked(s, 'wayfire/reload-plugins') + actual = capture(s, 'reloaded-renderer') + assert np.array_equal(actual, expected), 'Reload still renders using the resident old version' + checked(s, 'test/cube', {'output': 'VIRTUAL-1', 'angle': .7, 'zoom': .55}) + actual_cube = capture(s, 'reloaded-cube') + assert np.array_equal(actual_cube, expected_cube), 'Cube reload uses a different renderer' + checked(s, 'test/cube', {'output': 'VIRTUAL-1', 'stop': True}) + checked(s, 'wayfire/set-config-options', {'ambient-background/hyperspace_speed': 2.0}) + time.sleep(.15) + moved = capture(s, 'fast-pass') + assert not np.array_equal(moved, actual), 'Reloaded motion is frozen' + log = (s.root/'wayfire.log').read_text(errors='replace') + assert 'GL_INVALID' not in log and 'Shader compilation failed' not in log +print('PASS old-to-new native reload matches fresh deep galaxy rendering, including Cube delegation', flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxies.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxies.py new file mode 100755 index 000000000000..9ff2f1a30934 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxies.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Actual shared GPU renderer: deep galaxies, coherent drift and bounded wrap.""" +from pathlib import Path +import argparse, json, os, subprocess, tempfile +import numpy as np +from PIL import Image +from scipy.ndimage import label, center_of_mass + +p=argparse.ArgumentParser();p.add_argument('--renderer',type=Path,required=True) +a=p.parse_args();assert os.geteuid()!=0 +root=Path(tempfile.mkdtemp(prefix='ambient-galaxies-')) +env={'PATH':'/usr/bin:/bin','EGL_PLATFORM':'surfaceless','HOME':str(root)} +def render(name,seed=31415,drift=0,density=1.5,quality=2,width=1920,height=1080,brightness=1,opacity=1): + path=root/(name+'.rgba') + subprocess.run([str(a.renderer),str(path),str(seed),str(drift),str(width),str(height), + str(density),str(quality),str(brightness),str(opacity)],env=env,check=True,capture_output=True) + frame=np.frombuffer(path.read_bytes(),dtype=np.uint8).reshape(height,width,4)[::-1].copy() + path.unlink();Image.fromarray(frame[:,:,:3]).save(root/(name+'.png')) + return frame[:,:,:3] + +first=render('deep-field');assert first.max()>25 and (first.max(axis=2)>8).sum()>200 +assert np.array_equal(first,render('repeatable')) +assert not np.array_equal(first,render('other-seed',seed=27182)) +assert not render('disabled',density=0).any() +assert not render('brightness-zero',brightness=0).any() +half=render('half-opacity',opacity=.5) +assert np.abs(half.astype(float)-first/2).max()<=1.5,'Galaxies bypass shared effect opacity' +assert (render('sparse',density=.7).max(axis=2)>8).sum()<(first.max(axis=2)>8).sum() + +# Track emitted silhouettes rather than duplicating the renderer's equations. +def components(frame): + labels,count=label(frame.max(axis=2)>3) + sizes=np.bincount(labels.ravel()) + ids=[i for i in range(1,count+1) if sizes[i]>12] + return np.array(center_of_mass(frame.sum(axis=2),labels,ids))[:,::-1] +next_frame=render('slow-drift',drift=.04) +centres=components(first);later=components(next_frame) +assert len(centres)>3 and len(later)>3 +flows=[] +for centre in centres: + destination=later[np.argmin(np.linalg.norm(later-centre,axis=1))] + delta=destination-centre + radial=centre-np.array([960,540]);distance=np.linalg.norm(radial) + if distance<80 or np.linalg.norm(delta)>15:continue + flows.append((float(np.dot(delta,radial)/distance),float(np.linalg.norm(delta)))) +assert len(flows)>3 and sum(radial>0 for radial,_ in flows)/len(flows)>.8,flows +assert .05<np.median([distance for _,distance in flows])<8,flows +before=render('before-drift-wrap',drift=2*np.pi-.00001) +after=render('after-drift-wrap',drift=0) +assert np.abs(before.astype(int)-after.astype(int)).max()<4,'Deep field jumped at bounded clock wrap' +for quality in [0,1,2]:assert render('quality-'+str(quality),quality=quality).max()>20 +wide=render('panorama',width=2880,height=540,density=2) +for segment in np.split(wide,3,axis=1):assert (segment.max(axis=2)>5).sum()>10,'Galaxies concentrate in one monitor' +assert render('portrait',width=540,height=960,density=2).max()>15 +warm=(first[:,:,0].astype(int)>first[:,:,2].astype(int)+5) +cool=(first[:,:,2].astype(int)>first[:,:,0].astype(int)+3) +assert warm.sum()>25 and cool.sum()>25,'Missing warm cores or cool stellar discs' +report={'visible_pixels':int((first.max(axis=2)>8).sum()),'tracked_centres':len(flows), + 'median_motion_pixels':float(np.median([distance for _,distance in flows])), + 'wrap_max_difference':int(np.abs(before.astype(int)-after.astype(int)).max())} +(root/'checks.json').write_text(json.dumps(report,indent=2)) +print('PASS seeded deep galaxies, independent density, slow radial parallax, brightness/opacity, quality, panoramic coverage and seamless drift wrap',flush=True) +print(json.dumps(report),flush=True);print('ARTIFACTS',root,flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxy-render.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxy-render.cpp new file mode 100644 index 000000000000..71deac513794 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/galaxy-render.cpp @@ -0,0 +1,54 @@ +// Private, deterministic EGL fixture for the shared compositor/lock renderer. +#include "hyperspace-background.hpp" +#include "hyperspace-lock.h" +#include <EGL/egl.h> +#include <cstdio> +#include <cstdlib> +#include <vector> +static void check(bool ok,const char *what) { + if (!ok) { fprintf(stderr,"%s EGL=%x GL=%x\n",what,eglGetError(),glGetError());std::exit(1); } +} +int main(int argc,char **argv) { + check(argc>=8,"args output seed drift width height density quality [brightness opacity]"); + int seed=atoi(argv[2]),width=atoi(argv[4]),height=atoi(argv[5]),quality=atoi(argv[7]); + double drift=atof(argv[3]),density=atof(argv[6]); + check(eglBindAPI(EGL_OPENGL_ES_API),"bind API"); + EGLDisplay display=eglGetDisplay(EGL_DEFAULT_DISPLAY);check(eglInitialize(display,nullptr,nullptr),"display"); + const EGLint attrs[]={EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE,8,EGL_GREEN_SIZE,8,EGL_BLUE_SIZE,8,EGL_ALPHA_SIZE,8,EGL_NONE}; + EGLConfig config;EGLint count;check(eglChooseConfig(display,attrs,&config,1,&count)&&count,"config"); + const EGLint size[]={EGL_WIDTH,width,EGL_HEIGHT,height,EGL_NONE}; + EGLSurface surface=eglCreatePbufferSurface(display,config,size); + const EGLint ctxattrs[]={EGL_CONTEXT_CLIENT_VERSION,2,EGL_NONE}; + EGLContext context=eglCreateContext(display,config,EGL_NO_CONTEXT,ctxattrs); + check(eglMakeCurrent(display,surface,surface,context),"current"); + json_object *settings=json_object_new_object(); + json_object_object_add(settings,"hyperspace_seed",json_object_new_int(seed)); + json_object_object_add(settings,"hyperspace_quality",json_object_new_int(quality)); + json_object_object_add(settings,"hyperspace_galaxies",json_object_new_double(density)); + json_object_object_add(settings,"hyperspace_brightness",json_object_new_double(argc>8 ? atof(argv[8]) : 1)); + json_object_object_add(settings,"hyperspace_distant_stars",json_object_new_double(0)); + ambient_lock_host::parameters=settings; + OpenGL::program_t texture; + ambient_lock_host::texture_program=&texture; + { + ambient_hyperspace_background engine([]{}); + std::array<double,24> state{double(seed),0,0,0,0,drift, + 1,-3,15,.5,.3,0,.2,.1,.65,1,-15,18,.4,.4,.08,0,.3,0}; + check(engine.restore_scene(state),"restore scene"); + wf::auxilliary_buffer_t destination;destination.width=width;destination.height=height; + wf::render_target_t target(destination);target.geometry={0,0,double(width),double(height)}; + float background=0; + glClearColor(background,background,background,1);glClear(GL_COLOR_BUFFER_BIT); + engine.render(target,target.geometry,{width,height},argc>9 ? atof(argv[9]) : 1); + std::vector<unsigned char> pixels(width*height*4); + glReadPixels(0,0,width,height,GL_RGBA,GL_UNSIGNED_BYTE,pixels.data()); + check(glGetError()==GL_NO_ERROR,"render"); + FILE *file=fopen(argv[1],"wb");check(file,"output");fwrite(pixels.data(),pixels.size(),1,file);fclose(file); + fprintf(stderr,"GPU %s; %dx%d\n",glGetString(GL_RENDERER),width,height); + } + json_object_put(settings); + texture.free_resources(); + eglMakeCurrent(display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT); + eglDestroySurface(display,surface);eglDestroyContext(display,context);eglTerminate(display); +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/lock-mode.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/lock-mode.py new file mode 100755 index 000000000000..8944056869b1 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/lock-mode.py @@ -0,0 +1,196 @@ +"""Animated lock appearance and real test-service PAM in a private GPU session. +No test authentication or synthetic input endpoint is installed in production. +""" +from pathlib import Path +import argparse,json,os,shutil,signal,socket,sys,time +from PIL import Image,ImageChops +p=argparse.ArgumentParser();p.add_argument('--stage',type=Path,required=True) +p.add_argument('--auth-helper',type=Path,required=True);p.add_argument('--binary-dir',type=Path) +a=p.parse_args();assert os.geteuid()!=0 +sys.path.insert(0,str(a.stage/'tests'));from private_session import PrivateSession +extra=''' +[ambient-background] +enabled=true +controller= +hyperspace_quality=0 +hyperspace_speed=.5 +hyperspace_seed=173 +lock_show_clock=false +lock_show_date=false +lock_black_delay=0 +lock_brightness=1 +[output:HEADLESS-1] +mode=320x240@60 +position=0,0 +[output:HEADLESS-2] +mode=320x240@60 +position=320,0 +[output:HEADLESS-3] +mode=320x240@60 +position=640,0 +[output:HEADLESS-4] +position=960,0 +''' +with PrivateSession(a.stage,extra_plugins=' ambient-background session-lock digitalrain-idle digitalrain-cursor digitalrain-power lock-test desktop-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + bins=s.root/'bin';bins.mkdir() + for name in ['digitalrain-lock','digitalrain']: + shutil.copy2((a.binary_dir or a.stage)/name,bins/name) + shutil.copy2(a.auth_helper,bins/'digitalrain-auth') + cfg=s.root/'rain.toml';cfg.write_text('[global]\nTIMEOUT=999\nFOCUSED_TIMEOUT=999\nINHIBIT_VIDEO=false\n[matrix]\nBLACK_RAIN_TIMEOUT=0\nPOWER_OFF=false\n') + s.env['AMBIENT_BACKGROUND_CONFIG']=str(cfg) + def call(m,d=None): + r=s.call(m,d);assert not isinstance(r,dict) or not r.get('error'),r;return r + def wait(fn,timeout=8): + end=time.monotonic()+timeout + while time.monotonic()<end: + try: + r=fn() + if r:return r + except (ConnectionError,FileNotFoundError):pass + time.sleep(.04) + raise AssertionError('Timeout '+str(s.root)) + def status(method='status'): + with socket.socket(socket.AF_UNIX,socket.SOCK_SEQPACKET) as stream: + stream.settimeout(4);stream.connect(str(s.root/'digitalrain/lock.sock')) + assert __import__('struct').unpack('3i',stream.getsockopt(socket.SOL_SOCKET,socket.SO_PEERCRED,12))[1]==os.geteuid() + stream.send(json.dumps({'method':method}).encode());return json.loads(stream.recv(16384)) + def settings(**values): + before=status()['theme_revision'];call('wayfire/set-config-options',{'ambient-background/'+k:v for k,v in values.items()}) + wait(lambda:status()['theme_revision']>before);time.sleep(.15) + def shot(name,output='VIRTUAL-1'): + file=s.root/(name+'.png');s.run(['grim','-o',output,str(file)],timeout=8) + return Image.open(file).convert('RGB') + def key(code):call('test/key',{'code':code}) + def password(text): + codes={'r':19,'a':30,'i':23,'n':49,'-':12,'t':20,'e':18,'s':31,'7':8,'x':45} + for c in text:key(codes[c]);time.sleep(.02) + key(28) + def clear_prompt():key(1);wait(lambda:status()['controls_opacity']==0) + call('digitalrain/hide_cursor') + call('test/solid',{'output':'VIRTUAL-1','key':'private','layer':3,'x':0,'y':0,'w':960,'h':240,'r':.8,'g':0.,'b':0.}) + wait(lambda:(204,0,0) in shot('desktop').get_flattened_data()) + original={k:call('wayfire/get-config-option',{'option':'ambient-background/'+k})['value'] for k in ['backend','hyperspace_speed','hyperspace_brightness','hyperspace_color','matrix_color']} + lock=s.spawn('locker',[str(bins/'digitalrain-lock')]);wait(lambda:status()['state']=='locked') + wait(lambda:all(call('test/lock-state').values())) + assert status()['controls_opacity']==0 and not status()['clock_visible'] + assert status()['background']=='hyperspace';time.sleep(.25) + first=shot('hyperspace');assert first.getbbox() and (204,0,0) not in first.get_flattened_data() + time.sleep(.3);assert ImageChops.difference(first,shot('hyperspace-moving')).getbbox() + print('PASS actual hyperspace lock surfaces cover every physical output and move',flush=True) + settings(lock_speed='0');frozen=shot('frozen');time.sleep(.3) + assert not ImageChops.difference(frozen,shot('still-frozen')).getbbox() + settings(lock_brightness='0');assert not shot('dark').getbbox() + settings(lock_brightness='1',lock_speed='1',lock_background='nebula') + assert status()['background']=='nebula';time.sleep(.3) + nebula=shot('nebula-lock');assert nebula.getbbox() and (204,0,0) not in nebula.get_flattened_data() + time.sleep(.3);assert ImageChops.difference(nebula,shot('nebula-lock-moving')).getbbox() + settings(lock_speed='0');frozen_nebula=shot('nebula-lock-frozen');time.sleep(.3) + assert not ImageChops.difference(frozen_nebula,shot('nebula-lock-still')).getbbox() + settings(lock_brightness='0');assert not shot('nebula-lock-dark').getbbox() + print('PASS Nebula lock switching, continuous motion, freeze and brightness; every output stays protected',flush=True) + settings(lock_brightness='1',lock_speed='1',lock_background='digitalrain' ,lock_colour_override='true',lock_colour='0 0 1 1') + assert status()['background']=='digitalrain' + def blue(): + img=shot('blue');totals=[sum(p[c] for p in img.get_flattened_data()) for c in range(3)] + return totals[2]>1.5*max(totals[:2]) and totals[2]>1000 + wait(blue) + assert all(call('wayfire/get-config-option',{'option':'ambient-background/'+k})['value']==v for k,v in original.items()) + print('PASS independent effect, frozen motion, brightness and colour override preserve Ambient preferences',flush=True) + settings(lock_background='black',lock_show_clock='true',lock_show_date='true', + lock_output='HEADLESS-2',lock_ui_hide_delay='.9',lock_ui_fade_duration='.3') + assert not shot('controls-initially-hidden').getbbox() + key(42) + wait(lambda:0<status()['controls_opacity']<1) + partial=shot('controls-appearing') + wait(lambda:status()['controls_opacity']==1) + selected=shot('controls-selected-monitor') + assert not selected.crop((0,0,320,240)).getbbox() + assert selected.crop((320,0,640,240)).getbbox() + assert not selected.crop((640,0,960,240)).getbbox() + assert not shot('other-output-hidden','HEADLESS-4').getbbox() + assert status()['controls_outputs']==['HEADLESS-2'] and status()['clock_visible'] + assert sum(sum(p) for p in partial.get_flattened_data()) < sum(sum(p) for p in selected.get_flattened_data()) + key(45) # An unfinished password must be discarded when controls fade away. + wait(lambda:status()['controls_opacity']==0) + assert not shot('controls-idle-hidden').getbbox() and all(call('test/lock-state').values()) + settings(lock_output='all',lock_ui_hide_delay='0');status('wake') + wait(lambda:status()['controls_opacity']==1) + every=shot('controls-all-monitors') + assert all(every.crop((i*320,0,(i+1)*320,240)).getbbox() for i in range(3)) + assert shot('controls-separate-monitor','HEADLESS-4').getbbox() + settings(lock_output='HEADLESS-4') + assert not shot('controls-away-from-joined-output').getbbox() + assert shot('controls-on-separate-output','HEADLESS-4').getbbox() + call('test/remove-output',{'output':'HEADLESS-4'}) + wait(lambda:status()['controls_outputs']!=['HEADLESS-4']) + assert call('wayfire/get-config-option',{'option':'ambient-background/lock_output'})['value']=='HEADLESS-4' + assert all(call('test/lock-state').values()) + settings(lock_output='DISCONNECTED-MONITOR') + fallback=shot('controls-fallback') + assert fallback.crop((0,0,320,240)).getbbox() + assert not fallback.crop((320,0,960,240)).getbbox() + assert status()['controls_outputs']==['HEADLESS-1'] + clear_prompt();assert not shot('controls-escape-hidden').getbbox() + print('PASS input-only clock/prompt fade, idle password clearing, individual/all physical monitors and disconnected fallback',flush=True) + settings(lock_output='all',lock_ui_on_interaction='false') + wait(lambda:status()['controls_opacity']==1) + settings(lock_background='black',lock_show_clock='true',lock_show_date='true',lock_speed='0') + clock=shot('clock') + assert all(clock.crop((i*320,0,(i+1)*320,240)).getbbox() for i in range(3)) + settings(lock_show_date='false');assert ImageChops.difference(clock,shot('clock-no-date')).getbbox() + settings(lock_show_clock='false');assert not shot('plain-black').getbbox() + print('PASS clock/date appear on all three physical monitors with independent controls',flush=True) + settings(lock_ui_on_interaction='true',lock_background='hyperspace',lock_speed='1',lock_black_delay='1.2',lock_fade_duration='.5') + status('wake');clear_prompt();wait(lambda:0<status()['background_opacity']<.95) + wait(lambda:status()['phase']=='black');assert not shot('faded-black').getbbox() + stat=Path(f'/proc/{lock.pid}/stat').read_text().split();time.sleep(.7) + later=Path(f'/proc/{lock.pid}/stat').read_text().split() + assert sum(map(int,later[13:15]))-sum(map(int,stat[13:15]))<=8 + status('wake');assert all(call('test/output-power').values()) + assert status()['state']=='locked' and all(call('test/lock-state').values()) + settings(lock_black_delay='0');clear_prompt();wait(lambda:status()['background_opacity']>.99) + assert shot('resumed').getbbox() + print('PASS smooth black fade pauses GPU rendering; wake restores animation while remaining locked',flush=True) + password('x');time.sleep(.7);assert lock.poll() is None and status()['state']=='locked' + lock.send_signal(signal.SIGUSR1);assert all(call('test/lock-state').values()) + time.sleep(2);password('rain-test-7');assert lock.wait(8)==0;s.processes.remove(lock) + wait(lambda:not any(call('test/lock-state').values())) + print('PASS bad password and saver signals retain protection; only real PAM success unlocks',flush=True) + lock=s.spawn('locker-crash',[str(bins/'digitalrain-lock')]);wait(lambda:status()['state']=='locked') + lock.kill();lock.wait(4);s.processes.remove(lock);assert all(call('test/lock-state').values()) + lock=s.spawn('locker-recover',[str(bins/'digitalrain-lock')]);wait(lambda:status()['state']=='locked') + call('test/add-output');time.sleep(.4);assert all(call('test/lock-state').values()) + if 'HEADLESS-5' in call('test/output-power'): + call('test/remove-output',{'output':'HEADLESS-5'});time.sleep(.2) + assert all(call('test/lock-state').values()) + password('rain-test-7');assert lock.wait(8)==0;s.processes.remove(lock) + print('PASS renderer crash stays locked, replacement recovers, newly connected output is protected',flush=True) + for path in s.root.glob('locker*.log'): + log=path.read_text();assert 'Ambient lock shader:' not in log and 'framebuffer allocation failed' not in log,log + print('PASS lock-mode integration',flush=True) + +# Power policy gets a fresh compositor, so previous output hot-plug and live +# output-control changes do not alter the fixture's initial monitor topology. +power_session=PrivateSession(a.stage,config_extra=extra+""" +[core] +plugins=ipc stipc ipc-rules ambient-background session-lock digitalrain-idle digitalrain-cursor digitalrain-power lock-test desktop-test +[ambient-background] +lock_black_delay=1 +lock_fade_duration=.2 +""") +power_session.env['WLR_HEADLESS_OUTPUTS']='3' +with power_session as s: + print('POWER SESSION',s.root,flush=True) + bins=s.root/'bin';bins.mkdir() + shutil.copy2((a.binary_dir or a.stage)/'digitalrain-lock',bins/'digitalrain-lock') + shutil.copy2(a.auth_helper,bins/'digitalrain-auth') + cfg=s.root/'rain.toml';cfg.write_text('[global]\nTIMEOUT=999\nINHIBIT_VIDEO=false\n[matrix]\nPOWER_OFF=true\n') + s.env['AMBIENT_BACKGROUND_CONFIG']=str(cfg) + powered=s.spawn('locker-power',[str(bins/'digitalrain-lock')]);wait(lambda:status()['state']=='locked') + physical=[f'HEADLESS-{i}' for i in range(1,4)] + wait(lambda:all(not call('test/output-power')[name] for name in physical)) + status('wake');wait(lambda:all(call('test/output-power')[name] for name in physical)) + assert all(call('test/lock-state').values()) + password('rain-test-7');assert powered.wait(8)==0;s.processes.remove(powered) + print('PASS locked power-off and wake preserve protection and pause animation',flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula-native.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula-native.py new file mode 100755 index 000000000000..16e34b1991d3 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula-native.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +"""Private compositor: solid encounters, paused travel, Cube and partial damage.""" +from pathlib import Path +import sys,time +import numpy as np +from PIL import Image +stage=Path(sys.argv[1]).resolve();sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=''' +[core] +vwidth=4 +vheight=3 +[cube] +render_at_rest=true +mouse_overview=true +[ambient-background] +enabled=true +controller= +focused_delay=86400 +inactive_delay=86400 +hyperspace_speed=0 +hyperspace_seed=129 +hyperspace_cloud_density=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_dust_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_galaxies=1.5 +backend=nebula +nebula_speed=0 +nebula_seed=129 +nebula_cloud_density=.85 +nebula_star_density=1 +hyperspace_distant_drift=1 +hyperspace_asteroids=0 +''' +with PrivateSession(stage,hotplug=True,extra_plugins=' ambient-background cube render-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + def call(method,data=None): + r=s.call(method,data);assert not isinstance(r,dict) or not r.get('error'),r;return r + def snapshot(name): + call('ambient/sync',{'outputs':[{'name':'HEADLESS-'+str(i),'opacity':0,'black':0,'effect':1} for i in range(1,5)]}) + time.sleep(.08);p=s.root/(name+'.png');s.run(['grim','-o','VIRTUAL-1',str(p)]) + return np.asarray(Image.open(p).convert('RGB')) + def settings(**options): + call('wayfire/set-config-options',{'ambient-background/'+k:v for k,v in options.items()});time.sleep(.12) + s.wait(lambda:len(call('ambient/status')['outputs'])==4) + call('stipc/move_cursor',{'x':1930,'y':10}) + first=snapshot('frozen-galaxies');assert first.max()>20 + time.sleep(.25);assert np.array_equal(first,snapshot('still-frozen')) + settings(nebula_cloud_density=0,nebula_star_density=0);assert snapshot('disabled').max()==0 + settings(nebula_cloud_density=.85,nebula_star_density=1);assert np.array_equal(first,snapshot('restored')) + settings(nebula_speed=1.5);time.sleep(.4) + moved=snapshot('approaching');assert not np.array_equal(first,moved) + settings(nebula_speed=0) + frozen=snapshot('stopped');time.sleep(.25);assert np.array_equal(frozen,snapshot('still-stopped')) + assert call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + call('test/cube',{'output':'VIRTUAL-1','angle':.7,'zoom':.55});time.sleep(.25) + assert snapshot('cube-with-galaxies').max()>20 + assert call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + call('test/cube',{'output':'VIRTUAL-1','stop':True}) + log=(s.root/'wayfire.log').read_text(errors='replace') + assert 'GL_INVALID' not in log and 'GL_OUT_OF_MEMORY' not in log and 'Shader compilation failed' not in log + print('PASS native Nebula motion/freeze, stars/clouds disable/restore, shared Cube rendering and clipped partial damage',flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula-reload.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula-reload.py new file mode 100755 index 000000000000..153a4ab7576b --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula-reload.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""Reload after a globally loaded old renderer; match a fresh candidate frame.""" +from pathlib import Path +import subprocess, sys, time +import numpy as np +from PIL import Image + +stage = Path(sys.argv[1]).resolve() +sys.path.insert(0, str(stage/'tests')) +from private_session import PrivateSession + +candidate = stage/'plugins/libambient-background.so' +old = stage/'plugins/old-r15/libambient-background.so' +exports = subprocess.check_output(['nm', '-D', '-C', str(candidate)], text=True) +assert 'ambient_nebula_background' not in exports, 'Renderer internals are interposable' +config = ''' +[cube] +render_at_rest=true +[ambient-background] +enabled=true +controller= +focused_delay=86400 +inactive_delay=86400 +hyperspace_speed=0 +hyperspace_seed=129 +hyperspace_cloud_density=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_dust_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_galaxies=1.5 +backend=nebula +nebula_speed=0 +nebula_seed=129 +nebula_cloud_density=.85 +nebula_star_density=1 +hyperspace_distant_drift=1 +hyperspace_asteroids=0 +''' + +def capture(s, name): + s.call('ambient/sync', {'outputs': [ + {'name': 'HEADLESS-'+str(i), 'opacity': 0, 'black': 0, 'effect': 1} + for i in range(1, 5)]}) + s.wait(lambda: all(z['opacity'] == 0 for z in s.call('ambient/status')['outputs'])) + + time.sleep(.08) + p = s.root/(name+'.png') + s.run(['grim', '-o', 'VIRTUAL-1', str(p)]) + return np.asarray(Image.open(p).convert('RGB')) + +def checked(s, method, data=None): + r = s.call(method, data) + assert not isinstance(r, dict) or not r.get('error'), r + return r + +with PrivateSession(stage, hotplug=True, extra_plugins=' ambient-background cube render-test', + config_extra=config) as s: + print('FRESH', s.root, flush=True) + checked(s, 'stipc/move_cursor', {'x': 1930, 'y': 10}) + expected = capture(s, 'fresh-renderer') + assert expected.max() > 20 and expected.min() >= 0 + checked(s, 'test/cube', {'output': 'VIRTUAL-1', 'angle': .7, 'zoom': .55}) + expected_cube = capture(s, 'fresh-cube') + +with PrivateSession(stage, hotplug=True, extra_plugins=f' {old} cube render-test', + config_extra=config) as s: + print('RELOADED', s.root, flush=True) + checked(s, 'stipc/move_cursor', {'x': 1930, 'y': 10}) + previous = capture(s, 'old-renderer') + assert not np.array_equal(previous, expected), 'Fixture does not distinguish old/new rendering' + plugins = checked(s, 'wayfire/get-config-option', {'option': 'core/plugins'})['value'] + checked(s, 'wayfire/set-config-options', {'core/plugins': plugins.replace(str(old), str(candidate))}) + checked(s, 'wayfire/reload-plugins') + actual = capture(s, 'reloaded-renderer') + assert np.array_equal(actual, expected), 'Reload still renders using the resident old version' + checked(s, 'test/cube', {'output': 'VIRTUAL-1', 'angle': .7, 'zoom': .55}) + actual_cube = capture(s, 'reloaded-cube') + assert np.array_equal(actual_cube, expected_cube), 'Cube reload uses a different renderer' + checked(s, 'test/cube', {'output': 'VIRTUAL-1', 'stop': True}) + checked(s, 'wayfire/set-config-options', {'ambient-background/nebula_speed': 2.0}) + time.sleep(.15) + moved = capture(s, 'fast-pass') + assert not np.array_equal(moved, actual), 'Reloaded motion is frozen' + log = (s.root/'wayfire.log').read_text(errors='replace') + assert 'GL_INVALID' not in log and 'Shader compilation failed' not in log +print('PASS r15-to-r16 native reload matches fresh Nebula rendering, including Cube delegation', flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula-render.cpp b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula-render.cpp new file mode 100644 index 000000000000..039abaacb99c --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula-render.cpp @@ -0,0 +1,51 @@ +// Private, deterministic EGL fixture for the shared compositor/lock renderer. +#include "hyperspace-background.hpp" +#include "../effects/nebula/nebula-background.hpp" +#include "hyperspace-lock.h" +#include <EGL/egl.h> +#include <cstdio> +#include <cstdlib> +#include <vector> +static void check(bool ok,const char *what) { + if (!ok) { fprintf(stderr,"%s EGL=%x GL=%x\n",what,eglGetError(),glGetError());std::exit(1); } +} +int main(int argc,char **argv) { + check(argc>=7,"args output settings-json width height opacity background"); + int width=atoi(argv[3]),height=atoi(argv[4]); + check(eglBindAPI(EGL_OPENGL_ES_API),"bind API"); + EGLDisplay display=eglGetDisplay(EGL_DEFAULT_DISPLAY);check(eglInitialize(display,nullptr,nullptr),"display"); + const EGLint attrs[]={EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE,8,EGL_GREEN_SIZE,8,EGL_BLUE_SIZE,8,EGL_ALPHA_SIZE,8,EGL_NONE}; + EGLConfig config;EGLint count;check(eglChooseConfig(display,attrs,&config,1,&count)&&count,"config"); + const EGLint size[]={EGL_WIDTH,width,EGL_HEIGHT,height,EGL_NONE}; + EGLSurface surface=eglCreatePbufferSurface(display,config,size); + const EGLint ctxattrs[]={EGL_CONTEXT_CLIENT_VERSION,2,EGL_NONE}; + EGLContext context=eglCreateContext(display,config,EGL_NO_CONTEXT,ctxattrs); + check(eglMakeCurrent(display,surface,surface,context),"current"); + json_object *settings=json_object_from_file(argv[2]);check(settings,"settings"); + ambient_lock_host::parameters=settings; + OpenGL::program_t texture;ambient_lock_host::texture_program=&texture; + { + wf::auxilliary_buffer_t destination;destination.width=width;destination.height=height; + wf::render_target_t target(destination);target.geometry={0,0,double(width),double(height)}; + float background=atof(argv[6]);glClearColor(background,background,background,1);glClear(GL_COLOR_BUFFER_BIT); + json_object *backend=nullptr,*scene=nullptr; + json_object_object_get_ex(settings,"_backend",&backend);json_object_object_get_ex(settings,"_scene",&scene); + if (backend && std::string(json_object_get_string(backend))=="nebula") { + ambient_nebula_background engine([]{}); + check(engine.restore_scene({json_object_get_double(json_object_array_get_idx(scene,0)),json_object_get_double(json_object_array_get_idx(scene,1))}),"nebula scene"); + engine.render(target,target.geometry,{width,height},atof(argv[5])); + } else { + ambient_hyperspace_background engine([]{});std::array<double,24> state; + for (unsigned i=0;i<state.size();i++) state[i]=json_object_get_double(json_object_array_get_idx(scene,i)); + check(engine.restore_scene(state),"hyperspace scene");engine.render(target,target.geometry,{width,height},atof(argv[5])); + } + std::vector<unsigned char> pixels(width*height*4);glReadPixels(0,0,width,height,GL_RGBA,GL_UNSIGNED_BYTE,pixels.data()); + check(glGetError()==GL_NO_ERROR,"render");FILE *file=fopen(argv[1],"wb");check(file,"output");fwrite(pixels.data(),pixels.size(),1,file);fclose(file); + fprintf(stderr,"GPU %s; %dx%d\n",glGetString(GL_RENDERER),width,height); + } + json_object_put(settings); + texture.free_resources(); + eglMakeCurrent(display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT); + eglDestroySurface(display,surface);eglDestroyContext(display,context);eglTerminate(display); +} diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula.py new file mode 100755 index 000000000000..21418595f0b7 --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/nebula.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Private actual-GPU checks for Nebula and reused Hyperspace cloud detail.""" +from pathlib import Path +import argparse,json,os,subprocess,tempfile,xml.etree.ElementTree as ET +import numpy as np +from PIL import Image +p=argparse.ArgumentParser();p.add_argument('--stage',type=Path,required=True);a=p.parse_args();assert os.geteuid()!=0 +root=Path(tempfile.mkdtemp(prefix='ambient-nebula-')) +metadata=ET.parse(a.stage/'metadata/ambient-background.xml') +def defaults(): + result={} + for o in metadata.findall('.//option'): + value=o.findtext('default');kind=o.get('type') + if value is not None and (o.get('name').startswith('nebula_') or o.get('name').startswith('hyperspace_')): + result[o.get('name')]=float(value) if kind=='double' else int(value) if kind=='int' else value + return result +def render(name,backend='nebula',phase=0,seed=173,width=960,height=540,opacity=1,background=0,**options): + settings=defaults();settings.update(options);settings['_backend']=backend;settings[backend+'_seed']=seed + settings['_scene']=[seed,phase] if backend=='nebula' else [seed,0,0,0,phase,0,1,-3,15,.5,.3,0,.2,.1,.65,1,-15,18,.4,.4,.08,0,.3,0] + cfg=root/(name+'.json');cfg.write_text(json.dumps(settings));out=root/(name+'.rgba') + subprocess.run([str(a.stage/'nebula-render'),str(out),str(cfg),str(width),str(height),str(opacity),str(background)], + env={'PATH':'/usr/bin:/bin','EGL_PLATFORM':'surfaceless','HOME':str(root)},check=True,capture_output=True) + frame=np.frombuffer(out.read_bytes(),dtype=np.uint8).reshape(height,width,4)[::-1].copy();out.unlink() + Image.fromarray(frame[:,:,:3]).save(root/(name+'.png'));return frame[:,:,:3] +first=render('nebula');assert first.max()>25 and (first.max(axis=2)>10).sum()>1000 +assert np.array_equal(first,render('repeatable')) +assert not np.array_equal(first,render('different-seed',seed=27182)) +assert not np.array_equal(first,render('moving',phase=.03)) +assert not render('no-light',nebula_cloud_density=0,nebula_star_density=0).any() +assert not render('no-brightness',nebula_brightness=0).any() +half=render('half',opacity=.5);assert np.abs(half.astype(float)-first/2).max()<=1.5 +wallpaper=render('transparent',background=.15,nebula_cloud_density=0,nebula_star_density=0) +assert wallpaper.min()>=37 and wallpaper.max()<=39,'No-background shader erases the backdrop' +assert not np.array_equal(first,render('no-stars',nebula_star_density=0)) +assert not np.array_equal(first,render('no-clouds',nebula_cloud_density=0)) +for quality in range(3):assert render('detail-'+str(quality),nebula_quality=quality).max()>20 +for w,h in [(1920,360),(360,640)]: + f=render(f'geometry-{w}-{h}',width=w,height=h) + for segment in np.array_split(f,3,axis=1):assert (segment.max(axis=2)>8).sum()>100 +wrap=render('wrap',phase=2*np.pi-1e-6);assert np.abs(wrap.astype(int)-first.astype(int)).max()<5 +isolated=dict(hyperspace_star_density=0,hyperspace_distant_stars=0,hyperspace_galaxies=0,hyperspace_dust_density=0,hyperspace_pulsars=0,hyperspace_anomalies=0,hyperspace_asteroids=0,hyperspace_cloud_density=1.5,hyperspace_quality=2) +base=render('hyperspace-original',backend='hyperspace',**isolated) +folded=render('hyperspace-fractal',backend='hyperspace',hyperspace_fractal_detail=.65,**isolated) +assert base.max()>10 and folded.max()>10 and not np.array_equal(base,folded) +assert np.array_equal(folded,render('hyperspace-fixed',backend='hyperspace',hyperspace_fractal_detail=.65,**isolated)) +print('PASS seeded Nebula, independent stars/clouds, brightness, opacity, transparent backdrop, detail, panoramic/portrait coverage, motion and clock wrap; reused Hyperspace cloud detail',flush=True) +print('ARTIFACTS',root,flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/output-fade.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/output-fade.py new file mode 100755 index 000000000000..3fe3d548562d --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/output-fade.py @@ -0,0 +1,114 @@ +"""Final desktop composition must fade per monitor, including every Cube row. + +Only private synthetic desktop elements and private application windows are read. +""" +from pathlib import Path +import sys +import time +from PIL import Image + +stage = Path(sys.argv[1]).resolve() +sys.path.insert(0, str(stage / 'tests')) +from private_session import PrivateSession + +extra = ''' +[core] +vwidth=4 +vheight=3 +[cube] +render_at_rest=true +mouse_overview=true +rotate_layer_shell=* +[ambient-background] +enabled=true +controller= +restore_duration=.05 +manual_duration=.1 +hyperspace_speed=0 +hyperspace_star_density=0 +hyperspace_distant_stars=0 +hyperspace_cloud_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_asteroids=0 +hyperspace_dust_density=0 +''' +with PrivateSession(stage, hotplug=True, extra_plugins=' ambient-background cube render-test', config_extra=extra) as s: + print('SESSION', s.root, flush=True) + def call(method, data=None): + result = s.call(method, data) + assert not isinstance(result, dict) or not result.get('error'), result + return result + def snap(name): + path = s.root / (name + '.png') + s.run(['grim', '-o', 'VIRTUAL-1', str(path)]) + return Image.open(path).convert('RGB') + def sync(alpha): + call('ambient/sync', {'outputs': [{'name': 'HEADLESS-' + str(i), 'opacity': a, 'black': 0, 'effect': 1} + for i, a in enumerate(alpha, 1)]}) + time.sleep(.18) + def coloured(image, left, right): + return sum(max(p) > 25 for p in image.crop((left, 0, right, image.height)).get_flattened_data()) + def application_geometry(): + view = next(v for v in call('window-rules/list-views') if v.get('app-id') == 'ambient-output-test') + output = next(o for o in call('window-rules/list-outputs') if o['name'] == 'VIRTUAL-1') + geometry = view['geometry'].copy() + # Cube's lock inhibition ends its preview and selects the visible face. + # Compare the application's absolute workspace position, not the + # coordinates relative to a potentially different current workspace. + geometry['x'] += output['workspace']['x'] * 1920 + geometry['y'] += output['workspace']['y'] * 720 + return geometry + s.wait(lambda: len(call('ambient/status')['outputs']) == 4) + call('stipc/move_cursor', {'x': 1930, 'y': 10}) + for layer in [1, 3, 4, 5]: + call('test/solid', {'key': 'layer-' + str(layer), 'output': 'VIRTUAL-1', 'layer': layer, + 'x': 0, 'y': (layer - 1) * 25, 'w': 1920, 'h': 20, 'r': 0, 'g': 1, 'b': 1, 'a': 1}) + s.spawn('app', ['alacritty', '--config-file', '/dev/null', '--class', 'ambient-output-test', + '-o', 'colors.primary.background="#D76F2E"', '-e', 'sleep', '180']) + view = s.wait(lambda: next((v for v in call('window-rules/list-views') + if v.get('app-id') == 'ambient-output-test' and v.get('mapped')), None)) + call('window-rules/set-window-scope', {'id': view['id'], 'scope': 'virtual'}) + call('window-rules/configure-view', {'id': view['id'], 'tiled-edges': 0, + 'geometry': {'x': 100, 'y': 280, 'width': 1600, 'height': 300}}) + time.sleep(.3) + original = application_geometry() + for angle in [0, .65, 1.57, 3.14, 4.71]: + call('test/cube', {'output': 'VIRTUAL-1', 'angle': angle, 'zoom': .45}) + sync([1, 1, 1, 1]) + normal = snap('normal-' + str(angle)) + assert coloured(normal, 0, 640) > 100, ('Fixture has no Cube content on first monitor', angle) + sync([0, 1, 1, 1]) + faded = snap('faded-' + str(angle)) + leak = coloured(faded, 0, 640) + assert leak == 0, ('Unfaded Cube faces or rows leaked onto idle monitor', angle, leak) + assert faded.crop((640, 0, 1920, 720)).tobytes() == normal.crop((640, 0, 1920, 720)).tobytes(), 'Active monitors changed' + sync([.5, 1, 1, 1]) + half = snap('half-' + str(angle)) + samples = [(x, y) for y in range(0, 720, 9) for x in range(0, 640, 9) if max(normal.getpixel((x, y))) > 25] + assert samples + assert max(abs(a / 2 - b) for xy in samples for a, b in zip(normal.getpixel(xy), half.getpixel(xy))) <= 3 + print('PASS output mask covers front, side, rear and stacked Cube rows; active monitors and 50% fade preserved', flush=True) + assert application_geometry() == original, (original, application_geometry()) + print('PASS output fading preserves application geometry', flush=True) + sync([0, 0, 0, 1]) + call('wayfire/set-config-options', {'ambient-background/hyperspace_speed': 1.5, + 'ambient-background/hyperspace_asteroids': 1, + 'ambient-background/hyperspace_star_density': 1}) + time.sleep(.3) + assert coloured(snap('live-wallpaper'), 0, 1920) > 100, 'Fade hid the live wallpaper' + assert call('test/partial', {'output': 'VIRTUAL-1'})['outside'] == 0 + print('PASS live wallpaper remains visible and partial output damage stays clipped', flush=True) + call('test/inhibit', {'inhibit': True}) + time.sleep(.15) + assert coloured(snap('inhibited'), 0, 640) > 100, 'Lock inhibition did not restore desktop' + call('test/inhibit', {'inhibit': False}) + print('PASS lock inhibition bypasses the ambient cover', flush=True) + for layer in [6, 7]: + call('test/solid', {'key': 'protected', 'output': 'VIRTUAL-1', 'layer': layer, + 'x': 100, 'y': 200, 'w': 120, 'h': 80, 'r': 1, 'g': 0, 'b': 0, 'a': 1}) + sync([0, 0, 0, 1]) + assert snap('protected-' + str(layer)).getpixel((150, 240)) == (255, 0, 0), 'Ambient cover obscured a protected layer' + call('test/solid', {'key': 'protected', 'close': True}) + print('PASS screensaver and lock layers remain above output fading', flush=True) diff --git a/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/stars-frustum.py b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/stars-frustum.py new file mode 100755 index 000000000000..645d5c7b0e1e --- /dev/null +++ b/gui-apps/ambient-background/files/ambient-background-1.0.0-r16/tests/stars-frustum.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""GPU regression: travelling stars occupy the full panoramic view.""" +from pathlib import Path +import os,sys,time +import numpy as np +from PIL import Image +stage=Path(sys.argv[1]).resolve();sys.path.insert(0,str(stage/'tests')) +from private_session import PrivateSession +extra=""" +[cube] +render_at_rest=true +[ambient-background] +enabled=true +controller= +focused_delay=86400 +inactive_delay=86400 +hyperspace_speed=0 +hyperspace_seed=31415 +hyperspace_quality=2 +hyperspace_cloud_density=0 +hyperspace_star_density=1 +hyperspace_distant_stars=0 +hyperspace_dust_density=0 +hyperspace_glow=0 +hyperspace_pulsars=0 +hyperspace_anomalies=0 +hyperspace_brightness=1 +""" +module=os.environ.get('AMBIENT_MODULE','ambient-background') +with PrivateSession(stage,hotplug=True,extra_plugins=' cube '+module+' render-test',config_extra=extra) as s: + print('SESSION',s.root,flush=True) + s.wait(lambda:len(s.call('ambient/status')['outputs'])==4) + s.call('stipc/move_cursor',{'x':1930,'y':10});time.sleep(.25) + def snapshot(name): + p=s.root/(name+'.png');s.run(['grim','-o','VIRTUAL-1',str(p)]) + return np.asarray(Image.open(p).convert('RGB')) + still=snapshot('full-frustum') + assert still.shape==(720,1920,3) + mask=still.max(axis=2)>12 + outer=np.concatenate([mask[:,0:384],mask[:,1536:1920]],axis=1).mean() + centre=mask[:,768:1152].mean() + ratio=outer/max(centre,1e-9) + assert outer>.0001 and ratio>.65, ('Travelling stars collapsed into a central cone',float(outer),float(centre),float(ratio)) + time.sleep(.2);assert np.array_equal(still,snapshot('frozen')) + s.call('wayfire/set-config-options',{'ambient-background/hyperspace_speed':1.5}) + time.sleep(.3);moving=snapshot('moving');time.sleep(.3) + assert not np.array_equal(moving,snapshot('moving-next')) + assert s.call('test/partial',{'output':'VIRTUAL-1'})['outside']==0 + print('PASS full-frustum travelling stars and depth bridge; outer/central density',round(float(ratio),3),'; freeze, animation and damage',flush=True) diff --git a/gui-apps/wayfire-applications/wayfire-applications-0.1.2.ebuild b/gui-apps/wayfire-applications/wayfire-applications-0.1.2.ebuild index e165c16f0f96..7ccf913faf0c 100644 --- a/gui-apps/wayfire-applications/wayfire-applications-0.1.2.ebuild +++ b/gui-apps/wayfire-applications/wayfire-applications-0.1.2.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: applications" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" dev-libs/glib:2 =dev-libs/yyjson-0.13.0* " @@ -18,6 +19,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-appmenu/wayfire-appmenu-0.1.0.ebuild b/gui-apps/wayfire-appmenu/wayfire-appmenu-0.1.0.ebuild index 95e4b01288da..743945308e12 100644 --- a/gui-apps/wayfire-appmenu/wayfire-appmenu-0.1.0.ebuild +++ b/gui-apps/wayfire-appmenu/wayfire-appmenu-0.1.0.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: appmenu" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.3 >=gui-libs/wayfire-ui-0.1.3 @@ -25,6 +26,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-appmenu/wayfire-appmenu-0.1.1.ebuild b/gui-apps/wayfire-appmenu/wayfire-appmenu-0.1.1.ebuild index da5f70777d61..a5001c0d3dac 100644 --- a/gui-apps/wayfire-appmenu/wayfire-appmenu-0.1.1.ebuild +++ b/gui-apps/wayfire-appmenu/wayfire-appmenu-0.1.1.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: appmenu" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.3 >=gui-libs/wayfire-ui-0.1.3 @@ -27,6 +28,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-ctl/wayfire-ctl-0.1.2.ebuild b/gui-apps/wayfire-ctl/wayfire-ctl-0.1.2.ebuild index cc47de8d88ab..970c289b3a4b 100644 --- a/gui-apps/wayfire-ctl/wayfire-ctl-0.1.2.ebuild +++ b/gui-apps/wayfire-ctl/wayfire-ctl-0.1.2.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: ctl" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=gui-libs/wayfire-core-0.1.5 dev-libs/glib:2 =dev-libs/yyjson-0.13.0* @@ -19,6 +20,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-ctl/wayfire-ctl-0.1.4.ebuild b/gui-apps/wayfire-ctl/wayfire-ctl-0.1.4.ebuild index cc47de8d88ab..970c289b3a4b 100644 --- a/gui-apps/wayfire-ctl/wayfire-ctl-0.1.4.ebuild +++ b/gui-apps/wayfire-ctl/wayfire-ctl-0.1.4.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: ctl" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=gui-libs/wayfire-core-0.1.5 dev-libs/glib:2 =dev-libs/yyjson-0.13.0* @@ -19,6 +20,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-desktop/wayfire-desktop-0.1.11.ebuild b/gui-apps/wayfire-desktop/wayfire-desktop-0.1.11.ebuild index 529eb8d4a3c5..2da14071e21c 100644 --- a/gui-apps/wayfire-desktop/wayfire-desktop-0.1.11.ebuild +++ b/gui-apps/wayfire-desktop/wayfire-desktop-0.1.11.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: desktop" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" " RDEPEND="${DEPEND} @@ -43,6 +44,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-desktop/wayfire-desktop-0.1.7.ebuild b/gui-apps/wayfire-desktop/wayfire-desktop-0.1.7.ebuild index c8332366e14e..4d434503929e 100644 --- a/gui-apps/wayfire-desktop/wayfire-desktop-0.1.7.ebuild +++ b/gui-apps/wayfire-desktop/wayfire-desktop-0.1.7.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: desktop" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" " RDEPEND="${DEPEND} @@ -41,6 +42,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-dock/files/wayfire-dock-0.1.1-hidden-position.patch b/gui-apps/wayfire-dock/files/wayfire-dock-0.1.1-hidden-position.patch new file mode 100644 index 000000000000..add4cf313a4d --- /dev/null +++ b/gui-apps/wayfire-dock/files/wayfire-dock-0.1.1-hidden-position.patch @@ -0,0 +1,42 @@ +--- a/private/src/util/wf-autohide-window.cpp ++++ b/private/src/util/wf-autohide-window.cpp +@@ -284,7 +284,10 @@ + /* Set new anchor */ + GtkLayerShellEdge edge = get_anchor_edge(position); + gtk_layer_set_anchor(this->gobj(), edge, true); +- gtk_layer_set_margin(this->gobj(), edge, edge_margin); ++ // A layout refresh must retain the current slide position. In particular, ++ // never publish the visible margin while a member dock is hidden. ++ gtk_layer_set_margin(this->gobj(), edge, ++ edge_margin + (independent_edge ? double(autohide_animation) : 0)); + + // margins on both sides of the window + if (mirror_margin && auto_exclusive_zone) +@@ -337,14 +340,17 @@ + gtk_layer_set_margin(gobj(), GTK_LAYER_SHELL_EDGE_LEFT, + margins->left + std::max(0, int(std::lround(region->width)) - width) / 2); + gtk_layer_set_margin(gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, 0); +- if (get_surface()) wl_surface_commit(get_wl_surface()); + } else if (margins) + { + gtk_layer_set_margin(gobj(), GTK_LAYER_SHELL_EDGE_LEFT, margins->left); + gtk_layer_set_margin(gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, margins->right); + } + update_region_reservation(); +- if (!independent_edge) return; ++ if (!independent_edge) ++ { ++ if (get_surface()) wl_surface_commit(get_wl_surface()); ++ return; ++ } + } + + // need different measurements depending on position +@@ -375,6 +381,7 @@ + + void WayfireAutohidingWindow::set_region_content_width(int width) + { ++ if (region_content_width == width) return; + region_content_width = width; + if (centered_region) update_position(); + } diff --git a/gui-apps/wayfire-dock/wayfire-dock-0.1.0.ebuild b/gui-apps/wayfire-dock/wayfire-dock-0.1.0.ebuild index 8b9cadfac4f0..8093e3f2769b 100644 --- a/gui-apps/wayfire-dock/wayfire-dock-0.1.0.ebuild +++ b/gui-apps/wayfire-dock/wayfire-dock-0.1.0.ebuild @@ -1,12 +1,14 @@ -# Local SrcUX release recipe. Sources retain their upstream MIT provenance. +# Local SrcUX release recipe. Sources retain their upstream MIT provenance, except +# private/src/panel/dock-material.cpp, adapted from Sendit (all rights reserved, +# no redistribution grant), so the source and binaries stay fetch-restricted. EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: dock" SRC_URI="${P}.tar.gz" -LICENSE="MIT" +LICENSE="MIT all-rights-reserved" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="bindist fetch mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.3 >=gui-libs/wayfire-ui-0.1.3 diff --git a/gui-apps/wayfire-dock/wayfire-dock-0.1.1-r1.ebuild b/gui-apps/wayfire-dock/wayfire-dock-0.1.1-r1.ebuild new file mode 100644 index 000000000000..068bf0b4bd7a --- /dev/null +++ b/gui-apps/wayfire-dock/wayfire-dock-0.1.1-r1.ebuild @@ -0,0 +1,37 @@ +# Local SrcUX release recipe. Sources retain their upstream MIT provenance, except +# private/src/panel/dock-material.cpp, adapted from Sendit (all rights reserved, +# no redistribution grant), so the source and binaries stay fetch-restricted. +EAPI=8 +inherit meson +DESCRIPTION="SrcUX components for Wayfire: dock" +SRC_URI="${P}.tar.gz" +LICENSE="MIT all-rights-reserved" +SLOT="0" +KEYWORDS="~amd64" +RESTRICT="bindist fetch mirror test" +DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 + >=gui-libs/wayfire-core-0.1.3 + >=gui-libs/wayfire-ui-0.1.3 + >=gui-libs/gtk4-layer-shell-1.3.0 + dev-libs/wayland + >=gui-libs/wf-config-0.7.0 + gui-wm/wayfire + dev-libs/glib:2 + =dev-libs/yyjson-0.13.0* + >=gui-libs/wayfire-core-0.1.8 + >=gui-libs/wayfire-ui-0.1.6 + media-libs/libepoxy +" +RDEPEND="${DEPEND} + >=gui-apps/wayfire-session-0.1.11 +" +BDEPEND="virtual/pkgconfig dev-util/wayland-scanner dev-libs/wayland-protocols" +src_configure() { + local emesonargs=( -Dtests=false ) + meson_src_configure +} +pkg_nofetch() { + einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." +} + +PATCHES=( "${FILESDIR}/${P}-hidden-position.patch" ) diff --git a/gui-apps/wayfire-dock/wayfire-dock-0.1.1.ebuild b/gui-apps/wayfire-dock/wayfire-dock-0.1.1.ebuild index 657d88fc9e3d..0e7db79bbadc 100644 --- a/gui-apps/wayfire-dock/wayfire-dock-0.1.1.ebuild +++ b/gui-apps/wayfire-dock/wayfire-dock-0.1.1.ebuild @@ -1,12 +1,14 @@ -# Local SrcUX release recipe. Sources retain their upstream MIT provenance. +# Local SrcUX release recipe. Sources retain their upstream MIT provenance, except +# private/src/panel/dock-material.cpp, adapted from Sendit (all rights reserved, +# no redistribution grant), so the source and binaries stay fetch-restricted. EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: dock" SRC_URI="${P}.tar.gz" -LICENSE="MIT" +LICENSE="MIT all-rights-reserved" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="bindist fetch mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.3 >=gui-libs/wayfire-ui-0.1.3 diff --git a/gui-apps/wayfire-file-index/wayfire-file-index-0.1.0-r1.ebuild b/gui-apps/wayfire-file-index/wayfire-file-index-0.1.0-r1.ebuild index b9dd2fe26c1a..fc940fa3e5ca 100644 --- a/gui-apps/wayfire-file-index/wayfire-file-index-0.1.0-r1.ebuild +++ b/gui-apps/wayfire-file-index/wayfire-file-index-0.1.0-r1.ebuild @@ -1,13 +1,13 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX independently supervised file catalogue for Wayfire" -SRC_URI="${PF}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${PF}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror" +RESTRICT="mirror" DEPEND="dev-libs/glib:2 =dev-libs/yyjson-0.13.0* dev-db/sqlite:3" RDEPEND="${DEPEND}" BDEPEND="virtual/pkgconfig" src_configure() { local emesonargs=( -Dtests=false ); meson_src_configure; } -pkg_nofetch() { einfo "Use the qualified local ${PF}.tar.gz."; } diff --git a/gui-apps/wayfire-files/wayfire-files-0.1.2-r4.ebuild b/gui-apps/wayfire-files/wayfire-files-0.1.2-r4.ebuild index be68e031ee25..37061634c703 100644 --- a/gui-apps/wayfire-files/wayfire-files-0.1.2-r4.ebuild +++ b/gui-apps/wayfire-files/wayfire-files-0.1.2-r4.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: files" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.7 >=gui-libs/wayfire-ui-0.1.3-r3 @@ -32,6 +33,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-files/wayfire-files-0.1.2-r6.ebuild b/gui-apps/wayfire-files/wayfire-files-0.1.2-r6.ebuild index 8f6d60736237..9ec7fc4ad582 100644 --- a/gui-apps/wayfire-files/wayfire-files-0.1.2-r6.ebuild +++ b/gui-apps/wayfire-files/wayfire-files-0.1.2-r6.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: files" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.7 >=gui-libs/wayfire-ui-0.1.3-r3 @@ -32,6 +33,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-mcp/wayfire-mcp-0.1.2.ebuild b/gui-apps/wayfire-mcp/wayfire-mcp-0.1.2.ebuild index 07ac9ce9cacb..81d4730957c8 100644 --- a/gui-apps/wayfire-mcp/wayfire-mcp-0.1.2.ebuild +++ b/gui-apps/wayfire-mcp/wayfire-mcp-0.1.2.ebuild @@ -2,11 +2,12 @@ EAPI=8 PYTHON_COMPAT=( python3_{13..14} ) inherit meson python-single-r1 DESCRIPTION="Optional local read-only MCP bridge for SrcUX Wayfire components" -SRC_URI="${P}.tar.gz wayfire-mcp-wheels-${PV}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz https://www.srcux.com/distfiles/wayfire-mcp-wheels-${PV}.tar.gz" LICENSE="MIT Apache-2.0 BSD-2 BSD ISC MPL-2.0 PSF-2" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" QA_PREBUILT="usr/libexec/wayfire-mcp/python/*.so usr/libexec/wayfire-mcp/python/*/*.so usr/libexec/wayfire-mcp/python/*/*/*/*.so" REQUIRED_USE="${PYTHON_REQUIRED_USE}" RDEPEND="${PYTHON_DEPS} >=gui-apps/wayfire-ctl-0.1.4 >=gui-apps/wayfire-session-0.1.10" @@ -21,4 +22,3 @@ src_install() { # Bundled Python distributions retain dist-info licences and provenance. find "${ED}/usr/libexec/wayfire-mcp/python" -type d -name __pycache__ -prune -exec rm -rf {} + || die } -pkg_nofetch() { einfo "Use qualified source and hash-locked wheel archives."; } diff --git a/gui-apps/wayfire-panel/wayfire-panel-0.1.0-r4.ebuild b/gui-apps/wayfire-panel/wayfire-panel-0.1.0-r4.ebuild index 50c1e45a56fb..edd87e2d225d 100644 --- a/gui-apps/wayfire-panel/wayfire-panel-0.1.0-r4.ebuild +++ b/gui-apps/wayfire-panel/wayfire-panel-0.1.0-r4.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: panel" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.3 >=gui-libs/wayfire-ui-0.1.6 @@ -41,6 +42,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-panel/wayfire-panel-0.1.0.ebuild b/gui-apps/wayfire-panel/wayfire-panel-0.1.0.ebuild index 9fabe7dfb1e0..1beac86a5629 100644 --- a/gui-apps/wayfire-panel/wayfire-panel-0.1.0.ebuild +++ b/gui-apps/wayfire-panel/wayfire-panel-0.1.0.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: panel" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.3 >=gui-libs/wayfire-ui-0.1.3 @@ -29,6 +30,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-preferences/wayfire-preferences-0.1.0-r1.ebuild b/gui-apps/wayfire-preferences/wayfire-preferences-0.1.0-r1.ebuild index a3d7ee46a64e..2c4360386f61 100644 --- a/gui-apps/wayfire-preferences/wayfire-preferences-0.1.0-r1.ebuild +++ b/gui-apps/wayfire-preferences/wayfire-preferences-0.1.0-r1.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: preferences" -SRC_URI="${PF}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${PF}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" dev-libs/glib:2 =dev-libs/yyjson-0.13.0* " @@ -18,6 +19,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${PF}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-run/wayfire-run-0.1.0-r1.ebuild b/gui-apps/wayfire-run/wayfire-run-0.1.0-r1.ebuild index 731473cb2b00..e884f50770d6 100644 --- a/gui-apps/wayfire-run/wayfire-run-0.1.0-r1.ebuild +++ b/gui-apps/wayfire-run/wayfire-run-0.1.0-r1.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: run" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.3 >=gui-libs/wayfire-ui-0.1.4 @@ -23,8 +24,5 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} PATCHES=( "${FILESDIR}/${P}-shared-ui-jobs.patch" ) diff --git a/gui-apps/wayfire-run/wayfire-run-0.1.0.ebuild b/gui-apps/wayfire-run/wayfire-run-0.1.0.ebuild index 42933b66fb90..ab4bd5e2ab93 100644 --- a/gui-apps/wayfire-run/wayfire-run-0.1.0.ebuild +++ b/gui-apps/wayfire-run/wayfire-run-0.1.0.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: run" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.3 >=gui-libs/wayfire-ui-0.1.3 @@ -23,6 +24,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-screenshot/wayfire-screenshot-0.1.0-r1.ebuild b/gui-apps/wayfire-screenshot/wayfire-screenshot-0.1.0-r1.ebuild index 351208c6f821..2675cdfe3936 100644 --- a/gui-apps/wayfire-screenshot/wayfire-screenshot-0.1.0-r1.ebuild +++ b/gui-apps/wayfire-screenshot/wayfire-screenshot-0.1.0-r1.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: screenshot" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-ui-0.1.4 dev-libs/glib:2 @@ -25,8 +26,5 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} PATCHES=( "${FILESDIR}/${P}-shared-ui-jobs.patch" ) diff --git a/gui-apps/wayfire-screenshot/wayfire-screenshot-0.1.0.ebuild b/gui-apps/wayfire-screenshot/wayfire-screenshot-0.1.0.ebuild index 610db72a38e1..638262799b61 100644 --- a/gui-apps/wayfire-screenshot/wayfire-screenshot-0.1.0.ebuild +++ b/gui-apps/wayfire-screenshot/wayfire-screenshot-0.1.0.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: screenshot" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-ui-0.1.3 dev-libs/glib:2 @@ -25,6 +26,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-session/wayfire-session-0.1.11.ebuild b/gui-apps/wayfire-session/wayfire-session-0.1.11.ebuild index c40415fce260..1e4d983998b7 100644 --- a/gui-apps/wayfire-session/wayfire-session-0.1.11.ebuild +++ b/gui-apps/wayfire-session/wayfire-session-0.1.11.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: session" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 dev-libs/glib:2 =dev-libs/yyjson-0.13.0* @@ -30,6 +31,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-session/wayfire-session-0.1.7-r6.ebuild b/gui-apps/wayfire-session/wayfire-session-0.1.7-r6.ebuild index 6d422fa7bc60..00368d8d7977 100644 --- a/gui-apps/wayfire-session/wayfire-session-0.1.7-r6.ebuild +++ b/gui-apps/wayfire-session/wayfire-session-0.1.7-r6.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: session" -SRC_URI="${PF}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${PF}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 dev-libs/glib:2 =dev-libs/yyjson-0.13.0* @@ -29,6 +30,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${PF}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wayfire-settings/wayfire-settings-0.1.4-r1.ebuild b/gui-apps/wayfire-settings/wayfire-settings-0.1.4-r1.ebuild index 7dcf1b996c20..8c8fb9d89756 100644 --- a/gui-apps/wayfire-settings/wayfire-settings-0.1.4-r1.ebuild +++ b/gui-apps/wayfire-settings/wayfire-settings-0.1.4-r1.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: settings" -SRC_URI="${PF}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${PF}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=dev-cpp/gtkmm-4.12:4.0 >=gui-libs/wayfire-core-0.1.3 >=gui-libs/wayfire-ui-0.1.3 @@ -22,6 +23,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${PF}.tar.gz in ${DISTDIR}." -} diff --git a/gui-apps/wf-kill/files/killtopview.patch b/gui-apps/wf-kill/files/killtopview.patch new file mode 100644 index 000000000000..f8e767933b9c --- /dev/null +++ b/gui-apps/wf-kill/files/killtopview.patch @@ -0,0 +1,170 @@ +diff --git a/proto/wayfire-kill-view.xml b/proto/wayfire-kill-view.xml +index 4fe0c56..8274569 100644 +--- a/proto/wayfire-kill-view.xml ++++ b/proto/wayfire-kill-view.xml +@@ -36,6 +36,12 @@ + </description> + </request> + ++ <request name="top_view_kill"> ++ <description summary="send close to the top view"> ++ Send close to the top view. ++ </description> ++ </request> ++ + <event name="view_pid"> + <description summary="Send view pid to a client"> + Provide client with a view pid. +diff --git a/src/client/wf-kill.cpp b/src/client/wf-kill.cpp +index 762b7cf..01fe1e8 100644 +--- a/src/client/wf-kill.cpp ++++ b/src/client/wf-kill.cpp +@@ -27,6 +27,7 @@ + #include <string.h> + #include <sys/types.h> + #include <signal.h> ++#include <getopt.h> + + #include "wf-kill.hpp" + +@@ -113,11 +114,43 @@ WfKill::WfKill(int argc, char *argv[]) + std::cout << "Wayfire kill view protocol not advertised by compositor. Is wf-kill plugin enabled?" << std::endl; + return; + } +- wf_kill_view_base_view_kill(wf_kill_view_manager); + wf_kill_view_base_add_listener(wf_kill_view_manager, + &kill_view_listener, this); + +- send_kill = (argc > 1 && !strcmp(argv[1], "-k")); ++ send_kill = false; ++ top_view = false; ++ struct option opts[] = { ++ { "kill-view", no_argument, NULL, 'k' }, ++ { "top-view", no_argument, NULL, 't' }, ++ { 0, 0, NULL, 0 } ++ }; ++ ++ int c, i; ++ while((c = getopt_long(argc, argv, "kt", opts, &i)) != -1) ++ { ++ switch(c) ++ { ++ case 'k': ++ send_kill = true; ++ break; ++ ++ case 't': ++ top_view = true; ++ break; ++ ++ default: ++ std::cerr << "Usage: wf-kill [-k|--kill-view] [-t|--top-view]" << std::endl; ++ return; ++ } ++ } ++ if (top_view) ++ { ++ wf_kill_view_base_top_view_kill(wf_kill_view_manager); ++ } else ++ { ++ wf_kill_view_base_view_kill(wf_kill_view_manager); ++ } ++ + running = 1; + while(running) + wl_display_dispatch(display); +diff --git a/src/client/wf-kill.hpp b/src/client/wf-kill.hpp +index 6065675..9b85064 100644 +--- a/src/client/wf-kill.hpp ++++ b/src/client/wf-kill.hpp +@@ -35,6 +35,7 @@ class WfKill + + int running; + int send_kill; ++ int top_view; + wl_display *display; + wf_kill_view_base *wf_kill_view_manager; + }; +diff --git a/src/plugin/wayfire-kill-view.cpp b/src/plugin/wayfire-kill-view.cpp +index e338778..8753a26 100644 +--- a/src/plugin/wayfire-kill-view.cpp ++++ b/src/plugin/wayfire-kill-view.cpp +@@ -29,6 +29,7 @@ + #include <wayfire/plugin.hpp> + #include <wayfire/output.hpp> + #include <wayfire/output-layout.hpp> ++#include <wayfire/seat.hpp> + #include <wayfire/nonstd/wlroots-full.hpp> + #include <linux/input-event-codes.h> + +@@ -43,13 +44,8 @@ wf::plugin_activation_data_t grab_interface{ + static void bind_manager(wl_client *client, void *data, + uint32_t version, uint32_t id); + +-void wf_kill_view::send_view_info() ++static pid_t get_pid_for_view(wayfire_view view) + { +- wayfire_view view = wf::get_core().get_cursor_focus_view(); +- if (!view) +- { +- return; +- } + pid_t pid = 0; + wlr_surface *wlr_surface = view->get_wlr_surface(); + #if WF_HAS_XWAYLAND +@@ -62,6 +58,18 @@ void wf_kill_view::send_view_info() + wl_client_get_credentials(view->get_client(), &pid, 0, 0); + } + ++ return pid; ++} ++ ++void wf_kill_view::send_view_info() ++{ ++ wayfire_view view = wf::get_core().get_cursor_focus_view(); ++ if (!view) ++ { ++ return; ++ } ++ pid_t pid = get_pid_for_view(view); ++ + view->close(); + + for (auto r : client_resources) +@@ -72,7 +80,6 @@ void wf_kill_view::send_view_info() + + void wf_kill_view::send_cancel() + { +- + for (auto r : client_resources) + { + wf_kill_view_base_send_cancel(r); +@@ -161,9 +168,28 @@ static void view_kill(struct wl_client *client, struct wl_resource *resource) + } + } + ++static void top_view_kill(struct wl_client *client, struct wl_resource *resource) ++{ ++ wf_kill_view *wd = (wf_kill_view*)wl_resource_get_user_data(resource); ++ auto view = wf::get_core().seat->get_active_view(); ++ if (!view) ++ { ++ return; ++ } ++ pid_t pid = get_pid_for_view(view); ++ ++ view->close(); ++ ++ for (auto r : wd->client_resources) ++ { ++ wf_kill_view_base_send_view_pid(r, pid); ++ } ++} ++ + static const struct wf_kill_view_base_interface wf_kill_view_impl = + { + .view_kill = view_kill, ++ .top_view_kill = top_view_kill, + }; + + static void destroy_client(wl_resource *resource) diff --git a/gui-apps/wf-kill/files/wf-kill-grab-safety.patch b/gui-apps/wf-kill/files/wf-kill-grab-safety.patch new file mode 100644 index 000000000000..39d5258ce86d --- /dev/null +++ b/gui-apps/wf-kill/files/wf-kill-grab-safety.patch @@ -0,0 +1,370 @@ +diff --git a/src/plugin/wayfire-kill-view.cpp b/src/plugin/wayfire-kill-view.cpp +index 8753a26..3c657b7 100644 +--- a/src/plugin/wayfire-kill-view.cpp ++++ b/src/plugin/wayfire-kill-view.cpp +@@ -24,6 +24,8 @@ + + + #include <sys/time.h> ++#include <unistd.h> ++#include <algorithm> + #include <wayfire/core.hpp> + #include <wayfire/view.hpp> + #include <wayfire/plugin.hpp> +@@ -44,70 +46,144 @@ wf::plugin_activation_data_t grab_interface{ + static void bind_manager(wl_client *client, void *data, + uint32_t version, uint32_t id); + +-static pid_t get_pid_for_view(wayfire_view view) ++void wf_kill_view::close_view(wayfire_view view, wl_resource *resource) + { ++ /* Compositor-owned views (e.g. the mag plugin) have no client to kill. */ ++ wl_client *view_client = view ? view->get_client() : nullptr; ++ if (!view_client) ++ { ++ send_cancel(resource); ++ return; ++ } ++ + pid_t pid = 0; +- wlr_surface *wlr_surface = view->get_wlr_surface(); + #if WF_HAS_XWAYLAND +- if (auto xwayland_surface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface)) ++ wlr_surface *wlr_surface = view->get_wlr_surface(); ++ if (auto xwayland_surface = wlr_surface ? ++ wlr_xwayland_surface_try_from_wlr_surface(wlr_surface) : nullptr) + { + pid = xwayland_surface->pid; + } else + #endif + { +- wl_client_get_credentials(view->get_client(), &pid, 0, 0); ++ wl_client_get_credentials(view_client, &pid, 0, 0); + } + +- return pid; +-} +- +-void wf_kill_view::send_view_info() +-{ +- wayfire_view view = wf::get_core().get_cursor_focus_view(); +- if (!view) ++ if (pid == getpid()) + { ++ send_cancel(resource); + return; + } +- pid_t pid = get_pid_for_view(view); + + view->close(); ++ wf_kill_view_base_send_view_pid(resource, pid); ++} + +- for (auto r : client_resources) ++void wf_kill_view::send_view_info(wl_resource *resource) ++{ ++ close_view(wf::get_core().get_cursor_focus_view(), resource); ++} ++ ++void wf_kill_view::send_cancel(wl_resource *resource) ++{ ++ wf_kill_view_base_send_cancel(resource); ++} ++ ++void wf_kill_view::release_grabs() ++{ ++ for (auto& [o, grab] : input_grabs) + { +- wf_kill_view_base_send_view_pid(r, pid); ++ grab->ungrab_input(); ++ o->deactivate_plugin(&grab_interface); + } ++ ++ input_grabs.clear(); ++ grab_active = false; + } + +-void wf_kill_view::send_cancel() ++void wf_kill_view::start_grab(wl_resource *resource) + { +- for (auto r : client_resources) ++ /* One selection at a time; replacing a live grab orphans its scene node. */ ++ if (grab_active || requester) ++ { ++ send_cancel(resource); ++ return; ++ } ++ ++ for (auto& o : wf::get_core().output_layout->get_outputs()) ++ { ++ if (!o->activate_plugin(&grab_interface)) ++ { ++ continue; ++ } ++ ++ auto grab = std::make_unique<wf::input_grab_t>(grab_interface.name, o, nullptr, base, nullptr); ++ grab->grab_input(wf::scene::layer::OVERLAY); ++ input_grabs[o] = std::move(grab); ++ } ++ ++ if (input_grabs.empty()) + { +- wf_kill_view_base_send_cancel(r); ++ send_cancel(resource); ++ return; + } ++ ++ grab_active = true; ++ requester = resource; ++ idle_set_cursor.run_once([] () ++ { ++ wf::get_core().set_cursor("pirate"); ++ }); + } + + void wf_kill_view::deactivate(uint32_t b) + { +- for (auto& o : wf::get_core().output_layout->get_outputs()) ++ if (!grab_active) + { +- o->deactivate_plugin(&grab_interface); +- input_grabs[o]->ungrab_input(); +- input_grabs[o].reset(); ++ return; + } + ++ release_grabs(); ++ + idle_set_cursor.run_once([=] () + { +- if (b == BTN_LEFT) ++ /* The requester may have disconnected before the idle callback. */ ++ auto resource = requester; ++ requester = nullptr; ++ if (!resource) + { +- send_view_info(); ++ return; + } +- else ++ ++ if (b == BTN_LEFT) + { +- send_cancel(); ++ send_view_info(resource); ++ } else ++ { ++ send_cancel(resource); + } + }); + } + ++void wf_kill_view::client_destroyed(wl_resource *resource) ++{ ++ client_resources.erase(std::remove(client_resources.begin(), ++ client_resources.end(), resource), client_resources.end()); ++ ++ if (resource == requester) ++ { ++ /* Without its client the selection must not close anything. */ ++ if (grab_active) ++ { ++ release_grabs(); ++ idle_set_cursor.disconnect(); ++ wf::get_core().set_cursor("default"); ++ } ++ ++ requester = nullptr; ++ } ++} ++ + void wf_kill_view::end_grab(uint32_t b) + { + deactivate(b); +@@ -128,83 +204,95 @@ wf_kill_view::wf_kill_view() + LOGE("Failed to create wf_kill_view interface"); + return; + } ++ ++ on_output_pre_remove = [=] (wf::output_pre_remove_signal *ev) ++ { ++ auto it = input_grabs.find(ev->output); ++ if (it == input_grabs.end()) ++ { ++ return; ++ } ++ ++ it->second->ungrab_input(); ++ ev->output->deactivate_plugin(&grab_interface); ++ input_grabs.erase(it); ++ if (input_grabs.empty() && grab_active) ++ { ++ grab_active = false; ++ idle_set_cursor.disconnect(); ++ if (requester) ++ { ++ send_cancel(requester); ++ requester = nullptr; ++ } ++ } ++ }; ++ wf::get_core().output_layout->connect(&on_output_pre_remove); + } + + wf_kill_view::~wf_kill_view() + { +- wl_global_destroy(manager); +- +- for (auto& o : wf::get_core().output_layout->get_outputs()) ++ if (manager) + { +- input_grabs[o].reset(); ++ wl_global_destroy(manager); + } +-} +- +-static void view_kill(struct wl_client *client, struct wl_resource *resource) +-{ +- wf_kill_view *wd = (wf_kill_view*)wl_resource_get_user_data(resource); + +- bool set_cursor = false; +- for (auto& o : wf::get_core().output_layout->get_outputs()) ++ if (grab_active) + { +- wd->input_grabs[o] = std::make_unique<wf::input_grab_t> (grab_interface.name, o, nullptr, wd->base, nullptr); +- +- if (!o->activate_plugin(&grab_interface)) +- { +- continue; +- } +- +- wd->input_grabs[o]->grab_input(wf::scene::layer::OVERLAY); ++ release_grabs(); ++ wf::get_core().set_cursor("default"); ++ } + +- set_cursor = true; ++ if (requester) ++ { ++ send_cancel(requester); + } + +- if (set_cursor) ++ /* Resources outlive the plugin; detach them so later requests and ++ * destructors do not touch freed memory. */ ++ for (auto r : client_resources) + { +- wd->idle_set_cursor.run_once([wd] () +- { +- wf::get_core().set_cursor("pirate"); +- }); ++ wl_resource_set_user_data(r, nullptr); + } + } + +-static void top_view_kill(struct wl_client *client, struct wl_resource *resource) ++static void view_kill(struct wl_client *client, struct wl_resource *resource) + { + wf_kill_view *wd = (wf_kill_view*)wl_resource_get_user_data(resource); +- auto view = wf::get_core().seat->get_active_view(); +- if (!view) ++ if (!wd) + { ++ wf_kill_view_base_send_cancel(resource); + return; + } +- pid_t pid = get_pid_for_view(view); + +- view->close(); ++ wd->start_grab(resource); ++} + +- for (auto r : wd->client_resources) ++static void top_view_kill(struct wl_client *client, struct wl_resource *resource) ++{ ++ wf_kill_view *wd = (wf_kill_view*)wl_resource_get_user_data(resource); ++ if (!wd || wd->grab_active || wd->requester) + { +- wf_kill_view_base_send_view_pid(r, pid); ++ wf_kill_view_base_send_cancel(resource); ++ return; + } ++ ++ wd->close_view(wf::get_core().seat->get_active_view(), resource); + } + + static const struct wf_kill_view_base_interface wf_kill_view_impl = + { +- .view_kill = view_kill, ++ .view_kill = view_kill, + .top_view_kill = top_view_kill, + }; + + static void destroy_client(wl_resource *resource) + { + wf_kill_view *wd = (wf_kill_view*)wl_resource_get_user_data(resource); +- +- for (auto& r : wd->client_resources) ++ if (wd) + { +- if (r == resource) +- { +- r = nullptr; +- } ++ wd->client_destroyed(resource); + } +- wd->client_resources.erase(std::remove(wd->client_resources.begin(), +- wd->client_resources.end(), nullptr), wd->client_resources.end()); + } + + static void bind_manager(wl_client *client, void *data, +@@ -214,8 +302,13 @@ static void bind_manager(wl_client *client, void *data, + + auto resource = + wl_resource_create(client, &wf_kill_view_base_interface, 1, id); ++ if (!resource) ++ { ++ wl_client_post_no_memory(client); ++ return; ++ } ++ + wl_resource_set_implementation(resource, + &wf_kill_view_impl, data, destroy_client); + wd->client_resources.push_back(resource); +- + } +diff --git a/src/plugin/wayfire-kill-view.hpp b/src/plugin/wayfire-kill-view.hpp +index f2ed946..b7e5696 100644 +--- a/src/plugin/wayfire-kill-view.hpp ++++ b/src/plugin/wayfire-kill-view.hpp +@@ -25,6 +25,8 @@ + #pragma once + + #include <wayfire/plugins/common/input-grab.hpp> ++#include <wayfire/output-layout.hpp> ++#include <wayfire/view.hpp> + + class wf_kill_view + { +@@ -34,11 +36,19 @@ class wf_kill_view + wf::pointer_interaction_t *base; + void set_base_ptr(wf::pointer_interaction_t *base); + std::vector<wl_resource*> client_resources; +- void send_view_info(); +- void send_cancel(); ++ /* Client that issued the pending view_kill; replies go only to it. */ ++ wl_resource *requester = nullptr; ++ bool grab_active = false; ++ void start_grab(wl_resource *resource); ++ void release_grabs(); ++ void client_destroyed(wl_resource *resource); ++ void close_view(wayfire_view view, wl_resource *resource); ++ void send_view_info(wl_resource *resource); ++ void send_cancel(wl_resource *resource); + void deactivate(uint32_t b); + wf::wl_idle_call idle_set_cursor; + std::map<wf::output_t*, std::unique_ptr<wf::input_grab_t>> input_grabs; ++ wf::signal::connection_t<wf::output_pre_remove_signal> on_output_pre_remove; + void end_grab(uint32_t b); + wf_kill_view(); + ~wf_kill_view(); diff --git a/gui-apps/wf-kill/wf-kill-9999-r1.ebuild b/gui-apps/wf-kill/wf-kill-9999-r1.ebuild new file mode 100644 index 000000000000..8919d016b186 --- /dev/null +++ b/gui-apps/wf-kill/wf-kill-9999-r1.ebuild @@ -0,0 +1,40 @@ +# Copyright 2019-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit git-r3 meson + +DESCRIPTION="A simple wayfire plugin and program to get information from wayfire." +HOMEPAGE="https://github.com/soreau/wf-kill" +EGIT_REPO_URI="https://github.com/soreau/wf-kill.git" +# Pinned so the downstream patches stay reproducible. +EGIT_COMMIT="361958b0d916510e2c07562dd56ab9d77c40092a" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="" + +DEPEND=" + dev-libs/wayland + dev-libs/wayland-protocols + gui-libs/wf-config:= + gui-libs/wlroots:0.20= + gui-wm/wayfire:= +" +RDEPEND="${DEPEND}" +BDEPEND=" + dev-util/wayland-scanner + virtual/pkgconfig +" + +PATCHES=( + "${FILESDIR}/killtopview.patch" + "${FILESDIR}/${PN}-grab-safety.patch" +) + +src_install() { + meson_src_install + exeinto "/usr/$(get_libdir)/wayfire/wf-kill-r1" + doexe "${BUILD_DIR}/src/libwf-kill.so" +} diff --git a/gui-apps/wf-kill/wf-kill-9999.ebuild b/gui-apps/wf-kill/wf-kill-9999.ebuild deleted file mode 100644 index b3ca1a20d747..000000000000 --- a/gui-apps/wf-kill/wf-kill-9999.ebuild +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2019 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=7 - -inherit git-r3 meson - -DESCRIPTION="A simple wayfire plugin and program to get information from wayfire." -HOMEPAGE="https://github.com/soreau/wf-kill" -EGIT_REPO_URI="https://github.com/soreau/wf-kill.git" - -LICENSE="GPL-3" -SLOT="0" -KEYWORDS="" - -DEPEND=" - dev-libs/wayland - dev-libs/wayland-protocols - gui-libs/wlroots:0.20 - gui-wm/wayfire:= -" -RDEPEND="${DEPEND}" - -PATCHES=( - "${FILESDIR}/killtopview.patch" -) diff --git a/gui-apps/wfctl/wfctl-0.2.0.ebuild b/gui-apps/wfctl/wfctl-0.2.0.ebuild index 9508c117aea5..bca4d36274d8 100644 --- a/gui-apps/wfctl/wfctl-0.2.0.ebuild +++ b/gui-apps/wfctl/wfctl-0.2.0.ebuild @@ -6,11 +6,12 @@ PYTHON_COMPAT=( python3_{13..14} ) inherit meson python-single-r1 DESCRIPTION="Unified Wayfire inspection and control with wf-info and wf-ctrl compatibility" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror" +RESTRICT="mirror" REQUIRED_USE="${PYTHON_REQUIRED_USE}" RDEPEND=" @@ -26,9 +27,6 @@ src_install() { python_fix_shebang "${ED}/usr/bin/wfctl" } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} pkg_postinst() { einfo "wfctl owns the wf-info and wf-ctrl compatibility commands." diff --git a/gui-libs/pixdecor/files/pixdecor-render-hook-tracking.patch b/gui-libs/pixdecor/files/pixdecor-render-hook-tracking.patch new file mode 100644 index 000000000000..867bfe8105b5 --- /dev/null +++ b/gui-libs/pixdecor/files/pixdecor-render-hook-tracking.patch @@ -0,0 +1,130 @@ +--- a/src/decoration.cpp ++++ b/src/decoration.cpp +@@ -24,6 +24,7 @@ + #include <wayfire/plugins/ipc/ipc-method-repository.hpp> + #include <wayfire/util.hpp> + #include <chrono> ++#include <set> + #include "shell-theme.hpp" + + namespace wf +@@ -92,7 +93,31 @@ + wf::wl_idle_call idle_update_views; + std::function<void(void)> update_event; + wf::effect_hook_t pre_hook; +- bool hook_set = false; ++ /* Outputs that currently have pre_hook registered, tracked per-output so ++ * hotplug and option changes cannot desync a single global flag. */ ++ std::set<wf::output_t*> hook_outputs; ++ ++ bool effect_needs_hook() ++ { ++ return effect_animate || (std::string(effect_type) == "smoke") || ++ (std::string(effect_type) == "ink"); ++ } ++ ++ void add_pre_hook(wf::output_t *o) ++ { ++ if (hook_outputs.insert(o).second) ++ { ++ o->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); ++ } ++ } ++ ++ void remove_pre_hook(wf::output_t *o) ++ { ++ if (hook_outputs.erase(o)) ++ { ++ o->render->rem_effect(&pre_hook); ++ } ++ } + + wf::axis_callback shade_axis_cb; + +@@ -170,16 +195,16 @@ + wf::signal::connection_t<wf::output_added_signal> on_output_added = + [=] (wf::output_added_signal *ev) + { +- if (effect_animate || (std::string(effect_type) == "smoke") || (std::string(effect_type) == "ink")) ++ if (effect_needs_hook()) + { +- ev->output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); ++ add_pre_hook(ev->output); + } + }; + + wf::signal::connection_t<wf::output_removed_signal> on_output_removed = + [=] (wf::output_removed_signal *ev) + { +- ev->output->render->rem_effect(&pre_hook); ++ remove_pre_hook(ev->output); + }; + + void pop_transformer(wayfire_view view) +@@ -473,14 +498,12 @@ + } + }; + +- if (std::string(effect_type) != "none") ++ if (effect_needs_hook()) + { + for (auto& o : wf::get_core().output_layout->get_outputs()) + { +- o->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); ++ add_pre_hook(o); + } +- +- hook_set = true; + } + + titlebar.set_callback([=] {recreate_frames();}); +@@ -630,13 +653,11 @@ + } + } + +- if (hook_set) ++ for (auto& o : wf::get_core().output_layout->get_outputs()) + { +- for (auto& o : wf::get_core().output_layout->get_outputs()) +- { +- o->render->rem_effect(&pre_hook); +- } ++ remove_pre_hook(o); + } ++ hook_outputs.clear(); + + on_output_added.disconnect(); + on_output_removed.disconnect(); +@@ -676,27 +697,17 @@ + + void option_changed_cb(bool resize_decorations, bool recreate_decorations) + { +- if (effect_animate || (std::string(effect_type) == "smoke") || (std::string(effect_type) == "ink")) ++ if (effect_needs_hook()) + { +- if (!hook_set) ++ for (auto& o : wf::get_core().output_layout->get_outputs()) + { +- for (auto& o : wf::get_core().output_layout->get_outputs()) +- { +- o->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); +- } +- +- hook_set = true; ++ add_pre_hook(o); + } + } else + { +- if (hook_set) ++ for (auto& o : wf::get_core().output_layout->get_outputs()) + { +- for (auto& o : wf::get_core().output_layout->get_outputs()) +- { +- o->render->rem_effect(&pre_hook); +- } +- +- hook_set = false; ++ remove_pre_hook(o); + } + } + diff --git a/gui-libs/pixdecor/files/pixdecor-shade-deco-lifetime.patch b/gui-libs/pixdecor/files/pixdecor-shade-deco-lifetime.patch new file mode 100644 index 000000000000..cd98d36c431b --- /dev/null +++ b/gui-libs/pixdecor/files/pixdecor-shade-deco-lifetime.patch @@ -0,0 +1,18 @@ +--- a/src/shade.hpp ++++ b/src/shade.hpp +@@ -167,10 +167,13 @@ + { + if (auto toplevel = wf::toplevel_cast(view)) + { +- if (deco) ++ /* Re-resolve the decorator each frame: a cached observer_ptr dangles ++ * if the decoration is removed/recreated while the shade runs. */ ++ auto live_deco = toplevel->toplevel()->get_data<simple_decorator_t>(); ++ if (live_deco) + { + /* SSD */ +- deco->get_margins(toplevel->toplevel()->pending()); ++ live_deco->get_margins(toplevel->toplevel()->pending()); + } else + { + /* CSD */ diff --git a/gui-libs/pixdecor/files/pixdecor-shade-output-lifetime.patch b/gui-libs/pixdecor/files/pixdecor-shade-output-lifetime.patch new file mode 100644 index 000000000000..0ae4bd78f0b0 --- /dev/null +++ b/gui-libs/pixdecor/files/pixdecor-shade-output-lifetime.patch @@ -0,0 +1,122 @@ +--- a/src/shade.hpp ++++ b/src/shade.hpp +@@ -4,6 +4,7 @@ + #include <cmath> + + #include <wayfire/output.hpp> ++#include <wayfire/output-layout.hpp> + #include <wayfire/opengl.hpp> + #include <wayfire/core.hpp> + #include <wayfire/view-transform.hpp> +@@ -34,7 +35,11 @@ + { + nonstd::observer_ptr<simple_decorator_t> deco = nullptr; + wayfire_view view; +- wf::output_t *output; ++ /* The output that currently holds pre_hook. Re-resolved from the view and ++ * migrated when the view changes output, and detached (while the output is ++ * still alive) when that output is removed, so a cached pointer can never ++ * dangle into a rem_effect() use-after-free. */ ++ wf::output_t *hook_output = nullptr; + int titlebar_height; + wf::option_wrapper_t<wf::animation_description_t> shade_duration{"pixdecor/shade_duration"}; + +@@ -136,13 +141,10 @@ + pixdecor_shade(wayfire_view view, int titlebar_height) : wf::scene::view_2d_transformer_t(view) + { + this->view = view; +- this->output = view->get_output(); + this->titlebar_height = titlebar_height; + this->progression.shade.set(0.0, 1.0); +- if (output) +- { +- output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); +- } ++ set_hook_output(view->get_output()); ++ wf::get_core().output_layout->connect(&on_output_removed); + + if (auto toplevel = wf::toplevel_cast(view)) + { +@@ -163,8 +165,55 @@ + } + } + ++ /* Move pre_hook onto output o (which may be null), removing it from whatever ++ * output currently holds it. All add/rem of pre_hook goes through here so the ++ * tracked output stays in sync and is only ever a live pointer. */ ++ void set_hook_output(wf::output_t *o) ++ { ++ if (hook_output == o) ++ { ++ return; ++ } ++ ++ if (hook_output) ++ { ++ hook_output->render->rem_effect(&pre_hook); ++ } ++ ++ hook_output = o; ++ if (hook_output) ++ { ++ hook_output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); ++ } ++ } ++ ++ wf::signal::connection_t<wf::output_removed_signal> on_output_removed = ++ [=] (wf::output_removed_signal *ev) ++ { ++ if (ev->output != hook_output) ++ { ++ return; ++ } ++ ++ /* Detach while the output is still alive, then re-home onto the view's ++ * current output if it has already migrated, so the animation can finish ++ * instead of leaving a stale hook (or dangling pointer) behind. */ ++ hook_output->render->rem_effect(&pre_hook); ++ hook_output = nullptr; ++ if (auto o = view ? view->get_output() : nullptr; o && (o != ev->output)) ++ { ++ set_hook_output(o); ++ } ++ }; ++ + wf::effect_hook_t pre_hook = [=] () + { ++ /* Follow the view if it has moved to another output since last frame. */ ++ if (view && view->get_output() && (view->get_output() != hook_output)) ++ { ++ set_hook_output(view->get_output()); ++ } ++ + if (auto toplevel = wf::toplevel_cast(view)) + { + /* Re-resolve the decorator each frame: a cached observer_ptr dangles +@@ -202,11 +251,7 @@ + view->damage(); + if (!this->progression.running() && !last_direction) + { +- if (output) +- { +- output->render->rem_effect(&pre_hook); +- } +- ++ set_hook_output(nullptr); + pop_transformer(view); + } + }; +@@ -282,10 +327,9 @@ + + virtual ~pixdecor_shade() + { +- if (output) +- { +- output->render->rem_effect(&pre_hook); +- } ++ /* hook_output is kept valid (nulled when its output is removed), so this ++ * detaches cleanly instead of dereferencing a freed output. */ ++ set_hook_output(nullptr); + } + }; + } diff --git a/gui-libs/pixdecor/files/pixdecor-theme-ipc-validate.patch b/gui-libs/pixdecor/files/pixdecor-theme-ipc-validate.patch new file mode 100644 index 000000000000..2745ce81b154 --- /dev/null +++ b/gui-libs/pixdecor/files/pixdecor-theme-ipc-validate.patch @@ -0,0 +1,13 @@ +diff --git a/src/decoration.cpp b/src/decoration.cpp +index 6f806f8..f968de4 100644 +--- a/src/decoration.cpp ++++ b/src/decoration.cpp +@@ -308,7 +308,7 @@ class wayfire_pixdecor : public wf::plugin_interface_t + theme_methods->register_method("pixdecor/theme-configure", + [this] (wf::json_t data, wf::ipc::client_interface_t *client) + { +- if (!client || !data["version"].is_int64() || data["version"].as_int64() != 1 || ++ if (!client || !data.is_object() || !data["version"].is_int64() || data["version"].as_int64() != 1 || + !data["instance"].is_string() || data["instance"].as_string() != theme_instance || + (theme_owner && theme_owner != client)) + return wf::ipc::json_error("Unavailable theme owner or instance"); diff --git a/gui-libs/pixdecor/pixdecor-9999-r4.ebuild b/gui-libs/pixdecor/pixdecor-9999-r4.ebuild new file mode 100644 index 000000000000..460d2850c094 --- /dev/null +++ b/gui-libs/pixdecor/pixdecor-9999-r4.ebuild @@ -0,0 +1,64 @@ +# Copyright 2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit git-r3 meson + +DESCRIPTION="Wayfire decorator with rounded corners, shadows, and animated effects" +HOMEPAGE="https://github.com/soreau/pixdecor" +EGIT_REPO_URI="https://github.com/soreau/pixdecor.git" +EGIT_BRANCH="master" +# Keep the downstream patches reproducible until they are promoted upstream. +EGIT_COMMIT="222e61d971b045a821cf212a6e221367c05d966b" + +PATCHES=( + "${FILESDIR}/${PN}-sculpted-frames.patch" + "${FILESDIR}/${PN}-shell-theme.patch" + "${FILESDIR}/${PN}-shade-input.patch" + "${FILESDIR}/${PN}-render-hook-tracking.patch" + "${FILESDIR}/${PN}-shade-deco-lifetime.patch" + "${FILESDIR}/${PN}-shade-output-lifetime.patch" + "${FILESDIR}/${PN}-theme-ipc-validate.patch" +) + +LICENSE="MIT" +SLOT="0" +# Upstream has no test suite. +RESTRICT="test" + +RDEPEND=" + dev-libs/glib:2 + dev-libs/wayland + gui-libs/wf-config:= + gui-libs/wlroots:0.20= + >=gui-wm/wayfire-0.11.0:=[gles3] + media-libs/libglvnd + x11-libs/cairo + x11-libs/pango + x11-libs/pixman +" +DEPEND=" + ${RDEPEND} + media-libs/glm + sys-kernel/linux-headers +" +BDEPEND=" + sys-devel/gettext + virtual/pkgconfig +" + +src_install() { + meson_src_install + exeinto "/usr/$(get_libdir)/wayfire/pixdecor-shell-r4" + doexe "${BUILD_DIR}/src/libpixdecor.so" + dodoc README.md +} + +pkg_postinst() { + elog "Add pixdecor to the plugins list in the [core] section of wayfire.ini." + elog "Disable other decoration plugins; Pixdecor owns window frames." + elog "Frame settings are in WCM -> Pix Decoration -> Window frames." + elog "Sculpted adds border depth and pinstripe color/opacity." + elog "This revision installs to pixdecor-shell-r4; point wayfire.ini at it." +} diff --git a/gui-libs/wayfire-core/wayfire-core-0.1.7.ebuild b/gui-libs/wayfire-core/wayfire-core-0.1.7.ebuild index 4a59dff2fb39..4b84f4364dd6 100644 --- a/gui-libs/wayfire-core/wayfire-core-0.1.7.ebuild +++ b/gui-libs/wayfire-core/wayfire-core-0.1.7.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: core" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" dev-libs/glib:2 =dev-libs/yyjson-0.13.0* " @@ -18,6 +19,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-libs/wayfire-core/wayfire-core-0.1.8.ebuild b/gui-libs/wayfire-core/wayfire-core-0.1.8.ebuild index 4a59dff2fb39..4b84f4364dd6 100644 --- a/gui-libs/wayfire-core/wayfire-core-0.1.8.ebuild +++ b/gui-libs/wayfire-core/wayfire-core-0.1.8.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: core" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" dev-libs/glib:2 =dev-libs/yyjson-0.13.0* " @@ -18,6 +19,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-libs/wayfire-plugins-extra/files/wayfire-plugins-extra-0.11.2-join-views-multi-view.patch b/gui-libs/wayfire-plugins-extra/files/wayfire-plugins-extra-0.11.2-join-views-multi-view.patch new file mode 100644 index 000000000000..9689ad1ab60a --- /dev/null +++ b/gui-libs/wayfire-plugins-extra/files/wayfire-plugins-extra-0.11.2-join-views-multi-view.patch @@ -0,0 +1,89 @@ +--- a/src/join-views.cpp ++++ b/src/join-views.cpp +@@ -1,3 +1,5 @@ ++#include <map> ++#include <memory> + #include <wayfire/signal-definitions.hpp> + #include <wayfire/per-output-plugin.hpp> + #include <wayfire/plugins/common/shared-core-data.hpp> +@@ -5,41 +7,62 @@ + + class JoinViewsSingleton + { ++ /* One geometry-changed connection per mapped view, so every parent keeps ++ * positioning its children -- not just the most recently mapped view. */ ++ using geometry_conn_t = wf::signal::connection_t<wf::view_geometry_changed_signal>; ++ std::map<wf::view_interface_t*, std::shared_ptr<geometry_conn_t>> geometry_conns; ++ ++ void reposition_children(wayfire_toplevel_view view) ++ { ++ if (!view || !view->is_mapped()) ++ { ++ return; ++ } ++ ++ auto parent_geometry = view->get_pending_geometry(); ++ int cx = parent_geometry.x + parent_geometry.width / 2; ++ int cy = parent_geometry.y + parent_geometry.height / 2; ++ for (auto child : view->children) ++ { ++ auto target = child->get_pending_geometry(); ++ target.x = cx - target.width / 2; ++ target.y = cy - target.height / 2; ++ child->set_geometry(target); ++ } ++ } ++ + public: +- wf::signal::connection_t<wf::view_geometry_changed_signal> on_geometry_changed{[= +- ] (wf::view_geometry_changed_signal *ev) ++ wf::signal::connection_t<wf::view_mapped_signal> on_view_map{[=] (wf::view_mapped_signal *ev) + { +- auto view = wf::toplevel_cast(ev->view); +- if (!view || !view->is_mapped()) ++ auto view = ev->view; ++ if (!view || geometry_conns.count(view.get())) + { + return; + } + +- auto parent_geometry = view->get_pending_geometry(); +- int cx = parent_geometry.x + parent_geometry.width / 2; +- int cy = parent_geometry.y + parent_geometry.height / 2; +- for (auto child : view->children) ++ auto conn = std::make_shared<geometry_conn_t>( ++ [=] (wf::view_geometry_changed_signal *gev) + { +- auto target = child->get_pending_geometry(); +- target.x = cx - target.width / 2; +- target.y = cy - target.height / 2; +- child->set_geometry(target); +- } ++ reposition_children(gev->view); ++ }); ++ view->connect(conn.get()); ++ geometry_conns[view.get()] = conn; + } + }; + +- wf::signal::connection_t<wf::view_mapped_signal> on_view_map{[=] (wf::view_mapped_signal *ev) ++ wf::signal::connection_t<wf::view_unmapped_signal> on_view_unmap{[=] (wf::view_unmapped_signal *ev) + { +- auto view = ev->view; +- /* Make sure only a single connection is made */ +- on_geometry_changed.disconnect(); +- view->connect(&on_geometry_changed); ++ if (ev->view) ++ { ++ geometry_conns.erase(ev->view.get()); ++ } + } + }; + + void handle_new_output(wf::output_t *output) + { + output->connect(&on_view_map); ++ output->connect(&on_view_unmap); + } + + JoinViewsSingleton() diff --git a/gui-libs/wayfire-plugins-extra/wayfire-plugins-extra-0.11.2-r4.ebuild b/gui-libs/wayfire-plugins-extra/wayfire-plugins-extra-0.11.2-r4.ebuild new file mode 100644 index 000000000000..449b2a072ce3 --- /dev/null +++ b/gui-libs/wayfire-plugins-extra/wayfire-plugins-extra-0.11.2-r4.ebuild @@ -0,0 +1,65 @@ +# Copyright 2019-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit meson + +DESCRIPTION="extra plugins for wayfire" +HOMEPAGE="https://github.com/WayfireWM/wayfire-plugins-extra" + +if [[ ${PV} == 9999 ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/WayfireWM/wayfire-plugins-extra.git" + SLOT="0/0.12" +else + SRC_URI="https://github.com/WayfireWM/wayfire-plugins-extra/releases/download/v${PV}/${P}.tar.xz" + KEYWORDS="~amd64 ~arm64" + SLOT="0/$(ver_cut 1-2)" +fi + +LICENSE="MIT" + +# No upstream suite; private integration fixtures are in tests/force-fullscreen-hotplug. +RESTRICT="test" + +WAYFIRE_REVDEP=" + dev-libs/glib:2 + dev-libs/libsigc++:2 + gui-libs/wf-config:= + gui-libs/wlroots:0.20 + x11-libs/cairo +" + +DEPEND=" + ${WAYFIRE_REVDEP} + dev-cpp/glibmm:2 + dev-libs/boost + dev-libs/libevdev + dev-libs/wayland + >=gui-wm/wayfire-0.11:= + media-libs/glm + media-libs/libglvnd +" +RDEPEND="${DEPEND}" +BDEPEND=" + >=dev-libs/wayland-protocols-1.12 + dev-util/wayland-scanner + sys-devel/gettext + virtual/pkgconfig +" + +PATCHES=( + "${FILESDIR}/wayfire-plugins-extra-0.11.0-workspace-focus.patch" + "${FILESDIR}/wayfire-plugins-extra-0.11.0-force-fullscreen-teardown.patch" + "${FILESDIR}/wayfire-plugins-extra-0.11.2-force-fullscreen-physical-scope.patch" + "${FILESDIR}/wayfire-plugins-extra-0.11.2-join-views-multi-view.patch" +) + +src_install() { + meson_src_install + + # A distinct library path permits replacement while the old plugin is cached. + exeinto "/usr/$(get_libdir)/wayfire/force-fullscreen-physical-r3" + doexe "${BUILD_DIR}/src/libforce-fullscreen.so" +} diff --git a/gui-libs/wayfire-ui/wayfire-ui-0.1.3-r3.ebuild b/gui-libs/wayfire-ui/wayfire-ui-0.1.3-r3.ebuild index 36c95002ad58..428915ab0577 100644 --- a/gui-libs/wayfire-ui/wayfire-ui-0.1.3-r3.ebuild +++ b/gui-libs/wayfire-ui/wayfire-ui-0.1.3-r3.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: ui" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=gui-libs/wayfire-core-0.1.3 dev-libs/glib:2 =dev-libs/yyjson-0.13.0* @@ -21,6 +22,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-libs/wayfire-ui/wayfire-ui-0.1.6.ebuild b/gui-libs/wayfire-ui/wayfire-ui-0.1.6.ebuild index a0afec5635f9..5bb664048b0e 100644 --- a/gui-libs/wayfire-ui/wayfire-ui-0.1.6.ebuild +++ b/gui-libs/wayfire-ui/wayfire-ui-0.1.6.ebuild @@ -2,11 +2,12 @@ EAPI=8 inherit meson DESCRIPTION="SrcUX components for Wayfire: ui" -SRC_URI="${P}.tar.gz" +HOMEPAGE="https://www.srcux.com/" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" LICENSE="MIT" SLOT="0" KEYWORDS="~amd64" -RESTRICT="fetch mirror test" +RESTRICT="mirror test" DEPEND=" >=gui-libs/wayfire-core-0.1.3 dev-libs/glib:2 =dev-libs/yyjson-0.13.0* @@ -20,6 +21,3 @@ src_configure() { local emesonargs=( -Dtests=false ) meson_src_configure } -pkg_nofetch() { - einfo "Place the qualified local ${P}.tar.gz in ${DISTDIR}." -} diff --git a/gui-wm/wayfire/files/wayfire-0.11.0-ipc-backpressure.patch b/gui-wm/wayfire/files/wayfire-0.11.0-ipc-backpressure.patch new file mode 100644 index 000000000000..8d787b5590fa --- /dev/null +++ b/gui-wm/wayfire/files/wayfire-0.11.0-ipc-backpressure.patch @@ -0,0 +1,166 @@ +--- a/plugins/ipc/ipc.hpp ++++ b/plugins/ipc/ipc.hpp +@@ -33,6 +33,18 @@ + /** Handle incoming data on the socket */ + std::function<void(uint32_t)> handle_fd_activity; + void handle_fd_incoming(uint32_t); ++ ++ /** Outgoing bytes not yet written because the reader is slow/backpressured. */ ++ std::vector<char> out_buffer; ++ size_t out_sent = 0; ++ bool want_writable = false; ++ ++ /** Append bytes to the outgoing queue. */ ++ void queue_output(const char *data, size_t size); ++ /** Try to drain the outgoing queue; arms a writable watch on EAGAIN. */ ++ void flush_output(); ++ /** Toggle WL_EVENT_WRITABLE interest on the client's event source. */ ++ void set_writable_watch(bool on); + }; + + /** +--- a/plugins/ipc/ipc.cpp ++++ b/plugins/ipc/ipc.cpp +@@ -10,6 +10,7 @@ + #include <sys/ioctl.h> + #include <unistd.h> + #include <cstring> ++#include <cerrno> + + /** + * Handle WL_EVENT_READABLE on the socket. +@@ -147,6 +148,7 @@ + + static constexpr int MAX_MESSAGE_LEN = (1 << 20); + static constexpr int HEADER_LEN = 4; ++static constexpr size_t MAX_OUTPUT_QUEUE = 16u * 1024 * 1024; + + wf::ipc::client_t::client_t(server_t *ipc, int fd) + { +@@ -205,6 +207,11 @@ + return; + } + ++ if (event_mask & WL_EVENT_WRITABLE) ++ { ++ flush_output(); ++ } ++ + int available = 0; + if (ioctl(this->fd, FIONREAD, &available) != 0) + { +@@ -291,47 +298,95 @@ + close(this->fd); + } + +-static bool write_exact(int fd, const char *buf, ssize_t n) ++void wf::ipc::client_t::queue_output(const char *data, size_t size) ++{ ++ out_buffer.insert(out_buffer.end(), data, data + size); ++} ++ ++void wf::ipc::client_t::set_writable_watch(bool on) ++{ ++ if (on == want_writable) ++ { ++ return; ++ } ++ ++ want_writable = on; ++ wl_event_source_fd_update(source, ++ WL_EVENT_READABLE | (on ? WL_EVENT_WRITABLE : 0)); ++} ++ ++void wf::ipc::client_t::flush_output() + { +- while (n > 0) ++ while (out_sent < out_buffer.size()) + { +- ssize_t w = write(fd, buf, n); +- if (w <= 0) ++ ssize_t w = write(fd, out_buffer.data() + out_sent, out_buffer.size() - out_sent); ++ if (w > 0) + { +- return false; ++ out_sent += (size_t)w; ++ continue; ++ } ++ ++ if ((w < 0) && (errno == EINTR)) ++ { ++ continue; + } + +- n -= w; +- buf += w; ++ if ((w < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) ++ { ++ // Reader is slow: keep the remainder queued and flush when writable ++ // instead of dropping the connection on transient backpressure. ++ set_writable_watch(true); ++ return; ++ } ++ ++ LOGE("Error sending json to client!"); ++ shutdown(fd, SHUT_RDWR); ++ out_buffer.clear(); ++ out_sent = 0; ++ set_writable_watch(false); ++ return; + } + +- return true; ++ out_buffer.clear(); ++ out_sent = 0; ++ set_writable_watch(false); + } + + bool wf::ipc::client_t::send_json(wf::json_t json) + { +- bool status = false; ++ bool status = true; + json.map_serialized([&] (const char *buffer, size_t size) + { + if (size > MAX_MESSAGE_LEN) + { + LOGE("Error sending json to client: message too long!"); +- shutdown(fd, SHUT_RDWR); ++ status = false; + return; + } + + uint32_t len = size; +- if (!write_exact(fd, (char*)&len, 4) || !write_exact(fd, buffer, len)) +- { +- LOGE("Error sending json to client!"); +- shutdown(fd, SHUT_RDWR); +- return; +- } +- +- status = true; ++ queue_output((const char*)&len, sizeof(len)); ++ queue_output(buffer, size); + }); + +- return status; ++ if (!status) ++ { ++ shutdown(fd, SHUT_RDWR); ++ return false; ++ } ++ ++ if ((out_buffer.size() - out_sent) > MAX_OUTPUT_QUEUE) ++ { ++ LOGE("IPC client output backlog too large; dropping connection"); ++ shutdown(fd, SHUT_RDWR); ++ out_buffer.clear(); ++ out_sent = 0; ++ set_writable_watch(false); ++ return false; ++ } ++ ++ flush_output(); ++ return true; + } + + namespace wf diff --git a/gui-wm/wayfire/wayfire-0.11.0-r69.ebuild b/gui-wm/wayfire/wayfire-0.11.0-r69.ebuild new file mode 100644 index 000000000000..b1300c773ea2 --- /dev/null +++ b/gui-wm/wayfire/wayfire-0.11.0-r69.ebuild @@ -0,0 +1,321 @@ +# Copyright 2019-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit dot-a meson toolchain-funcs + +DESCRIPTION="compiz like 3D wayland compositor" +HOMEPAGE="https://github.com/WayfireWM/wayfire" + +if [[ ${PV} == 9999 ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/WayfireWM/${PN}.git" + SLOT="0/0.12" + + IUSE="vulkan" + + # vulkan support is experimental and uses a bundled fork of wlroots + # keep it only in the live ebuild for now + RDEPEND="vulkan? ( media-libs/vulkan-loader )" + DEPEND="vulkan? ( dev-util/vulkan-headers )" + COMMON_DEPEND=" + !vulkan? ( + gui-libs/wlroots:0.20[drm(+),libinput(+),x11-backend,X?] + ) + vulkan? ( + dev-libs/libliftoff + media-libs/lcms:2 + media-libs/libdisplay-info:= + media-libs/mesa + sys-auth/seatd:= + x11-libs/libdrm + x11-libs/xcb-util-errors + x11-libs/xcb-util-renderutil + x11-libs/xcb-util-wm + ) + " +else + SRC_URI="https://github.com/WayfireWM/${PN}/releases/download/v${PV}/${P}.tar.xz" + KEYWORDS="~amd64 ~arm64 ~riscv" + SLOT="0/$(ver_cut 1-2)" + + COMMON_DEPEND="gui-libs/wlroots:0.20[drm(+),libinput(+),x11-backend,X?]" +fi + +LICENSE="MIT" +IUSE+=" X +dbus +gles3 nls openmp test" +RESTRICT="!test? ( test )" + +# bundled wlroots has the following dependency string according to included headers. +# wlroots[drm,gles2-renderer,libinput,x11-backend?,X?] +# enable x11-backend with X and vice versa +COMMON_DEPEND+=" + dev-cpp/nlohmann_json + dev-libs/glib:2 + dev-libs/libevdev + dev-libs/libinput:= + dev-libs/wayland + dev-libs/yyjson:= + >=dev-libs/wayland-protocols-1.12 + gui-libs/wf-config:${SLOT} + media-libs/glm + media-libs/libglvnd + media-libs/libjpeg-turbo:= + media-libs/libpng:= + virtual/libudev:= + x11-libs/cairo + x11-libs/libxkbcommon + x11-libs/pango + x11-libs/pixman + dbus? ( sys-apps/dbus ) + X? ( x11-libs/libxcb:= ) +" + +RDEPEND+=" + >=dev-lang/python-3.11 + ${COMMON_DEPEND} + x11-misc/xkeyboard-config +" +DEPEND+=" + ${COMMON_DEPEND} + test? ( dev-cpp/doctest ) +" +BDEPEND=" + dev-util/wayland-scanner + virtual/pkgconfig + openmp? ( + || ( + sys-devel/gcc[openmp] + llvm-runtimes/clang-runtime[openmp] + ) + ) + nls? ( + sys-devel/gettext + ) +" + +PATCHES=( + "${FILESDIR}"/${P}-transparent-cube.patch + "${FILESDIR}"/${P}-rotate-layer-shell.patch + "${FILESDIR}"/${P}-screensaver-layer.patch + "${FILESDIR}"/${P}-desktop-sampling.patch + "${FILESDIR}"/${P}-cube-damage-stacking.patch + "${FILESDIR}"/${P}-cube-panel-overlay.patch + "${FILESDIR}"/${P}-stitched-outputs.patch + "${FILESDIR}"/${P}-virtual-output-polish.patch + "${FILESDIR}"/${P}-wcm-displays.patch + "${FILESDIR}"/${P}-window-scope.patch + "${FILESDIR}"/${P}-float-on-top.patch + "${FILESDIR}"/${P}-physical-tiling.patch + "${FILESDIR}"/${P}-cube-key-repeat.patch + "${FILESDIR}"/${P}-cube-repeat-minimum-20.patch + "${FILESDIR}"/${P}-scope-toggle.patch + "${FILESDIR}"/${P}-dialog-family-stacking.patch + "${FILESDIR}"/${P}-physical-grid.patch + "${FILESDIR}"/${P}-grid-preview.patch + "${FILESDIR}"/${P}-grid-cycle.patch + "${FILESDIR}"/${P}-physical-output-focus.patch + "${FILESDIR}"/${P}-oswitch-local-symbols.patch + "${FILESDIR}"/${P}-oswitch-wrap.patch + "${FILESDIR}"/${P}-popup-physical-scope.patch + "${FILESDIR}"/${P}-move-snapping.patch + "${FILESDIR}"/${P}-move-held-redraw.patch + "${FILESDIR}"/${P}-workspace-backgrounds.patch + "${FILESDIR}"/${P}-show-desktop-workspace.patch + "${FILESDIR}"/${P}-vswitch-held-target.patch + "${FILESDIR}"/${P}-decoration-window-menu.patch + "${FILESDIR}"/${P}-layer-move-old-damage.patch + "${FILESDIR}"/${P}-workspace-grid-events.patch + "${FILESDIR}"/${P}-r35-fullscreen-subpasses.patch + "${FILESDIR}"/${P}-r36-cube-fixed-top.patch + "${FILESDIR}"/${P}-r37-cube-carried-visibility.patch + "${FILESDIR}"/${P}-r38-cube-workspace-lifecycle.patch + "${FILESDIR}"/${P}-r39-cube-workspace-rows.patch + "${FILESDIR}"/${P}-r40-cube-row-translation.patch + "${FILESDIR}"/${P}-r41-cube-complete-row-translation.patch + "${FILESDIR}"/${P}-r42-grid-monitor-window-move.patch + "${FILESDIR}"/${P}-r43-cube-mouse-overview.patch + "${FILESDIR}"/${P}-r44-cube-rigid-stack.patch + "${FILESDIR}"/${P}-r45-cube-stack-tilt.patch + "${FILESDIR}"/${P}-r46-cube-resting-faces.patch + "${FILESDIR}"/${P}-r47-cube-direct-drag.patch + "${FILESDIR}"/${P}-r48-cube-wheel-zoom.patch + "${FILESDIR}"/${P}-r49-cube-square-footprint.patch + "${FILESDIR}"/${P}-r50-cube-center-pivot.patch + "${FILESDIR}"/${P}-r51-cube-centered-entry.patch + "${FILESDIR}"/${P}-r53-cube-warp.patch + "${FILESDIR}"/${P}-r55-cube-backdrop-opacity.patch + "${FILESDIR}"/${P}-r56-cube-warp-rest.patch + "${FILESDIR}"/${P}-r57-cube-hyperspace.patch + "${FILESDIR}"/${P}-r58-cube-nebula.patch + "${FILESDIR}"/${P}-r59-ambient-background.patch + "${FILESDIR}"/${P}-r60-ambient-package-split.patch + "${FILESDIR}"/${P}-r65-output-transforms.patch + "${FILESDIR}"/${P}-drag-escape.patch + "${FILESDIR}"/${P}-r67-grid-gap.patch + "${FILESDIR}"/${P}-r68-ipc-control.patch + "${FILESDIR}"/${P}-ipc-backpressure.patch +) + +# The local patches change the plugin ABI (scene layer indices, class layouts). +# Tie the subslot to WAYFIRE_API_ABI_VERSION so := consumers rebuild whenever a +# patch bumps it; src_prepare refuses to build if the two drift apart. +# wf-config above retains its upstream 0.11 subslot. +WAYFIRE_ABI="20260918" +SLOT="${SLOT}.abi${WAYFIRE_ABI}" + +pkg_pretend() { + [[ ${MERGE_TYPE} != binary ]] && use openmp && tc-check-openmp +} + +pkg_setup() { + [[ ${MERGE_TYPE} != binary ]] && use openmp && tc-check-openmp +} + +src_prepare() { + default + + local abi + abi=$(sed -n "s/^#define WAYFIRE_API_ABI_VERSION_MACRO \([0-9']*\)$/\1/p" \ + src/api/wayfire/plugin.hpp) || die + abi=${abi//\'/} + if [[ ${abi} != "${WAYFIRE_ABI}" ]]; then + die "WAYFIRE_API_ABI_VERSION is '${abi}' but the subslot says '${WAYFIRE_ABI}'; update WAYFIRE_ABI" + fi + + local dbusrunsession=$(usev dbus dbus-run-session) + sed -e "s:@EPREFIX@:${EPREFIX}:g" -e "s:@DBUS_RUN_SESSION@:${dbusrunsession}:" \ + "${FILESDIR}"/wayfire-session-2 > "${T}"/wayfire-session || die + sed -e "s:@EPREFIX@:${EPREFIX}:" \ + "${FILESDIR}"/wayfire-session.desktop > "${T}"/wayfire-session.desktop || die +} + +src_configure() { + lto-guarantee-fat + + local emesonargs=( + $(meson_feature test tests) + $(meson_feature X xwayland) + $(meson_use gles3 enable_gles32) + $(meson_feature nls build_locales) + $(meson_use openmp enable_openmp) + -Duse_system_wfconfig=enabled + ) + + if [[ ${PV} == 9999 ]]; then + emesonargs+=( + $(meson_feature !vulkan use_system_wlroots) + $(meson_use vulkan vulkan_effects) + ) + else + emesonargs+=( + -Duse_system_wlroots=enabled + -Dvulkan_effects=false + ) + fi + + meson_src_configure +} + +src_install() { + meson_src_install + + exeinto "/usr/$(get_libdir)/wayfire/ipc-control-r68" + doexe "${BUILD_DIR}/plugins/ipc-rules/libipc-rules.so" + doexe "${BUILD_DIR}/plugins/wm-actions/libwm-actions.so" + + exeinto "/usr/$(get_libdir)/wayfire/cube-ambient-r59" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + + # A distinct packaged path permits upgrading a resident cached plugin. + exeinto "/usr/$(get_libdir)/wayfire/wm-actions" + doexe "${BUILD_DIR}/plugins/wm-actions/libwm-actions.so" + exeinto "/usr/$(get_libdir)/wayfire/grid" + doexe "${BUILD_DIR}/plugins/grid/libgrid.so" + exeinto "/usr/$(get_libdir)/wayfire/grid-physical" + doexe "${BUILD_DIR}/plugins/grid/libgrid.so" + exeinto "/usr/$(get_libdir)/wayfire/move-physical" + doexe "${BUILD_DIR}/plugins/single_plugins/libmove.so" + exeinto "/usr/$(get_libdir)/wayfire/grid-preview" + doexe "${BUILD_DIR}/plugins/grid/libgrid.so" + exeinto "/usr/$(get_libdir)/wayfire/grid-cycle" + doexe "${BUILD_DIR}/plugins/grid/libgrid.so" + exeinto "/usr/$(get_libdir)/wayfire/oswitch-physical-r24" + doexe "${BUILD_DIR}/plugins/single_plugins/liboswitch.so" + exeinto "/usr/$(get_libdir)/wayfire/grid-move-r27" + doexe "${BUILD_DIR}/plugins/grid/libgrid.so" + # Legacy activation paths remain valid, now containing ordinary Grid only. + exeinto "/usr/$(get_libdir)/wayfire/grid-occupancy-r54" + doexe "${BUILD_DIR}/plugins/grid/libgrid.so" + exeinto "/usr/$(get_libdir)/wayfire/grid-occupancy-r52" + doexe "${BUILD_DIR}/plugins/grid/libgrid.so" + exeinto "/usr/$(get_libdir)/wayfire/grid-monitor-r42" + doexe "${BUILD_DIR}/plugins/grid/libgrid.so" + exeinto "/usr/$(get_libdir)/wayfire/move-snap-r27" + doexe "${BUILD_DIR}/plugins/single_plugins/libmove.so" + exeinto "/usr/$(get_libdir)/wayfire/grid-gap-r67" + doexe "${BUILD_DIR}/plugins/grid/libgrid.so" + exeinto "/usr/$(get_libdir)/wayfire/move-gap-r67" + doexe "${BUILD_DIR}/plugins/single_plugins/libmove.so" + exeinto "/usr/$(get_libdir)/wayfire/move-redraw-r28" + doexe "${BUILD_DIR}/plugins/single_plugins/libmove.so" + exeinto "/usr/$(get_libdir)/wayfire/wm-actions-desktop-r30" + doexe "${BUILD_DIR}/plugins/wm-actions/libwm-actions.so" + exeinto "/usr/$(get_libdir)/wayfire/vswitch-held-r31" + doexe "${BUILD_DIR}/plugins/vswitch/libvswitch.so" + exeinto "/usr/$(get_libdir)/wayfire/vswitch-held-r37" + doexe "${BUILD_DIR}/plugins/vswitch/libvswitch.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-r38" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-r39" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-r40" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-r41" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-nebula-r58" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-hyperspace-r57" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-warp-rest-r56" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-backdrop-r55" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-warp-r53" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-centered-entry-r51" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-center-r50" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-square-r49" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-wheel-r48" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-direct-r47" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-rest-r46" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-tilt-r45" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-stack-r44" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/cube-overview-r43" + doexe "${BUILD_DIR}/plugins/cube/libcube.so" + exeinto "/usr/$(get_libdir)/wayfire/vswitch-held-r39" + doexe "${BUILD_DIR}/plugins/vswitch/libvswitch.so" + dobin "${T}"/wayfire-session + + insinto "/usr/share/wayland-sessions/" + insopts -m644 + doins wayfire.desktop + doins "${T}"/wayfire-session.desktop + + insinto "/usr/share/wayfire/" + doins wayfire.ini + + insinto "/etc" + doins "${FILESDIR}"/wayfire.env + + strip-lto-bytecode +} diff --git a/llvm-core/clang/Manifest b/llvm-core/clang/Manifest index 88f21febb53a..98808015389f 100644 --- a/llvm-core/clang/Manifest +++ b/llvm-core/clang/Manifest @@ -4,7 +4,6 @@ DIST llvm-19.1.0-manpages.tar.bz2 286165 BLAKE2B d96df2e748d2d52af2e8b7023f59af9 DIST llvm-20.1.0-manpages.tar.xz 296208 BLAKE2B 38832b461de120fd79ff02eb1bc2b71d5bb1280a1daf91860ffcb6994fff6dfcc2bbd1c1d850d9fc31fe47c4a35aa14fc2964113768d53e7c1ebe6710e24a9ae SHA512 296b4a9b8aa50761c342f0f279c529d1a56f2ab5430e2c65efc3b53c4a3ddc506ce4222b9d9bd49d38884c5b00a36ee16ae62746912ec1c881f69bfb4d124a54 DIST llvm-21.1.0-manpages.tar.xz 303556 BLAKE2B 93e844bb13826e0154a24bfcdc108c9e373903621b797d3382dbccd4d24ae5af17e994f9ffeb482a89e0c123c82b3b61cd45e3f9376c9223075915377a4dc7fc SHA512 9940332e94439ba57fe7ef5ef4439f505d28c2ddfbbdb3d79e8a664a7164bd80b874c29c9c4abdde1eadaadc975daa4254bf110e57359c3012464be6eac6fb87 DIST llvm-22.1.0-r2-manpages.tar.xz 320124 BLAKE2B 7a54177781e84d5e0f788a3ebc00f9417b943e14d8c462a859c5267f87bb8c9ed5132000d069b6cbab089411d025852af3b35fd7ace0a1f0f606bee210f9e8e5 SHA512 cdf986c1927436e49ef0b5a02b5fc08473bad4aeef11164119824ef02afb8c5513a19e1d0bed2211ba259ef2d331f833daf9543d030e56d02407ef3efdb74196 -DIST llvm-23.1.4-manpages.tar.xz 337700 BLAKE2B ecdbda976c00923f07a0342ebb1065fb4ac5c51d398c5460bbd1039493ff3a16cdf0c9abe17bd42a2ff1b426f8ae9241c4d81d3eb1751b1a5c43c544baee4735 SHA512 5f2c19582c9887eecd916d7e9855446e0f0c8a458d0c43850994de4ebd0ab24cae83a4e50049d82b218c61b46664877e2b1fcbf720970e9ef558f65ff751737c DIST llvm-gentoo-patchset-17.0.6-r5.tar.xz 7256 BLAKE2B d0f3f7df4ee38fbcd236225e9b64e313a1d4bae8d8eff0473fe50e20e79e0e161af418eb4a56e29e5b188cb17cd12c43bc42716d390286f6ab42d45b86b7a040 SHA512 70ca00b79e927931edfe905688f82b744443675c8b6ad7b5a962636629ea73d778dbf7a43c4947f05773eb04cdc0c1a64fe9d0e41d46ef04237b95bfc076740f DIST llvm-gentoo-patchset-18.1.8-r8.tar.xz 27220 BLAKE2B 2bf71ea769ebe61c863a52a891743d705f313d41416d02614deb9f5e8bffec3a22b3c681d3cee4b4a4da224943d1015f061562d379f7d3934f4e9196ef6feeb4 SHA512 08264c6a5ae30666d6e18c74cb9cd706317157d5cb9b31f35c6807b9396f733dc80ba957c711bf20e2a4388f2ef8ba620e82598fa779922accb50dad7db23eb3 DIST llvm-project-17.0.6.src.tar.xz 127838860 BLAKE2B d6ede1a9fda8756995c3e0654111941649e15794179641806f18919f1dc68c41ca0cabd5693b5096d05dccc3a391cd20d34af1137bf8af92ed3117a1ce84d1b2 SHA512 6d85bf749e0d77553cc215cbfa61cec4ac4f4f652847f56f946b6a892a99a5ea40b6ab8b39a9708a035001f007986941ccf17e4635260a8b0c1fa59e78d41e30 diff --git a/llvm-core/llvm/Manifest b/llvm-core/llvm/Manifest index 88f21febb53a..98808015389f 100644 --- a/llvm-core/llvm/Manifest +++ b/llvm-core/llvm/Manifest @@ -4,7 +4,6 @@ DIST llvm-19.1.0-manpages.tar.bz2 286165 BLAKE2B d96df2e748d2d52af2e8b7023f59af9 DIST llvm-20.1.0-manpages.tar.xz 296208 BLAKE2B 38832b461de120fd79ff02eb1bc2b71d5bb1280a1daf91860ffcb6994fff6dfcc2bbd1c1d850d9fc31fe47c4a35aa14fc2964113768d53e7c1ebe6710e24a9ae SHA512 296b4a9b8aa50761c342f0f279c529d1a56f2ab5430e2c65efc3b53c4a3ddc506ce4222b9d9bd49d38884c5b00a36ee16ae62746912ec1c881f69bfb4d124a54 DIST llvm-21.1.0-manpages.tar.xz 303556 BLAKE2B 93e844bb13826e0154a24bfcdc108c9e373903621b797d3382dbccd4d24ae5af17e994f9ffeb482a89e0c123c82b3b61cd45e3f9376c9223075915377a4dc7fc SHA512 9940332e94439ba57fe7ef5ef4439f505d28c2ddfbbdb3d79e8a664a7164bd80b874c29c9c4abdde1eadaadc975daa4254bf110e57359c3012464be6eac6fb87 DIST llvm-22.1.0-r2-manpages.tar.xz 320124 BLAKE2B 7a54177781e84d5e0f788a3ebc00f9417b943e14d8c462a859c5267f87bb8c9ed5132000d069b6cbab089411d025852af3b35fd7ace0a1f0f606bee210f9e8e5 SHA512 cdf986c1927436e49ef0b5a02b5fc08473bad4aeef11164119824ef02afb8c5513a19e1d0bed2211ba259ef2d331f833daf9543d030e56d02407ef3efdb74196 -DIST llvm-23.1.4-manpages.tar.xz 337700 BLAKE2B ecdbda976c00923f07a0342ebb1065fb4ac5c51d398c5460bbd1039493ff3a16cdf0c9abe17bd42a2ff1b426f8ae9241c4d81d3eb1751b1a5c43c544baee4735 SHA512 5f2c19582c9887eecd916d7e9855446e0f0c8a458d0c43850994de4ebd0ab24cae83a4e50049d82b218c61b46664877e2b1fcbf720970e9ef558f65ff751737c DIST llvm-gentoo-patchset-17.0.6-r5.tar.xz 7256 BLAKE2B d0f3f7df4ee38fbcd236225e9b64e313a1d4bae8d8eff0473fe50e20e79e0e161af418eb4a56e29e5b188cb17cd12c43bc42716d390286f6ab42d45b86b7a040 SHA512 70ca00b79e927931edfe905688f82b744443675c8b6ad7b5a962636629ea73d778dbf7a43c4947f05773eb04cdc0c1a64fe9d0e41d46ef04237b95bfc076740f DIST llvm-gentoo-patchset-18.1.8-r8.tar.xz 27220 BLAKE2B 2bf71ea769ebe61c863a52a891743d705f313d41416d02614deb9f5e8bffec3a22b3c681d3cee4b4a4da224943d1015f061562d379f7d3934f4e9196ef6feeb4 SHA512 08264c6a5ae30666d6e18c74cb9cd706317157d5cb9b31f35c6807b9396f733dc80ba957c711bf20e2a4388f2ef8ba620e82598fa779922accb50dad7db23eb3 DIST llvm-project-17.0.6.src.tar.xz 127838860 BLAKE2B d6ede1a9fda8756995c3e0654111941649e15794179641806f18919f1dc68c41ca0cabd5693b5096d05dccc3a391cd20d34af1137bf8af92ed3117a1ce84d1b2 SHA512 6d85bf749e0d77553cc215cbfa61cec4ac4f4f652847f56f946b6a892a99a5ea40b6ab8b39a9708a035001f007986941ccf17e4635260a8b0c1fa59e78d41e30 diff --git a/lxqt-base/lxqt-config/files/lxqt-config-2.4.0-libkscreen-6.8-984462.patch b/lxqt-base/lxqt-config/files/lxqt-config-2.4.0-libkscreen-6.8-984462.patch new file mode 100644 index 000000000000..27bfb4e391b9 --- /dev/null +++ b/lxqt-base/lxqt-config/files/lxqt-config-2.4.0-libkscreen-6.8-984462.patch @@ -0,0 +1,37 @@ +https://bugs.gentoo.org/984462 +https://github.com/lxqt/lxqt-config/commit/8efdae223bb + +From 8efdae223bbea13a653d66a1bca6004df77d7033 Mon Sep 17 00:00:00 2001 +From: n3rdopolis <bluescreenavenger@gmail.com> +Date: Fri, 2 Oct 2026 13:30:17 -0400 +Subject: [PATCH] Fix KScreen compile against latest KScreen changes (#1227) + +--- + lxqt-config-monitor/kscreenutils.cpp | 1 + + lxqt-config-monitor/monitorpicture.cpp | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/lxqt-config-monitor/kscreenutils.cpp b/lxqt-config-monitor/kscreenutils.cpp +index c7d8f867..9f34959d 100644 +--- a/lxqt-config-monitor/kscreenutils.cpp ++++ b/lxqt-config-monitor/kscreenutils.cpp +@@ -3,6 +3,7 @@ + + #include <KScreen/Output> + #include <KScreen/Mode> ++#include <KScreen/Screen> + #include <KScreen/Config> + #include <KScreen/GetConfigOperation> + #include <KScreen/SetConfigOperation> +diff --git a/lxqt-config-monitor/monitorpicture.cpp b/lxqt-config-monitor/monitorpicture.cpp +index c43bf232..11b6d4c9 100644 +--- a/lxqt-config-monitor/monitorpicture.cpp ++++ b/lxqt-config-monitor/monitorpicture.cpp +@@ -25,6 +25,7 @@ + #include <QVector2D> + #include <QRectF> + #include <KScreen/Mode> ++#include <KScreen/Screen> + #include <QScrollBar> + #include <QResizeEvent> + #include <QTransform> diff --git a/lxqt-base/lxqt-config/lxqt-config-2.4.0-r1.ebuild b/lxqt-base/lxqt-config/lxqt-config-2.4.0-r1.ebuild new file mode 100644 index 000000000000..2bae207931e6 --- /dev/null +++ b/lxqt-base/lxqt-config/lxqt-config-2.4.0-r1.ebuild @@ -0,0 +1,69 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake xdg + +MY_PV="$(ver_cut 1-2)" + +DESCRIPTION="LXQt system configuration control center" +HOMEPAGE=" + https://lxqt-project.org/ + https://github.com/lxqt/lxqt-config/ +" + +if [[ ${PV} == 9999 ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/lxqt/${PN}.git" +else + SRC_URI="https://github.com/lxqt/${PN}/releases/download/${PV}/${P}.tar.xz" + KEYWORDS="~amd64 ~arm64 ~ppc64 ~riscv ~x86" +fi + +LICENSE="GPL-2 GPL-2+ GPL-3 LGPL-2 LGPL-2+ LGPL-2.1+ WTFPL-2" +SLOT="0" +IUSE="+monitor +touchpad" + +BDEPEND=" + >=dev-qt/qttools-6.6:6[linguist] + >=dev-util/lxqt-build-tools-2.4.0 +" +DEPEND=" + >=dev-libs/libqtxdg-4.4.0 + >=dev-qt/qtbase-6.6:6[gui,widgets,xml] + >=dev-qt/qtsvg-6.6:6 + =lxqt-base/liblxqt-${MY_PV}*:= + =lxqt-base/lxqt-menu-data-${MY_PV}* + virtual/zlib:= + x11-apps/setxkbmap + x11-libs/libxcb:= + x11-libs/libX11 + x11-libs/libXcursor + x11-libs/libXfixes + monitor? ( kde-plasma/libkscreen:6= ) + touchpad? ( + virtual/libudev:= + x11-drivers/xf86-input-libinput + x11-libs/libXi + ) +" +RDEPEND="${DEPEND}" + +PATCHES=( + "${FILESDIR}/${PN}-2.4.0-libkscreen-6.8-984462.patch" +) + +src_configure() { + local mycmakeargs=( + -DWITH_MONITOR=$(usex monitor) + -DWITH_TOUCHPAD=$(usex touchpad) + ) + + cmake_src_configure +} + +src_install() { + cmake_src_install + doman man/*.1 liblxqt-config-cursor/man/*.1 lxqt-config-appearance/man/*.1 +} diff --git a/media-gfx/alembic/Manifest b/media-gfx/alembic/Manifest index e6378a9716fe..1d56d1fdad33 100644 --- a/media-gfx/alembic/Manifest +++ b/media-gfx/alembic/Manifest @@ -1,2 +1 @@ -DIST alembic-1.8.6.tar.gz 868945 BLAKE2B 1ac30ffa75a4b8bbb3f32820e6f67cebcd894465f16cd12ee0396c88611af821b13973f46228fd59726c6bc0cc8885afd4de81a0cebaa0f94cb30fd9daba9885 SHA512 6371b830242be90d4ea833248df5fd42d9e713e305d15eb1383d04410319acdae5743d48d65e8f75f1cedce777d2af7d969cde095f678b17322c19f1c69f477b DIST alembic-1.8.8.tar.gz 933258 BLAKE2B 4f56f5addcf153407da14d6646255ee35b1126b307179fbcfa2c13398825680652b557772db0a08906d04a9bd46c1fa82862a65367d9ce162f2b9d948ff9b701 SHA512 02b7bf5782e83efb08a8653f130b02565fa997e857dbd8d0523e1b218ff58d929fbf9690db0980e8101a31f01a67341b6000af8794538890ef7d759fe0289e2f diff --git a/media-gfx/alembic/alembic-1.8.6-r1.ebuild b/media-gfx/alembic/alembic-1.8.6-r1.ebuild deleted file mode 100644 index a4d28ee04134..000000000000 --- a/media-gfx/alembic/alembic-1.8.6-r1.ebuild +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 1999-2025 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -PYTHON_COMPAT=( python3_{13..14} ) - -inherit cmake python-single-r1 - -DESCRIPTION="Open framework for storing and sharing scene data" -HOMEPAGE="https://www.alembic.io/" -SRC_URI="https://github.com/${PN}/${PN}/archive/${PV}.tar.gz -> ${P}.tar.gz" - -LICENSE="BSD" -SLOT="0" -KEYWORDS="amd64 ~arm ~arm64 ~ppc64 ~x86" -IUSE="examples hdf5 python test" -REQUIRED_USE=" - ${PYTHON_REQUIRED_USE} - examples? ( python ) -" -RESTRICT="!test? ( test )" - -RDEPEND=" - ${PYTHON_DEPS} - >=dev-libs/imath-3.1.11-r1:= - python? ( dev-libs/imath:=[python,${PYTHON_SINGLE_USEDEP}] ) - hdf5? ( - >=sci-libs/hdf5-1.10.2:=[zlib(+)] - >=virtual/zlib-1.2.11-r1:= - ) - python? ( $(python_gen_cond_dep 'dev-libs/boost[python,${PYTHON_USEDEP}]') ) -" -DEPEND="${RDEPEND}" - -PATCHES=( - "${FILESDIR}/${PN}-1.8.5-set-correct-libdir.patch" - "${FILESDIR}/${PN}-1.8.6-py312.patch" - -) - -DOCS=( ACKNOWLEDGEMENTS.txt FEEDBACK.txt NEWS.txt README.txt ) - -src_configure() { - local mycmakeargs=( - -DALEMBIC_BUILD_LIBS=ON - -DALEMBIC_DEBUG_WARNINGS_AS_ERRORS=OFF - -DALEMBIC_SHARED_LIBS=ON - # currently does nothing but require doxygen - -DDOCS_PATH=OFF - -DUSE_ARNOLD=OFF - -DUSE_BINARIES=ON - -DUSE_EXAMPLES=$(usex examples) - -DUSE_HDF5=$(usex hdf5) - -DUSE_MAYA=OFF - -DUSE_PRMAN=OFF - -DUSE_PYALEMBIC=$(usex python) - -DUSE_TESTS=$(usex test) - ) - - use python && mycmakeargs+=( -DPython3_EXECUTABLE=${PYTHON} ) - - cmake_src_configure -} - -# some tests may fail if run in parallel mode -# see https://github.com/alembic/alembic/issues/401 -src_test() { - cmake_src_test -j1 -} diff --git a/media-gfx/alembic/files/alembic-1.8.5-set-correct-libdir.patch b/media-gfx/alembic/files/alembic-1.8.5-set-correct-libdir.patch deleted file mode 100644 index 70071a68e49b..000000000000 --- a/media-gfx/alembic/files/alembic-1.8.5-set-correct-libdir.patch +++ /dev/null @@ -1,48 +0,0 @@ -From fade0b08f9343fa3cd941b281a3ce49a1918acca Mon Sep 17 00:00:00 2001 -From: Bernd Waibel <waebbl-gentoo@posteo.net> -Date: Sat, 18 Mar 2023 19:08:51 +0100 -Subject: [PATCH] Gentoo-specific: set correct libdir - -Signed-off-by: Bernd Waibel <waebbl-gentoo@posteo.net> ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -74,12 +74,12 @@ option(DOCS_PATH - - # Set static/dynamic build options - SET(LIB_TYPE STATIC) --SET(RUNTIME_INSTALL_DIR lib) --SET(LIBRARY_INSTALL_DIR lib) --SET(ARCHIVE_INSTALL_DIR lib) -+SET(RUNTIME_INSTALL_DIR lib${LIB_SUFFIX}) -+SET(LIBRARY_INSTALL_DIR lib${LIB_SUFFIX}) -+SET(ARCHIVE_INSTALL_DIR lib${LIB_SUFFIX}) - IF (ALEMBIC_SHARED_LIBS) - SET(LIB_TYPE SHARED) -- SET(ARCHIVE_INSTALL_DIR lib) -+ SET(ARCHIVE_INSTALL_DIR lib${LIB_SUFFIX}) - IF (WIN32) - ADD_DEFINITIONS(-DALEMBIC_DLL) - ENDIF() ---- a/lib/Alembic/CMakeLists.txt -+++ b/lib/Alembic/CMakeLists.txt -@@ -84,7 +84,7 @@ TARGET_LINK_LIBRARIES(Alembic - ${ZLIB_LIBRARY} - ) - --SET( ALEMBIC_LIB_INSTALL_DIR lib CACHE STRING "Where to install the Alembic libs") -+SET( ALEMBIC_LIB_INSTALL_DIR lib${LIB_SUFFIX} CACHE STRING "Where to install the Alembic libs") - INSTALL(TARGETS Alembic - EXPORT AlembicTargets - LIBRARY DESTINATION ${ALEMBIC_LIB_INSTALL_DIR} -@@ -97,7 +97,7 @@ INSTALL(TARGETS Alembic - - set(alembic_targets_file "${PROJECT_NAME}Targets.cmake") - --SET(ConfigPackageLocation lib/cmake/Alembic CACHE PATH -+SET(ConfigPackageLocation lib${LIB_SUFFIX}/cmake/Alembic CACHE PATH - "Where to install the Alembic's cmake files") - - INCLUDE(CMakePackageConfigHelpers) --- -2.40.0 - diff --git a/media-libs/aubio/aubio-0.4.9-r4.ebuild b/media-libs/aubio/aubio-0.4.9-r4.ebuild index 227c9274565d..918162cda775 100644 --- a/media-libs/aubio/aubio-0.4.9-r4.ebuild +++ b/media-libs/aubio/aubio-0.4.9-r4.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -6,7 +6,7 @@ EAPI=8 DISTUTILS_USE_PEP517=setuptools DISTUTILS_EXT=1 DISTUTILS_OPTIONAL=1 -PYTHON_COMPAT=( python3_{12..13} ) +PYTHON_COMPAT=( python3_13 ) PYTHON_REQ_USE='threads(+)' inherit distutils-r1 waf-utils diff --git a/media-libs/aubio/aubio-0.4.9-r5.ebuild b/media-libs/aubio/aubio-0.4.9-r5.ebuild index 8919e9f68ec0..0bddeebf2a55 100644 --- a/media-libs/aubio/aubio-0.4.9-r5.ebuild +++ b/media-libs/aubio/aubio-0.4.9-r5.ebuild @@ -6,7 +6,7 @@ EAPI=8 DISTUTILS_USE_PEP517=setuptools DISTUTILS_EXT=1 DISTUTILS_OPTIONAL=1 -PYTHON_COMPAT=( python3_{12..14} ) +PYTHON_COMPAT=( python3_{13..14} ) PYTHON_REQ_USE='threads(+)' inherit distutils-r1 ffmpeg-compat waf-utils diff --git a/media-libs/mesa/Manifest b/media-libs/mesa/Manifest index 625d520d9412..08dc2da94143 100644 --- a/media-libs/mesa/Manifest +++ b/media-libs/mesa/Manifest @@ -2,6 +2,7 @@ DIST mesa-26.1.8.tar.xz 77827888 BLAKE2B 173997a90bbb27d8f6b1e4c349430d063a9264e DIST mesa-26.2.2.tar.xz 68533264 BLAKE2B a9924a7cbac5dbdb4d93ba8499c49a6be51edd8ec5b258f2986a5690b04f6898012389dcc7e07097bad0defed52e9255d3a2c82bb6101a531b17fde82ef69afc SHA512 55b1234c8653dad9b85ba3de7b777a0d957909baf4628f0c00e21d4ba89c004cb687a1dcf8da46ed0a092231dd4f859027509f83ad8c558750845ba470902615 DIST mesa-26.2.3.tar.xz 68561540 BLAKE2B 53e451060bdc0df6ea8f7b5d3a23563c4bf56613c0871a91c5f0570649fcfe35e0dc1c0fc7834642621fc0772d0614b6a60acf196e82d4b77132629307c4a82e SHA512 ec621735ae4679762ea27b979a5314c7aecfdd4165afea19c8c55f933d79b40f01fa041a95874fc4347e732ac565786d9b616a9aa8856a422204d9ecc9a12ec4 DIST mesa-26.2.4.tar.xz 68613948 BLAKE2B 28506a9366e3c031acbb52924e43b0ec098f44a3ac3bb839ec2a86fa0faf83652347704b0e58bdc447aaebae5a2a254e5456e0b893755fd7721e57d6b614ecc4 SHA512 42e793561661a567e72c7324d071ab9f82d3a9f1e000827ed989b8d5fcc2cd16e50c02808d43465b3135c25a3cad77649ba0bf9e35d95b848f8fc8d8b9ce327c +DIST mesa-26.2.5.tar.xz 68652756 BLAKE2B cfcc4d72c5e6f54904c77b98c65df8159c41ebe9c4b8f82a18a38983bfe06e25c02ea20ba613753838f2455edb156a2d79938f28328313a8eb79fe67d2ea9624 SHA512 08c7d7ea426ddfb0f7bc51b06e18c363a4d31cf1a8b203828bb455eda0189e41d2215eb9f627585ed11e0d0f231a6a8c9f0c7176238760cd76b62e20c35ccd6a DIST paste-1.0.14.crate 18157 BLAKE2B 35e8548611c51ee75f4d04926149e5e54870d7073d9b635d550a6fa0f85891f57f326bdbcff3dd8618cf40f8e08cf903ef87d9c034d5921d8b91e1db842cdd7c SHA512 3a793f0e5e773a7f7defc798a4c17ae9a40d715144632ea6cb0a8c785e14c4212046491df016bb9838281f8eaf327a79f01c1e2ac5f26785c028bc880faff9ee DIST proc-macro2-1.0.86.crate 48958 BLAKE2B 4b89e07f23af8328dbb34fe2b3f1b202f1e6a3885a6269740a23359b41bb4099ac2484565d3b2b0936261689ca525785ac620c766997234fd8d0f409e80e5ea3 SHA512 1cdb7e22a35ae231d880c9420784c9acf97bda2db258b3d34aae5061dc1858449defe19a49e12c6a4173906aa72a4115059ac2db0fc760205fd2ab8b5b414434 DIST quote-1.0.35.crate 28136 BLAKE2B 81424245e1e2b94459df68bb3a9a866c6a364102b5e1d010ede9c5f8278f8406d7b651957d091c5914e936b494b0f6e9a6a1dd8b7d35cd7d7100f86dee4ec12e SHA512 f5314fb6af17cf36c228e1970c569c29ec248954a450a5f90ba9e2896d04f74904c9cec5a1f74325f2489295a94491eee4ce8fb461e22cd4b34e53f1f881efd2 diff --git a/media-libs/mesa/mesa-26.2.5.ebuild b/media-libs/mesa/mesa-26.2.5.ebuild new file mode 100644 index 000000000000..51b130171aea --- /dev/null +++ b/media-libs/mesa/mesa-26.2.5.ebuild @@ -0,0 +1,446 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +LLVM_COMPAT=( {18..22} ) +LLVM_OPTIONAL=1 +CARGO_OPTIONAL=1 +PYTHON_COMPAT=( python3_{13..14} ) + +inherit flag-o-matic linux-info llvm-r2 meson-multilib python-any-r1 toolchain-funcs + +MY_P="${P/_/-}" + +CRATES=" + paste@1.0.14 + proc-macro2@1.0.86 + quote@1.0.35 + rustc-hash@2.1.1 + syn@2.0.87 + unicode-ident@1.0.12 +" + +RUST_MIN_VER="1.85.0" +RUST_MULTILIB=1 +RUST_OPTIONAL=1 + +inherit cargo + +DESCRIPTION="OpenGL-like graphic library for Linux" +HOMEPAGE="https://mesa3d.org/" + +if [[ ${PV} == 9999 ]]; then + EGIT_REPO_URI="https://gitlab.freedesktop.org/mesa/mesa.git" + inherit git-r3 +else + SRC_URI=" + https://archive.mesa3d.org/${MY_P}.tar.xz + " + KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-solaris" +fi + +# This should be {CARGO_CRATE_URIS//.crate/.tar.gz} to correspond to the wrap files, +# but there are "stale" distfiles on the mirrors with the wrong names. +# export MESON_PACKAGE_CACHE_DIR="${DISTDIR}" +SRC_URI+=" + opencl? ( ${CARGO_CRATE_URIS} ) + !opencl? ( video_cards_nvk? ( ${CARGO_CRATE_URIS} ) ) +" + +S="${WORKDIR}/${MY_P}" +EGIT_CHECKOUT_DIR=${S} + +LICENSE="MIT SGI-B-2.0" +SLOT="0" + +VIDEO_CARDS=" + asahi d3d12 freedreno i915 imagination intel lavapipe lima nouveau nvk + panfrost r300 r600 radeon radeonsi v3d vc4 virgl vivante vmware zink" +for card in ${VIDEO_CARDS}; do + IUSE_VIDEO_CARDS+=" video_cards_${card}" +done + +IUSE="${IUSE_VIDEO_CARDS} + cpu_flags_x86_sse2 debug +llvm + lm-sensors opencl +opengl +proprietary-codecs + sysprof test unwind vaapi valgrind vulkan + wayland +X +zstd" +RESTRICT="!test? ( test )" +REQUIRED_USE=" + llvm? ( ${LLVM_REQUIRED_USE} ) + video_cards_i915? ( llvm ) + video_cards_lavapipe? ( llvm vulkan ) + video_cards_radeon? ( x86? ( llvm ) amd64? ( llvm ) ) + video_cards_r300? ( x86? ( llvm ) amd64? ( llvm ) ) + video_cards_zink? ( vulkan opengl ) + video_cards_nvk? ( vulkan video_cards_nouveau ) +" + +LIBDRM_DEPSTRING=">=x11-libs/libdrm-2.4.133" +RDEPEND=" + ${LIBDRM_DEPSTRING}[${MULTILIB_USEDEP}] + >=dev-libs/expat-2.1.0-r3[${MULTILIB_USEDEP}] + >=dev-util/spirv-tools-1.3.231.0[${MULTILIB_USEDEP}] + >=media-libs/libglvnd-1.3.2[X?,${MULTILIB_USEDEP}] + >=virtual/zlib-1.2.9:=[${MULTILIB_USEDEP}] + unwind? ( sys-libs/libunwind[${MULTILIB_USEDEP}] ) + llvm? ( + $(llvm_gen_dep " + llvm-core/llvm:\${LLVM_SLOT}[llvm_targets_AMDGPU(+),${MULTILIB_USEDEP}] + opencl? ( + dev-util/spirv-llvm-translator:\${LLVM_SLOT} + llvm-core/clang:\${LLVM_SLOT}[llvm_targets_AMDGPU(+),${MULTILIB_USEDEP}] + =llvm-runtimes/libclc-\${LLVM_SLOT}*[spirv(-)] + ) + ") + video_cards_r600? ( + virtual/libelf:0=[${MULTILIB_USEDEP}] + ) + video_cards_radeon? ( + virtual/libelf:0=[${MULTILIB_USEDEP}] + ) + ) + lm-sensors? ( sys-apps/lm-sensors:=[${MULTILIB_USEDEP}] ) + opencl? ( + >=virtual/opencl-3 + llvm-runtimes/libclc[spirv(-)] + virtual/libelf:0= + ) + vaapi? ( + >=media-libs/libva-1.7.3:=[${MULTILIB_USEDEP}] + ) + video_cards_i915? ( + ${LIBDRM_DEPSTRING}[video_cards_intel] + ) + video_cards_nvk? ( + virtual/libelf:0=[${MULTILIB_USEDEP}] + ) + video_cards_radeonsi? ( + ${LIBDRM_DEPSTRING}[video_cards_amdgpu] + virtual/libelf:0=[${MULTILIB_USEDEP}] + ) + video_cards_zink? ( media-libs/vulkan-loader:=[${MULTILIB_USEDEP}] ) + vulkan? ( + media-libs/libdisplay-info:=[${MULTILIB_USEDEP}] + virtual/libudev:= + ) + wayland? ( >=dev-libs/wayland-1.18.0[${MULTILIB_USEDEP}] ) + X? ( + >=x11-libs/libX11-1.8[${MULTILIB_USEDEP}] + >=x11-libs/libxshmfence-1.1[${MULTILIB_USEDEP}] + >=x11-libs/libXext-1.3.2[${MULTILIB_USEDEP}] + >=x11-libs/libXxf86vm-1.1.3[${MULTILIB_USEDEP}] + >=x11-libs/libxcb-1.17:=[${MULTILIB_USEDEP}] + x11-libs/libXfixes[${MULTILIB_USEDEP}] + x11-libs/xcb-util-keysyms[${MULTILIB_USEDEP}] + ) + zstd? ( app-arch/zstd:=[${MULTILIB_USEDEP}] ) +" + +DEPEND="${RDEPEND} + sysprof? ( >=dev-util/sysprof-capture-49.0[${MULTILIB_USEDEP}] ) + video_cards_d3d12? ( >=dev-util/directx-headers-1.619.1[${MULTILIB_USEDEP}] ) + valgrind? ( dev-debug/valgrind ) + wayland? ( >=dev-libs/wayland-protocols-1.41 ) + X? ( + x11-libs/libXrandr[${MULTILIB_USEDEP}] + x11-base/xorg-proto + ) +" + +CLC_DEPSTRING=" + ~dev-util/mesa_clc-${PV}[video_cards_asahi?,video_cards_panfrost?] + llvm-runtimes/libclc[spirv(-)] +" +BDEPEND=" + ${PYTHON_DEPS} + opencl? ( + >=dev-util/bindgen-0.71.1 + ${RUST_DEPEND} + ) + >=dev-build/meson-1.7.0 + app-alternatives/yacc + sys-devel/flex + virtual/pkgconfig + $(python_gen_any_dep " + >=dev-python/mako-0.8.0[\${PYTHON_USEDEP}] + dev-python/packaging[\${PYTHON_USEDEP}] + dev-python/pyyaml[\${PYTHON_USEDEP}] + ") + video_cards_asahi? ( ${CLC_DEPSTRING} ) + video_cards_intel? ( ${CLC_DEPSTRING} ) + video_cards_panfrost? ( ${CLC_DEPSTRING} ) + vulkan? ( + dev-util/glslang + video_cards_imagination? ( ${CLC_DEPSTRING} ) + video_cards_nvk? ( + >=dev-util/bindgen-0.71.1 + >=dev-util/cbindgen-0.26.0 + ${RUST_DEPEND} + ${CLC_DEPSTRING} + ) + ) + wayland? ( dev-util/wayland-scanner ) +" + +QA_WX_LOAD=" +x86? ( + usr/lib/libgallium-*.so + usr/lib/libGLX_mesa.so.0.0.0 +)" + +src_unpack() { + if [[ ${PV} == 9999 ]]; then + git-r3_src_unpack + else + unpack ${MY_P}.tar.xz + fi + + if use opencl || use video_cards_nvk; then + # We need this because we cannot tell meson to use DISTDIR yet + mkdir -p "${S}"/subprojects/packagecache || die + local i + for i in ${CRATES}; do + i=${i/@/-} + ln -s "${DISTDIR}/${i}.crate" \ + "${S}/subprojects/packagecache/${i}.tar.gz" || die + done + fi +} + +pkg_pretend() { + if [[ ${MERGE_TYPE} != binary ]] && use test; then + tc-check-openmp + fi + + if use vulkan; then + if ! use video_cards_asahi && + ! use video_cards_d3d12 && + ! use video_cards_freedreno && + ! use video_cards_imagination && + ! use video_cards_intel && + ! use video_cards_lavapipe && + ! use video_cards_nouveau && + ! use video_cards_nvk && + ! use video_cards_panfrost && + ! use video_cards_radeonsi && + ! use video_cards_v3d && + ! use video_cards_virgl; then + ewarn "Ignoring USE=vulkan since VIDEO_CARDS does not contain asahi, d3d12, freedreno, imagination, intel, lavapipe, nouveau, nvk, panfrost, radeonsi, v3d, or virgl" + fi + fi + + # VA + if use vaapi; then + if ! use video_cards_d3d12 && + ! use video_cards_nouveau && + ! use video_cards_r600 && + ! use video_cards_radeonsi && + ! use video_cards_virgl; then + ewarn "Ignoring USE=vaapi since VIDEO_CARDS does not contain d3d12, nouveau, r600, radeonsi, or virgl" + fi + fi + + if ! use llvm; then + use opencl && ewarn "Ignoring USE=opencl since USE does not contain llvm" + fi +} + +python_check_deps() { + python_has_version -b ">=dev-python/mako-0.8.0[${PYTHON_USEDEP}]" && + python_has_version -b "dev-python/packaging[${PYTHON_USEDEP}]" && + python_has_version -b "dev-python/pyyaml[${PYTHON_USEDEP}]" || return 1 +} + +pkg_setup() { + if [[ ${MERGE_TYPE} != binary ]] && use test; then + tc-check-openmp + fi + + # warning message for bug 459306 + if use llvm && has_version llvm-core/llvm[!debug=]; then + ewarn "Mismatch between debug USE flags in media-libs/mesa and llvm-core/llvm" + ewarn "detected! This can cause problems. For details, see bug 459306." + fi + + if use video_cards_intel || + use video_cards_radeonsi; then + if kernel_is -ge 5 11 3; then + CONFIG_CHECK="~KCMP" + elif kernel_is -ge 5 11; then + CONFIG_CHECK="~CHECKPOINT_RESTORE" + elif kernel_is -ge 5 10 20; then + CONFIG_CHECK="~KCMP" + else + CONFIG_CHECK="~CHECKPOINT_RESTORE" + fi + linux-info_pkg_setup + fi + + use llvm && llvm-r2_pkg_setup + python-any-r1_pkg_setup + + if use opencl || (use vulkan && use video_cards_nvk); then + rust_pkg_setup + fi +} + +src_prepare() { + default + sed -i -e "/^PLATFORM_SYMBOLS/a '__gentoo_check_ldflags__'," \ + bin/symbols-check.py || die # bug #830728 +} + +multilib_src_configure() { + local emesonargs=() + + # bug #932591 and https://gitlab.freedesktop.org/mesa/mesa/-/issues/11140 + filter-lto + + local platforms + use X && platforms+="x11" + use wayland && platforms+=",wayland" + emesonargs+=(-Dplatforms=${platforms#,}) + + if use video_cards_d3d12 || + use video_cards_nouveau || + use video_cards_r600 || + use video_cards_radeonsi || + use video_cards_virgl; then + emesonargs+=($(meson_feature vaapi gallium-va)) + use vaapi && emesonargs+=( -Dva-libs-path="${EPREFIX}"/usr/$(get_libdir)/va/drivers ) + else + emesonargs+=(-Dgallium-va=disabled) + fi + + if use video_cards_d3d12; then + emesonargs+=($(meson_feature vaapi gallium-d3d12-video)) + fi + + gallium_enable !llvm softpipe + gallium_enable llvm llvmpipe + gallium_enable video_cards_asahi asahi + gallium_enable video_cards_d3d12 d3d12 + gallium_enable video_cards_freedreno freedreno + gallium_enable video_cards_i915 i915 + gallium_enable video_cards_intel crocus iris + gallium_enable video_cards_lima lima + gallium_enable video_cards_nouveau nouveau + gallium_enable video_cards_panfrost panfrost + gallium_enable video_cards_v3d v3d + gallium_enable video_cards_vc4 vc4 + gallium_enable video_cards_virgl virgl + gallium_enable video_cards_vivante etnaviv + gallium_enable video_cards_vmware svga + gallium_enable video_cards_zink zink + + gallium_enable video_cards_r300 r300 + gallium_enable video_cards_r600 r600 + gallium_enable video_cards_radeonsi radeonsi + if ! use video_cards_r300 && + ! use video_cards_r600; then + gallium_enable video_cards_radeon r300 r600 + fi + + if use llvm && use opencl; then + PKG_CONFIG_PATH="$(get_llvm_prefix)/$(get_libdir)/pkgconfig" + # See https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/docs/rusticl.rst + emesonargs+=( + $(meson_native_true gallium-rusticl) + -Drust_std=2021 + ) + fi + + if use vulkan; then + vulkan_enable video_cards_asahi asahi + vulkan_enable video_cards_d3d12 microsoft-experimental + vulkan_enable video_cards_freedreno freedreno + vulkan_enable video_cards_imagination imagination + vulkan_enable video_cards_intel intel intel_hasvk + vulkan_enable video_cards_lavapipe swrast + vulkan_enable video_cards_nvk nouveau + vulkan_enable video_cards_panfrost panfrost + vulkan_enable video_cards_radeonsi amd + vulkan_enable video_cards_v3d broadcom + vulkan_enable video_cards_vc4 broadcom + vulkan_enable video_cards_virgl virtio + emesonargs+=(-Dvulkan-layers=anti-lag,device-select,overlay) + fi + + driver_list() { + local drivers="$(sort -u <<< "${1// /$'\n'}")" + echo "${drivers//$'\n'/,}" + } + + if use opengl && use X; then + emesonargs+=(-Dglx=dri) + else + emesonargs+=(-Dglx=disabled) + fi + + if [[ "${ABI}" == amd64 ]]; then + emesonargs+=($(meson_feature video_cards_intel intel-rt)) + fi + + if use video_cards_asahi || + use video_cards_intel || + use video_cards_nvk || + use video_cards_panfrost || + { use vulkan && use video_cards_imagination; }; then + emesonargs+=(-Dmesa-clc=system) + fi + + if use video_cards_asahi || + use video_cards_panfrost; then + emesonargs+=(-Dprecomp-compiler=system) + fi + + use debug && EMESON_BUILDTYPE=debug + + emesonargs+=( + $(meson_use test build-tests) + -Dexpat=enabled + $(meson_use opengl) + $(meson_feature opengl gbm) + $(meson_feature opengl gles1) + $(meson_feature opengl gles2) + $(meson_feature opengl glvnd) + $(meson_feature opengl egl) + $(meson_feature llvm) + $(meson_feature lm-sensors lmsensors) + $(meson_feature unwind libunwind) + $(meson_feature vulkan display-info) + $(meson_feature zstd) + $(meson_use llvm amd-use-llvm) + $(meson_use sysprof) + $(meson_use cpu_flags_x86_sse2 sse2) + -Dvalgrind=$(usex valgrind auto disabled) + -Dvideo-codecs=$(usex proprietary-codecs "all" "all_free") + -Dgallium-drivers=$(driver_list "${GALLIUM_DRIVERS[*]}") + -Dvulkan-drivers=$(driver_list "${VULKAN_DRIVERS[*]}") + -Db_ndebug=$(usex debug false true) + -Dallow-broken-lto=true + ) + meson_src_configure +} + +multilib_src_test() { + meson_src_test -t 100 +} + +# $1 - VIDEO_CARDS flag (check skipped for "--") +# other args - names of DRI drivers to enable +gallium_enable() { + if [[ $1 == -- ]] || use $1; then + shift + GALLIUM_DRIVERS+=("$@") + fi +} + +vulkan_enable() { + if [[ $1 == -- ]] || use $1; then + shift + VULKAN_DRIVERS+=("$@") + fi +} diff --git a/media-sound/mpd/Manifest b/media-sound/mpd/Manifest index 1786877b6ea4..403bacfe5ee0 100644 --- a/media-sound/mpd/Manifest +++ b/media-sound/mpd/Manifest @@ -1,3 +1,5 @@ DIST mpd-0.24.12.tar.xz 1020148 BLAKE2B 5a6114d7085229985f09acd95566e272629c491059ee92300961e58635bd981004480584c42eed8726556fd8b92ebc9c4dfb50dddfb77dfb17b477f4ed785a20 SHA512 868e6f97c63876ad36c1aa901c5e17ac501d9feccc9bffcc8a104e786c282d3e58ad2c0f8a2bb1fdada40fb5c9a8c55f7af19f396e54e58938231463daa0a624 DIST mpd-0.24.15.tar.xz 1023324 BLAKE2B cc15e6f0ce1803ed4b50fa83e61d019c4492e4bbc7af0cb2795f4df5a5d25abe31c7d88ba4a62e1ec803277e7a11858837585d0cb29b3cabc611c7e9da1e52f9 SHA512 5d373ab224dfad85ee19c6ec02d16958e0e5b0872ab6b3746218c81938d741937542823da8bf97ebeb7cab8da98b005e5249319717e72d25a435ded71ecdbe98 DIST mpd-0.24.15.tar.xz.sig 566 BLAKE2B 51d6315040cbfe977a256acbd2b21c66c1940bad5df4a0d5b7417cb978c7094f4249189f1e3409bfd697fd4952dc3be175e32721001960b92fa08af614b7bd40 SHA512 2546e794cf0a6b748b04073fd4f0a0100113a3adcc5ffc6c6d09de3c08a435fce5d80a2cc760f960f5780215c6e4e05ed16302db886e4671c34e47a6ddce067f +DIST mpd-0.24.16.tar.xz 1039200 BLAKE2B 96b2cd7988c43da7661ad5d77db8aaf9c5291af6a8adc4e29a2256eaab0e1df338aeaf12d3106b2085c2b96fc34c4869b00008337a6d6500da20247d05c44e1e SHA512 3c68b127e361b23d1b58580609949598258a4e4b9d5f2d85d79ecfb776a876a7464c1d597dece92e731bb9a4f4341d21330f39256e8d05d4de2b5f00a596ad78 +DIST mpd-0.24.16.tar.xz.sig 566 BLAKE2B fe5654895c79b06f278eaf5abf273720fddb0092dc4355b26a9f1a2f579ced60c6f5d56ca409f56567902f68258ec17b7d9118cb6278c8437c119dd807e7d123 SHA512 322a38c5b8cc292f94c0ba72d4b995892a500884966b4cf8683757aab3664e4dd6d243e2e9161a6beb189e2d9063ddbbcb049ddb2f50cea4fb9bfb19ae550fba diff --git a/media-sound/mpd/files/mpd-0.24.16-link_smbclient.patch b/media-sound/mpd/files/mpd-0.24.16-link_smbclient.patch new file mode 100644 index 000000000000..600d198c62f0 --- /dev/null +++ b/media-sound/mpd/files/mpd-0.24.16-link_smbclient.patch @@ -0,0 +1,12 @@ +PR merged https://github.com/MusicPlayerDaemon/MPD/pull/2600.patch +fix linker failure +--- a/src/lib/smbclient/meson.build ++++ b/src/lib/smbclient/meson.build +@@ -11,6 +11,7 @@ smbclient = static_library( + 'Context.cxx', + include_directories: inc, + dependencies: [ ++ protocol_dep, + smbclient_dep, + ], + ) diff --git a/media-sound/mpd/files/mpd-0.24.16.user.confd b/media-sound/mpd/files/mpd-0.24.16.user.confd new file mode 100644 index 000000000000..7f34dfb02319 --- /dev/null +++ b/media-sound/mpd/files/mpd-0.24.16.user.confd @@ -0,0 +1,14 @@ +# Copyright 2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +#MPD_CFGFILE="${XDG_CONFIG_HOME}/mpd/mpd.conf" + +# For all command line options, please see `mpd --help` +#MPD_EXTRA_ARGS="--verbose" + +# See https://mpd.readthedocs.io/en/stable/user.html#startup +rc_ulimit="" +# Required by io-uring +#rc_ulimit="${rc_ulimit} -l 65535" +# Required for real-time +#rc_ulimit="${rc_ulimit} -r 40" diff --git a/media-sound/mpd/files/mpd-0.24.16.user.init b/media-sound/mpd/files/mpd-0.24.16.user.init new file mode 100644 index 000000000000..4ca6ab94a1c1 --- /dev/null +++ b/media-sound/mpd/files/mpd-0.24.16.user.init @@ -0,0 +1,19 @@ +#!/sbin/openrc-run +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +: ${MPD_CFGFILE:=${XDG_CONFIG_HOME:-${HOME}/.config}/mpd/mpd.conf} + +command=/usr/bin/mpd +command_args="${MPD_EXTRA_ARGS} ${MPD_CFGFILE}" +command_args_foreground="--no-daemon" +required_files="${MPD_CFGFILE}" +supervisor="supervise-daemon" + +extra_started_commands="reload" + +reload() { + ebegin "Reloading ${RC_SVCNAME}" + supervise-daemon "${RC_SVCNAME}" --signal HUP + eend $? +} diff --git a/media-sound/mpd/mpd-0.24.16.ebuild b/media-sound/mpd/mpd-0.24.16.ebuild new file mode 100644 index 000000000000..89a4629d26ca --- /dev/null +++ b/media-sound/mpd/mpd-0.24.16.ebuild @@ -0,0 +1,370 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +PYTHON_COMPAT=( python3_{13..14} ) +inherit eapi9-ver flag-o-matic linux-info meson python-any-r1 systemd verify-sig xdg + +DESCRIPTION="The Music Player Daemon (mpd)" +HOMEPAGE="https://www.musicpd.org https://github.com/MusicPlayerDaemon/MPD" +SRC_URI=" + https://www.musicpd.org/download/${PN}/$(ver_cut 1-2)/${P}.tar.xz + verify-sig? ( https://www.musicpd.org/download/${PN}/$(ver_cut 1-2)/${P}.tar.xz.sig ) +" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~amd64 ~arm ~arm64 ~ppc ~ppc64 ~riscv ~x86" +IUSE="+alsa ao audiofile bzip2 cdio chromaprint +curl doc +dbus + +eventfd expat faad +ffmpeg flac fluidsynth gme httpd +icu +id3tag +inotify + +io-uring jack lame libmpdclient libsamplerate libsoxr mad mikmod mms + modplug +mpg123 musepack nfs openal openmpt opus oss pipewire pulseaudio qobuz + recorder samba selinux shout sid signalfd snapcast sndfile sndio sqlite + systemd test tremor twolame upnp vorbis wav wavpack webdav wildmidi + zeroconf zip zlib" + +OUTPUT_PLUGINS="alsa ao jack httpd openal oss pipewire pulseaudio shout snapcast sndio recorder" +DECODER_PLUGINS="audiofile faad ffmpeg flac fluidsynth mad mikmod + modplug mpg123 musepack opus openmpt flac sid tremor vorbis wavpack wildmidi" +ENCODER_PLUGINS="flac lame opus twolame vorbis wav" + +REQUIRED_USE=" + || ( ${OUTPUT_PLUGINS} ) + || ( ${DECODER_PLUGINS} ) + ?? ( tremor vorbis ) + httpd? ( || ( ${ENCODER_PLUGINS} ) ) + recorder? ( || ( ${ENCODER_PLUGINS} ) ) + shout? ( || ( ${ENCODER_PLUGINS} ) ) + snapcast? ( wav ) + qobuz? ( curl ) + upnp? ( curl expat ) + webdav? ( curl expat ) +" + +RESTRICT="!test? ( test )" + +COMMON_ENCODERS=" + lame? ( media-sound/lame ) + twolame? ( media-sound/twolame ) +" +RDEPEND=" + acct-user/mpd + dev-libs/libfmt:= + dev-libs/libpcre2:= + alsa? ( + media-libs/alsa-lib + media-sound/alsa-utils + ) + ao? ( media-libs/libao[alsa?,pulseaudio?] ) + audiofile? ( media-libs/audiofile:= ) + bzip2? ( app-arch/bzip2 ) + cdio? ( + dev-libs/libcdio:= + dev-libs/libcdio-paranoia:= + ) + chromaprint? ( media-libs/chromaprint:= ) + curl? ( net-misc/curl ) + dbus? ( sys-apps/dbus ) + expat? ( dev-libs/expat ) + faad? ( media-libs/faad2 ) + ffmpeg? ( media-video/ffmpeg:= ) + flac? ( media-libs/flac:= ) + fluidsynth? ( media-sound/fluidsynth:= ) + gme? ( media-libs/game-music-emu ) + httpd? ( ${COMMON_ENCODERS} ) + icu? ( + dev-libs/icu:= + virtual/libiconv + ) + id3tag? ( media-libs/libid3tag:= ) + io-uring? ( sys-libs/liburing:= ) + jack? ( virtual/jack ) + libmpdclient? ( media-libs/libmpdclient ) + libsamplerate? ( media-libs/libsamplerate ) + libsoxr? ( media-libs/soxr ) + mad? ( media-libs/libmad ) + mikmod? ( media-libs/libmikmod ) + mms? ( media-libs/libmms ) + modplug? ( media-libs/libmodplug ) + mpg123? ( media-sound/mpg123-base ) + musepack? ( media-sound/musepack-tools ) + nfs? ( net-fs/libnfs:= ) + openal? ( media-libs/openal ) + openmpt? ( media-libs/libopenmpt ) + opus? ( + media-libs/libogg + media-libs/opus + ) + pulseaudio? ( media-libs/libpulse ) + pipewire? ( media-video/pipewire:= ) + qobuz? ( + !ffmpeg? ( dev-libs/libgcrypt:= ) + ffmpeg? ( media-video/ffmpeg ) + ) + recorder? ( ${COMMON_ENCODERS} ) + samba? ( net-fs/samba:= ) + selinux? ( sec-policy/selinux-mpd ) + shout? ( + ${COMMON_ENCODERS} + media-libs/libshout + ) + sid? ( media-libs/libsidplayfp:= ) + sndfile? ( media-libs/libsndfile ) + sndio? ( media-sound/sndio:= ) + sqlite? ( dev-db/sqlite:3 ) + systemd? ( sys-apps/systemd:= ) + tremor? ( + media-libs/libogg + media-libs/tremor + ) + upnp? ( <net-libs/libupnp-1.18.0 ) + vorbis? ( + media-libs/libogg + media-libs/libvorbis + ) + wavpack? ( media-sound/wavpack ) + wildmidi? ( media-sound/wildmidi ) + zeroconf? ( net-dns/avahi[dbus] ) + zip? ( dev-libs/zziplib:= ) + zlib? ( virtual/zlib:= ) +" +DEPEND=" + ${RDEPEND} + qobuz? ( >=dev-cpp/nlohmann_json-3.11.3 ) + snapcast? ( >=dev-cpp/nlohmann_json-3.11.3 ) + test? ( dev-cpp/gtest ) +" +BDEPEND=" + virtual/pkgconfig + doc? ( + $(python_gen_any_dep ' + dev-python/sphinx[${PYTHON_USEDEP}] + dev-python/sphinx-rtd-theme[${PYTHON_USEDEP}] + ') + ) + verify-sig? ( sec-keys/openpgp-keys-mpd ) +" + +VERIFY_SIG_OPENPGP_KEY_PATH=/usr/share/openpgp-keys/${PN}.asc + +PATCHES=( + # PR merged + "${FILESDIR}"/${P}-link_smbclient.patch +) + +python_check_deps() { + use doc || return 0 + python_has_version "dev-python/sphinx[${PYTHON_USEDEP}]" && + python_has_version "dev-python/sphinx-rtd-theme[${PYTHON_USEDEP}]" +} + +pkg_setup() { + use doc && python-any-r1_pkg_setup + + if use eventfd; then + CONFIG_CHECK+=" ~EVENTFD" + ERROR_EVENTFD="${P} requires eventfd in-kernel support." + fi + + if use signalfd; then + CONFIG_CHECK+=" ~SIGNALFD" + ERROR_SIGNALFD="${P} requires signalfd in-kernel support." + fi + + if use inotify; then + CONFIG_CHECK+=" ~INOTIFY_USER" + ERROR_INOTIFY_USER="${P} requires inotify in-kernel support." + fi + + if use io-uring; then + CONFIG_CHECK+=" ~IO_URING" + ERROR_IO_URING="${P} requires io-uring in-kernel support." + fi + + if use eventfd || use signalfd || use inotify || use io-uring; then + linux-info_pkg_setup + fi +} + +src_configure() { + local emesonargs=( + # media-libs/adplug is not packaged anymore + -Dadplug=disabled + $(meson_feature alsa) + $(meson_feature ao) + $(meson_feature audiofile) + $(meson_feature bzip2) + $(meson_feature cdio cdio_paranoia) + $(meson_feature chromaprint) + -Dcue=true + $(meson_feature curl) + $(meson_feature dbus) + $(meson_use eventfd) + $(meson_feature expat) + $(meson_feature faad) + $(meson_feature ffmpeg) + -Dfifo=true + $(meson_feature flac) + $(meson_feature fluidsynth) + $(meson_feature gme) + $(meson_use httpd ) + $(meson_feature icu) + $(meson_feature id3tag) + $(meson_use inotify) + -Dipv6=enabled + $(meson_feature cdio iso9660) + $(meson_feature io-uring io_uring) + $(meson_feature jack) + $(meson_feature libmpdclient) + $(meson_feature libsamplerate) + $(meson_feature mad) + $(meson_feature mikmod) + $(meson_feature mms) + $(meson_feature modplug) + $(meson_feature musepack mpcdec) + $(meson_feature mpg123) + $(meson_feature nfs) + $(meson_feature openal) + $(meson_feature openmpt) + $(meson_feature opus) + $(meson_feature oss) + -Dpipe=true + $(meson_feature pipewire) + $(meson_feature pulseaudio pulse) + $(meson_feature qobuz) + $(meson_use recorder) + $(meson_feature shout) + $(meson_use signalfd) + $(meson_feature samba smbclient) + $(meson_use snapcast) + $(meson_feature sid sidplay) + $(meson_feature sndfile) + $(meson_feature sndio) + $(meson_feature libsoxr soxr) + $(meson_feature sqlite) + $(meson_feature systemd) + $(meson_use test) + $(meson_feature dbus udisks) + -Dupnp=$(usex upnp pupnp disabled) + $(meson_feature tremor) + $(meson_feature vorbis) + $(meson_use wav wave_encoder) + $(meson_feature wavpack) + $(meson_feature wildmidi) + $(meson_feature webdav) + -Dzeroconf=$(usex zeroconf avahi disabled) + $(meson_feature zlib) + $(meson_feature zip zzip) + + --libdir="/usr/$(get_libdir)" + $(meson_feature doc documentation) + -Dsolaris_output=disabled + + -Ddatabase=true + -Ddaemon=true + -Ddsd=true + -Dtcp=true + + -Dsystemd_system_unit_dir="$(systemd_get_systemunitdir)" + -Dsystemd_user_unit_dir="$(systemd_get_userunitdir)" + + $(meson_feature icu iconv) + ) + + if use samba || use upnp; then + emesonargs+=( -Dneighbor=true ) + fi + + append-lfs-flags + + # set useflag for encoders + if use httpd || use shout || use recorder; then + emesonargs+=( + # not in tree + -Dshine=disabled + $(meson_feature lame) + $(meson_feature twolame) + $(meson_feature vorbis vorbisenc) + ) + else + # avoid links even w/o encoder + emesonargs+=( + -Dlame=disabled + -Dtwolame=disabled + ) + fi + + # nlohmann_json is only required with these plugins enabled + if use qobuz || use snapcast; then + emesonargs+=( + -Dnlohmann_json=enabled + ) + fi + + meson_src_configure +} + +src_install() { + if use doc; then + local HTML_DOCS=( "${BUILD_DIR}"/doc/html/. ) + else + newman "${FILESDIR}"/${PN}.1-0.24.2 ${PN}.1 + newman "${FILESDIR}"/${PN}.conf.5-0.24.9 ${PN}.conf.5 + fi + + meson_src_install + + insinto /etc + newins doc/mpdconf.example mpd.conf + + if ! use systemd; then + # define a log_file that should not be set when using systemd + sed -e '0,/^#log_file.*$/s::log_file "/var/log/mpd/mpd.log"\n&:' \ + -i "${ED}/etc/mpd.conf" || die + fi + + insinto /etc/logrotate.d + newins "${FILESDIR}/${PN}-0.23.15.logrotate" "${PN}" + + newinitd "${FILESDIR}/${PN}-0.24.15.init" "${PN}" + newconfd "${FILESDIR}/${PN}-0.24.15.confd" "${PN}" + + exeinto /etc/user/init.d + newexe "${FILESDIR}/${PN}-0.24.16.user.init" "${PN}" + exeinto /etc/user/conf.d + newexe "${FILESDIR}/${PN}-0.24.16.user.confd" "${PN}" + + # set memlock limit for io-uring + # see https://mpd.readthedocs.io/en/latest/user.html#startup + if use io-uring; then + sed -e '/#rc_ulimit="${rc_ulimit} -l/s/^#//' \ + -i "${ED}/etc/conf.d/mpd" "${ED}/etc/user/conf.d/mpd" || die + fi + + keepdir /var/lib/mpd + keepdir /var/lib/mpd/music + keepdir /var/lib/mpd/playlists + keepdir /var/log/mpd + + rm -r "${ED}"/usr/share/doc/mpd || die + + fowners mpd: -R /var/lib/mpd + fowners mpd: -R /var/log/mpd +} + +pkg_postinst() { + xdg_pkg_postinst + + if ver_replacing -lt 0.24.8-r1; then + ewarn "The 'group' parameter is no longer used by default, because it" + ewarn "overrides the group(s) defined in the user database." + ewarn "Since the user 'mpd' is already part of the 'audio' group, please" + ewarn "consider removing 'group' parameter in ${EROOT}/etc/mpd.conf ." + fi + if ver_replacing -lt 0.24.15; then + ewarn "The daemon is now started directly as an unprivileged user." + ewarn "Update MPD_USER in ${EROOT}/etc/conf.d/mpd to override the default user 'mpd'." + ewarn "'user' and 'pid_file' must no longer be set in ${EROOT}/etc/mpd.conf ." + fi +} diff --git a/media-video/ffmpeg/ffmpeg-9999.ebuild b/media-video/ffmpeg/ffmpeg-9999.ebuild index 3d51f555141a..efb4d7be615b 100644 --- a/media-video/ffmpeg/ffmpeg-9999.ebuild +++ b/media-video/ffmpeg/ffmpeg-9999.ebuild @@ -96,7 +96,6 @@ FFMPEG_IUSE_MAP=( ocr:libtesseract openal opencl - opengl openh264:libopenh264 openmpt:libopenmpt openssl:openssl,!gnutls@v3ifgpl # still LGPL2.1+ if USE=-gpl @@ -249,7 +248,6 @@ COMMON_DEPEND=" ocr? ( app-text/tesseract:=[${MULTILIB_USEDEP}] ) openal? ( media-libs/openal[${MULTILIB_USEDEP}] ) opencl? ( virtual/opencl[${MULTILIB_USEDEP}] ) - opengl? ( media-libs/libglvnd[X,${MULTILIB_USEDEP}] ) openh264? ( media-libs/openh264:=[${MULTILIB_USEDEP}] ) openmpt? ( media-libs/libopenmpt[${MULTILIB_USEDEP}] ) openssl? ( >=dev-libs/openssl-3:=[${MULTILIB_USEDEP}] ) diff --git a/net-analyzer/gr-fosphor/gr-fosphor-0.0_p20260330-r1.ebuild b/net-analyzer/gr-fosphor/gr-fosphor-0.0_p20260330-r1.ebuild index 8d35f0669a8d..95a253e72ef9 100644 --- a/net-analyzer/gr-fosphor/gr-fosphor-0.0_p20260330-r1.ebuild +++ b/net-analyzer/gr-fosphor/gr-fosphor-0.0_p20260330-r1.ebuild @@ -37,7 +37,9 @@ RDEPEND="${PYTHON_DEPS} virtual/opencl virtual/opengl " -DEPEND="${RDEPEND}" +DEPEND="${RDEPEND} + dev-util/opencl-headers +" BDEPEND="$(python_gen_cond_dep 'dev-python/pybind11[${PYTHON_USEDEP}]')" PATCHES=( diff --git a/net-analyzer/gr-fosphor/gr-fosphor-9999.ebuild b/net-analyzer/gr-fosphor/gr-fosphor-9999.ebuild index 0aca85c2bcc0..bf2427017850 100644 --- a/net-analyzer/gr-fosphor/gr-fosphor-9999.ebuild +++ b/net-analyzer/gr-fosphor/gr-fosphor-9999.ebuild @@ -37,7 +37,9 @@ RDEPEND="${PYTHON_DEPS} virtual/opencl virtual/opengl " -DEPEND="${RDEPEND}" +DEPEND="${RDEPEND} + dev-util/opencl-headers +" BDEPEND="$(python_gen_cond_dep 'dev-python/pybind11[${PYTHON_USEDEP}]')" src_prepare() { diff --git a/net-dns/bind/Manifest b/net-dns/bind/Manifest index e611e7bff520..2adb6e73c84b 100644 --- a/net-dns/bind/Manifest +++ b/net-dns/bind/Manifest @@ -2,3 +2,5 @@ DIST bind-9.20.27.tar.xz 5932248 BLAKE2B 7f2f0f030748e722d12800bae567f9cfb1ddeba DIST bind-9.20.27.tar.xz.asc 870 BLAKE2B e24deaa0cbc186b31b86e983e5c9f2c53550d8978d84e291d67027cf558d4b39b2fe6e217e3bc61685645bc249a1576087915980420a3336c67f6992ba05227a SHA512 255a523bb73c94e04c4cfd6b00a60bc57a1351767fef2421258e358ca5c13a359e53d1b6921792447b655aac7911fc22cd2ded8e5b114221c08fa7bc17d3a154 DIST bind-9.20.29.tar.xz 5966372 BLAKE2B 0707c48f2d8eb898578bf1d6c2e626e17f93b50394855dd697ccd7e8b98c0926366112cc7dab9f6becd3d01210958e365bcc60142558cdf4ce28002dcc2906de SHA512 afedc73a1376d662eec86b7f0e5795a3b0302e7184af83f90d06e3b5054efb54f5d7ab73f777e0d1b84b25aa27f299de688c508e28f0a22e67ba5eb07aa46077 DIST bind-9.20.29.tar.xz.asc 870 BLAKE2B a3c564fb898fa4cf00ef5bf47339f9cfcad78180cf6bdd723e243854b53f3bc7549e3074ea6d2ea3852bb68b5910c5270d9e4abc376c6c839f7dea91b31719c7 SHA512 456be1551ccb858da7de072e51b1c5c9a3688cae3ac0d070ff84dcd3cb6a2ccfdf5afca3c06cf44089abcf5ae8f24f626fc5ded84bce521494dd7c56e6a786f2 +DIST bind-9.20.30.tar.xz 6004232 BLAKE2B 5ab0bba53c631817984d03a630f2f71a52a06a5a17bae34229af723e9acddcd13d12c4f40a90ecdb1967ce2fa8406977a84590985a143b38dc93c5894839424a SHA512 3717897d35311e2056f0c0cb7d97c32348b4e2202762f3b98dd0e405f78fa2281e5f7f07341df67e58c303f1c7acb81e707dd727db7b2e5592f1af549eae3325 +DIST bind-9.20.30.tar.xz.asc 870 BLAKE2B d1d60ab0d7318cb44280826046c28ac330655c96504b9f5618a63f9a33de3f9c314d6dcd36063e14fb97f021dc0fc8444ab10bd192b6d3f65ec82681fd290517 SHA512 149d38eb30f2b66207a691311e9d83a06ef8d56c8a491f87d84ccd2bd75316b5f4cdef2f1c84fe5b6b6da2d254ed389008825184dd88099a70f7fe0b3d7086c9 diff --git a/net-dns/bind/bind-9.20.30.ebuild b/net-dns/bind/bind-9.20.30.ebuild new file mode 100644 index 000000000000..f978e32366ab --- /dev/null +++ b/net-dns/bind/bind-9.20.30.ebuild @@ -0,0 +1,286 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +VERIFY_SIG_OPENPGP_KEY_PATH=/usr/share/openpgp-keys/isc.asc +inherit autotools eapi9-ver systemd tmpfiles toolchain-funcs verify-sig + +MY_PV="${PV/_p/-P}" +MY_PV="${MY_PV/_rc/rc}" + +DESCRIPTION="Berkeley Internet Name Domain - Name Server" +HOMEPAGE="https://www.isc.org/bind/" +SRC_URI=" + https://downloads.isc.org/isc/bind9/${PV}/${P}.tar.xz + verify-sig? ( https://downloads.isc.org/isc/bind9/${PV}/${P}.tar.xz.asc ) +" +S="${WORKDIR}/${PN}-${MY_PV}" + +LICENSE="MPL-2.0" +SLOT="0" +KEYWORDS="~alpha amd64 ~arm arm64 ~hppa ~loong ~mips ppc ppc64 ~riscv ~s390 ~sparc x86" +IUSE="dnstap doc doh fixed-rrset idn jemalloc geoip gssapi lmdb selinux systemtap test xml" +RESTRICT="!test? ( test )" + +DEPEND=" + acct-group/named + acct-user/named + dev-libs/json-c:= + dev-libs/userspace-rcu:= + >=dev-libs/libuv-1.37.0:= + virtual/zlib:= + dev-libs/openssl:=[-bindist(-)] + >=sys-libs/libcap-2.1.0 + dnstap? ( + dev-libs/fstrm + dev-libs/protobuf-c + ) + doh? ( net-libs/nghttp2:= ) + geoip? ( dev-libs/libmaxminddb ) + gssapi? ( virtual/krb5 ) + idn? ( net-dns/libidn2 ) + jemalloc? ( dev-libs/jemalloc:= ) + lmdb? ( dev-db/lmdb ) + xml? ( dev-libs/libxml2:= ) +" +RDEPEND=" + ${DEPEND} + selinux? ( sec-policy/selinux-bind ) + sys-process/psmisc + !<net-dns/bind-tools-9.18.0 +" +# sphinx required for man-page and html creation +BDEPEND=" + dev-lang/perl + virtual/pkgconfig + doc? ( dev-python/sphinx ) + test? ( dev-util/cmocka ) + systemtap? ( dev-debug/systemtap ) + verify-sig? ( sec-keys/openpgp-keys-isc ) +" + +src_prepare() { + default + eautoreconf + + # Don't clobber our toolchain defaults + sed -i -e '/FORTIFY_SOURCE=/d' configure || die + + # Relies on -Wl,--wrap (bug #877741) + if tc-is-lto ; then + sed -i -e 's:ISC_TEST_MAIN:int main(void) { exit(77); }:' tests/ns/query_test.c || die + fi +} + +src_configure() { + # configure automagically uses sphinx even if prebuilt man pages + # are available. Force fallback to prebuilt ones. + use doc || export ac_cv_path_SPHINX_BUILD= SPHINX_BUILD= + + # Workaround for bug #938302 + if use systemtap && has_version "dev-debug/systemtap[-dtrace-symlink(+)]" ; then + export DTRACE="${BROOT}"/usr/bin/stap-dtrace + fi + + local myeconfargs=( + --prefix="${EPREFIX}"/usr + --sysconfdir="${EPREFIX}"/etc/bind + --localstatedir="${EPREFIX}"/var + --enable-full-report + --without-readline + --with-openssl="${ESYSROOT}"/usr + --with-json-c + --with-zlib + --disable-dnsrps + $(use_enable dnstap) + $(use_enable doh) + $(use_with doh libnghttp2) + $(use_enable geoip) + $(use_enable systemtap tracing) + $(use_with test cmocka) + $(use_with geoip maxminddb) + $(use_with gssapi) + $(use_with idn libidn2) + $(use_with jemalloc) + $(use_with lmdb) + $(use_with xml libxml2) + ) + + econf "${myeconfargs[@]}" +} + +src_test() { + # system tests ('emake test') require network configuration for IPs etc + # so we run the unit tests instead. + CI=1 emake unit V=1 + + # libtest is an internal test helper library, it has no tests, + # so suppress the QA warning. + rm tests/libtest/test-suite.log || die +} + +src_install() { + default + + if use doc; then + docinto misc + dodoc -r doc/misc/ + + docinto html + dodoc -r doc/arm/ + + docinto dnssec-guide + dodoc -r doc/dnssec-guide/ + + docinto contrib + dodoc contrib/scripts/nanny.pl + fi + + insinto /etc/bind + newins "${FILESDIR}"/named.conf-r9 named.conf + newins "${FILESDIR}"/named.conf.auth named.conf.auth + + newinitd "${FILESDIR}"/named.init-r15 named + newconfd "${FILESDIR}"/named.confd-r8 named + + newenvd "${FILESDIR}"/10bind.env 10bind + + find "${ED}"/usr/lib* -name '*.la' -delete || die + + # + # /var/bind + # + # These need to remain for now because CONFIG_PROTECT won't + # save them and we shipped configs for years containing references + # to them. + # + # ftp://ftp.rs.internic.net/domain/named.cache: + insinto /var/bind + newins "${FILESDIR}"/named.cache-r4 named.cache + # bug #450406 + dosym named.cache /var/bind/root.cache + # + insinto /var/bind/pri + newins "${FILESDIR}"/localhost.zone-r3 localhost.zone + + dosym -r /var/bind/pri /etc/bind/pri + dosym -r /var/bind/sec /etc/bind/sec + dosym -r /var/bind/dyn /etc/bind/dyn + keepdir /var/bind/{pri,sec,dyn} /var/log/named + + fowners root:named /{etc,var}/bind /var/log/named /var/bind/{sec,pri,dyn} + fowners root:named /etc/bind/{named.conf,named.conf.auth} + fperms 0640 /etc/bind/{named.conf,named.conf.auth} + fperms 0750 /etc/bind /var/bind/pri + fperms 0770 /var/log/named /var/bind/{,sec,dyn} + + systemd_newunit "${FILESDIR}/named.service-r2" named.service + dotmpfiles "${FILESDIR}"/named.conf + exeinto /usr/libexec + doexe "${FILESDIR}/generate-rndc-key.sh" +} + +pkg_postinst() { + tmpfiles_process named.conf + + if [[ -z ${ROOT} && ! -f ${EPREFIX}/etc/bind/rndc.key && ! -f ${EPREFIX}/etc/bind/rndc.conf ]]; then + einfo "Generating rndc.key" + "${EPREFIX}"/usr/sbin/rndc-confgen -a || die + chown root:named "${EPREFIX}"/etc/bind/rndc.key || die + chmod 0640 "${EPREFIX}"/etc/bind/rndc.key || die + fi + + einfo + einfo "You can edit /etc/conf.d/named to customize named settings" + einfo + + einfo "If you'd like to run bind in a chroot AND this is a new" + einfo "install OR your bind doesn't already run in a chroot:" + einfo "1) Uncomment and set the CHROOT variable in /etc/conf.d/named." + einfo "2) Run \`emerge --config '=${CATEGORY}/${PF}'\`" + einfo + + CHROOT=$(source "${EROOT}"/etc/conf.d/named 2>/dev/null; echo ${CHROOT}) + if [[ -n ${CHROOT} ]]; then + elog "NOTE: As of net-dns/bind-9.4.3_p5-r1 the chroot part of the init-script got some major changes!" + elog "To enable the old behaviour (without using mount) uncomment the" + elog "CHROOT_NOMOUNT option in your /etc/conf.d/named config." + elog "If you decide to use the new/default method, ensure to make backup" + elog "first and merge your existing configs/zones to /etc/bind and" + elog "/var/bind because bind will now mount the needed directories into" + elog "the chroot dir." + fi + + # show only when upgrading to 9.18 + if ver_replacing -lt 9.18; then + elog "As this is a major bind version upgrade, please read:" + elog " https://kb.isc.org/docs/changes-to-be-aware-of-when-moving-from-bind-916-to-918" + elog "for differences in functionality." + elog "" + ewarn "In particular, please note that bind-9.18 does not need a root hints file anymore" + ewarn "and we only ship with one as a stop-gap. If your current configuration specifies a" + ewarn "root hints file - usually called named.cache - bind will not start as it will not be able" + ewarn "to find the specified file. Best practice is to delete the offending lines that" + ewarn "reference named.cache file from your configuration." + fi +} + +pkg_config() { + CHROOT=$(source "${EROOT}"/etc/conf.d/named; echo ${CHROOT}) + CHROOT_NOMOUNT=$(source "${EROOT}"/etc/conf.d/named; echo ${CHROOT_NOMOUNT}) + CHROOT_GEOIP=$(source "${EROOT}"/etc/conf.d/named; echo ${CHROOT_GEOIP}) + + if [[ -z "${CHROOT}" ]]; then + eerror "This config script is designed to automate setting up" + eerror "a chrooted bind/named. To do so, please first uncomment" + eerror "and set the CHROOT variable in '/etc/conf.d/named'." + die "Unset CHROOT" + fi + + if [[ -d "${CHROOT}" ]]; then + ewarn "NOTE: As of net-dns/bind-9.4.3_p5-r1 the chroot part of the init-script got some major changes!" + ewarn "To enable the old behaviour (without using mount) uncomment the" + ewarn "CHROOT_NOMOUNT option in your /etc/conf.d/named config." + ewarn + ewarn "${CHROOT} already exists... some things might become overridden" + ewarn "press CTRL+C if you don't want to continue" + sleep 10 + fi + + echo; einfo "Setting up the chroot directory..." + + mkdir -m 0750 -p "${CHROOT}" || die + mkdir -m 0755 -p "${CHROOT}"/{dev,etc,var/log,run} || die + mkdir -m 0750 -p "${CHROOT}"/etc/bind || die + mkdir -m 0770 -p "${CHROOT}"/var/{bind,log/named,run/named} "${CHROOT}"/run/named/ || die + + chown root:named \ + "${CHROOT}" \ + "${CHROOT}"/var/{bind,log/named,run/named} \ + "${CHROOT}"/run/named/ \ + "${CHROOT}"/etc/bind \ + || die + + mknod "${CHROOT}"/dev/null c 1 3 || die + chmod 0666 "${CHROOT}"/dev/null || die + + mknod "${CHROOT}"/dev/zero c 1 5 || die + chmod 0666 "${CHROOT}"/dev/zero || die + + if [[ "${CHROOT_NOMOUNT:-0}" -ne 0 ]]; then + cp -a /etc/bind "${CHROOT}"/etc/ || die + cp -a /var/bind "${CHROOT}"/var/ || die + fi + + if [[ "${CHROOT_GEOIP:-0}" -eq 1 ]]; then + if use geoip; then + mkdir -m 0755 -p "${CHROOT}"/usr/share/GeoIP || die + elif use geoip2; then + mkdir -m 0755 -p "${CHROOT}"/usr/share/GeoIP2 || die + fi + fi + + elog "You may need to add the following line to your syslog-ng.conf:" + elog "source jail { unix-stream(\"${CHROOT}/dev/log\"); };" +} diff --git a/net-dns/c-ares/Manifest b/net-dns/c-ares/Manifest index d7c992871d50..431f39bdd1c9 100644 --- a/net-dns/c-ares/Manifest +++ b/net-dns/c-ares/Manifest @@ -2,3 +2,5 @@ DIST c-ares-1.34.6.tar.gz 1017864 BLAKE2B 3a348cfaf4c992ba1712be73907599fd9d3e7b DIST c-ares-1.34.6.tar.gz.asc 228 BLAKE2B 047ed363c58840914ec6fe6f81fe51ce6aca1293b91cf48c9f476a013f2267cd728a97d600e172ef51267f4ade63c4c4ed381ed5c37c83d541df1d4dc41f0e44 SHA512 864e9bb1703729db1554f68e07432de6e1f364f9bf565e1dc51dfbd4a87ae8751d9e4b1a5c607455457dc2c1054ba687f33365909d8a4888644d99c5e518bbce DIST c-ares-1.34.8.tar.gz 1036098 BLAKE2B b5a88811d8ee6a0c06737746a356bc1fc7c13416b11a0af3a2e6cab092d97adebb93234cf73b97a12b06b764f8562272a793961dc3367024c4322d923ac43f9f SHA512 d5997a57fda9b3a3385b1685e6b2f9292c3a1d39bd27579acaabf3d2d33aa5e91bfec5d41c5e51ca6b50dbc1f4346d75e9bfbccf44a494fbd4bc7613b167143c DIST c-ares-1.34.8.tar.gz.asc 265 BLAKE2B 5981f7509e6236dc7b3f3c181fbb1f717b880a20ee03730e54ffa51a3660e9b29210699454e4f7f0388ce837826044a6e0a7f722d87ff42954f093ab4514829c SHA512 bef786b9dcf23cdcddf2d07c4aa56564869c44fa34732c49d612184297b48b98d5b591cda13d52060a3b9db396b4348f022222eb13a4283b929ed905c7c5d412 +DIST c-ares-1.34.9.tar.gz 1083895 BLAKE2B b708d8e302f7b2b8dcd9a30b9faa0864506990ced276ad4718b2cc3bbabbd548dcf3493033e777787089ba58c60e65f84d6ff47097a93cf1f3367490a3b00ee8 SHA512 f4be5818d14cec48e96cc66d3f7dea3de60e3ae3d990972cf2e055140ad995a3bbf38f2a6ffd5c671f04b5f105f28ef022af721477a2d126be0803355c7c297a +DIST c-ares-1.34.9.tar.gz.asc 265 BLAKE2B ec5ad2601040b63e50c0b0f05a3671a21c308c12c8a8af1758927edd9a3211f622731f2b68c87d7dcb8c53e62b2a50b7c047353726de5f78151c59d184edb802 SHA512 d0070f209d601c3386059e22fbec2ffb31d932d314381b4b24c62187409fdfb46cef9b2c210e891bc3c732966be8c67fb4e4bf8af546755faca142dc66fc410c diff --git a/net-dns/c-ares/c-ares-1.34.9.ebuild b/net-dns/c-ares/c-ares-1.34.9.ebuild new file mode 100644 index 000000000000..2649ec4d8a76 --- /dev/null +++ b/net-dns/c-ares/c-ares-1.34.9.ebuild @@ -0,0 +1,112 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +# Both Daniel and Brad are listed as possible signers on the homepage +VERIFY_SIG_OPENPGP_KEY_PATH=/usr/share/openpgp-keys/bradhouse.asc +inherit edo libtool multilib-minimal verify-sig + +DESCRIPTION="C library that resolves names asynchronously" +HOMEPAGE="https://c-ares.org/" +SRC_URI=" + https://github.com/c-ares/c-ares/releases/download/v${PV}/${P}.tar.gz + verify-sig? ( https://github.com/c-ares/c-ares/releases/download/v${PV}/${P}.tar.gz.asc ) +" + +# ISC for lib/{bitncmp.c,inet_ntop.c,inet_net_pton.c} (bug #912405) +LICENSE="MIT ISC" +# Subslot = SONAME of libcares.so.2 +SLOT="0/2" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~arm64-macos ~x64-macos ~x64-solaris" +IUSE="static-libs test" +RESTRICT="!test? ( test )" + +BDEPEND=" + test? ( dev-cpp/gtest ) + verify-sig? ( sec-keys/openpgp-keys-bradhouse ) +" + +DOCS=( AUTHORS README.md RELEASE-NOTES.md ) + +MULTILIB_WRAPPED_HEADERS=( + /usr/include/ares_build.h +) + +A__QA_CONFIG_IMPL_DECL_SKIP=( + # Checking for obsolete headers + malloc + calloc + free + + # Non-existent on Linux + closesocket + CloseSocket + ioctlsocket + bitncmp + ConvertInterfaceIndexToLuid + ConvertInterfaceLuidToNameA +) + +src_prepare() { + default + elibtoolize + + if [[ ${CHOST} == *-darwin* ]] ; then + # warnings are default, but enable -std=c90 which doesn't define + # 'bool' which is a type used/assumed in macOS system headers + sed -i -e 's/-std=c90/& -Dbool=int/' configure{.ac,} || die + # sysconfig integration requires deep framework compatibility + # and is not really desired in Prefix + sed -i -e 's/__APPLE__/__DISABLED__/' \ + src/lib/ares_sysconfig_mac.c || die + sed -i -e '/elif defined(__APPLE__)/s/__APPLE__/__DISABLED__/' \ + src/lib/ares_sysconfig.c || die + fi +} + +multilib_src_configure() { + local myeconfargs=( + --enable-symbol-hiding + $(use_enable static-libs static) + $(use_enable test tests) + ) + + # Needed for running unit tests only + # Violates sandbox and tests pass fine without + export ares_cv_user_namespace=no + export ares_cv_uts_namespace=no + ECONF_SOURCE="${S}" econf "${myeconfargs[@]}" +} + +multilib_src_test() { + cd "${BUILD_DIR}"/test || die + + # We're skipping the "real" network tests with the filter + # see https://github.com/c-ares/c-ares/tree/main/test + local network_tests=( + # Most live tests have Live in the name + *Live* + # These don't but are still in ares-test-live.cc => live + *GetTCPSock* + *TimeoutValue* + *GetSock* + *GetSock_virtualized* + *VerifySocketFunctionCallback* + # Seems flaky, even run manually + # https://github.com/c-ares/c-ares/commit/9e542a8839f81c990bb0dff14beeaf9aa6bcc18d + *MockUDPMaxQueriesTest.GetHostByNameParallelLookups* + # Live again + *SockStateDeliveryTest* + ) + + # The format for disabling test1, test2, and test3 looks like: + # -test1:test2:test3 + edo ./arestest --gtest_filter=-$(echo $(IFS=:; echo "${network_tests[*]}")) +} + +multilib_src_install_all() { + einstalldocs + + find "${ED}" -name "*.la" -delete || die +} diff --git a/net-dns/knot/Manifest b/net-dns/knot/Manifest index 59cb58b03796..c573ad4c9522 100644 --- a/net-dns/knot/Manifest +++ b/net-dns/knot/Manifest @@ -1,12 +1,10 @@ -DIST knot-3.5.5-manpages.tar.xz 41160 BLAKE2B 9dea7dd8500e82712f19816f16a00a4d3cb4fee1fd426c966052386472873438e7b63a64b49dccdb2a460fea1f5e9e2f1b8f27f16ce1a908c03ac4e1d367cf03 SHA512 eac4c2571b997809b8e4fedcdec51992ed6642877b022dafa80d3cfa9a0ed85a13b3fda8785b5a998de4756f72add4a399d9417968d978f522a1bbd90045307a -DIST knot-3.5.5.tar.xz 1734192 BLAKE2B 5ee478109a26e3b163da169e7e22a03780727528f84953e399f25670d48392b6b407388c38b152bc90399f517b70676168735c1aba4028326237b92cdf904938 SHA512 983789c7fbbb191d4bd8e9f13050a5d5310a8bc69184b6d3440711fff88fa631ca6703b0800dd5581bd84850b4253dddfb36c2bdd46f6f158333b5ad934fdfdc -DIST knot-3.5.5.tar.xz.asc 833 BLAKE2B 478aa1fba542f1e5cf00ea35af2d99cf2fe1b88e566dbc056696542e74508f58790c14a88e74a09bb06247a811a58bd4534e7d36cea6825f2111b9020c21e190 SHA512 b20be4fde7d62954898e8e1e9b9d81cad5a543d7d7636fcccebaa313905491c900f7c02c0782b8287f6262736bd08a1a208cda5557776dbeed8aaab589ef1b68 -DIST knot-3.5.6-manpages.tar.xz 41136 BLAKE2B 43b295092f42418566206600585f994c7a46498bacc40e0fa5f3a9ab441715c35a95757ee91e4d1b51a317e58b37fc3b248c33a6f77b7b72d6560c93a5341bdf SHA512 41af9e9bba8a06c9902b17a65052e9b0d9ba0cf79b15134181e0c58ecbcab770ef6f9d10a54d1d1707e56ab54953c28c39331dad9a013b54ad59df3402f951e0 -DIST knot-3.5.6.tar.xz 1734776 BLAKE2B f03a2da9390e89bd7955505d3f59ca54689e2183b1ea0f82efbf5abedd71f16902c6d09f77adceaedd29573d52135c168441dbf46721709b370269220b68f49b SHA512 bd1818552ab076a007aae2d14fdae053e7ffabb37bcfd6cd4485b866fe2d80675f3a4367a87afe4c4a32ea529a503f2b6857632eb74f7cac0598f15b093d286b -DIST knot-3.5.6.tar.xz.asc 833 BLAKE2B e50abb5e54db106e965ea01d8bed5ee183cc98922368d58144552cb2a6a55af738abd44195624397fc965fead72a687c8cd76554f60ea34013a6b2e6e94652ef SHA512 5647f932b145d6a4fd12e205fe895fe18df67a3e9c33a8bb3fc93b93762bbe46a359b86cf2482e0089eddb8fbe9fe22f306df94527804c9b3bf35631914c1101 +DIST knot-3.5.10.tar.xz 1772508 BLAKE2B 6dac5723600c11b78a58a6986f922c6235489620ae66f70339bb021fb9d3d0c66e523cd38ca2c4cf2864804a4190058b9f1c8754ebe5e66b607abcb09edef0b4 SHA512 1e3c65e8f336b54c73b3fa5ee0b600c28243ba0a8909b814658d5a87388f518e5340e10562d913d1c999ea969cefda64b01354cf792fb0661751b9c4e93a66a1 +DIST knot-3.5.10.tar.xz.asc 833 BLAKE2B 84df2f415667704bf076e8d989d9de84c9dd44df153878e5032ff9b863ac11257e3f63a8ea7924ed4c33d25230aede1c65a76f1c3afe41da78b534de587134b7 SHA512 51904a6c8632b27b643a94bcfb83c1074f029c8ca9ed86fe1e41da76220a5075b836b5d45652c58bdefdeffe865cdb5b214d912b5de4e27702d6247ce4ebcd24 DIST knot-3.5.8-manpages.tar.xz 41224 BLAKE2B b5e09b5d3af811a9e83a5f6e07c18bdd515fbbc3195ec72057f9b0586bf3be620f76c796d549413734e866d3e3738fb8607e74880eac05475363dd0618230a71 SHA512 9e69b6e29a71085a29c8666e82149a3d3bc4196d49294c096708109870f4ab3a980d1b3b70e12b05058fe8cbb42ebb74cb506f31bba827a788241bba459a666e DIST knot-3.5.8.tar.xz 1736408 BLAKE2B e7793b469c7782a3aa14c8e02fb038d8d1d943283a9cd4dde7bee6a7296d5c4149e0752f8741317dabadbe4dfe930c715a8c82b6c27bb483b8f358c17acac3c4 SHA512 de7212d4b2d6b5868e848db55a3b276ff650bfe46b1fb8df5a6702d6e1408d85ad25b692a95eb26bd5165e8ebf2b2b9bed6fac46d8f6b870698bb32a97c179f3 DIST knot-3.5.8.tar.xz.asc 833 BLAKE2B d1bf720b9b20478884ec6336722ac09c0e40a401a8b5c1adb58ae0c43adca5ecd8b941f85b53e2b6b4808bfc768315291f33d3a542ca44b75e7e9159269574b6 SHA512 9f85e7db04d310fed38811b7ee86da80bff4571ea974bc39e81d737a1fd4a587a5ff3fdcb6cea7a04c8ab2590d814411f6fc8b3b8e352ce4a7471c24901f26b4 DIST knot-3.6.0-manpages.tar.xz 42052 BLAKE2B 11c9c090fc5a9ee4b9d695fbc5252dc315d29d8d121e6ee700c9cb6abd619c1d4ebc32ed734596ef5d3f34a74d96791e165e0758f40fa4074c854b4a9aa378b9 SHA512 f224279e8f850b7edf445a794d205c387cbc01671745bf3ca197bbd30e282dd490cd28b73b8b1e23c3add879a3ec07b0e54212764f73f1f451a39195c937e3f9 DIST knot-3.6.0.tar.xz 1852000 BLAKE2B 454d12deb35c91fd3c3e71cfd4021bd0d36fb39446e3cef41a47df3bbc0362950de1a21eb742d936dbce13a822891b313da335b8bb21026b88d4d985d8c763c0 SHA512 83369a94ce7f35f41b08d599844e4c9586c29ba8efcf841e90560734b81df72054ddd62df609b84149944a36672bb71219c66e6e0c8ee7e41d5c421fc256e64a DIST knot-3.6.0.tar.xz.asc 833 BLAKE2B e8e9ca59ba1586854ab8de727dcd29fca23e7bb9a7572015f67e8e0173d2824419c91e6bc44d25ff4771bad59f15d03411dd4f8732174eff9e7d25e582d0b415 SHA512 31985e81306275b1cf1c382b0a97317a7220a1166ae680f1ed6dc02bb16dfe0789ccec3f98947d89d5966494132686169eb70c9222d0beada2f6c02072fa2c99 +DIST knot-3.6.2.tar.xz 1857304 BLAKE2B 185c8a9c3935dfd7a214b40b38fd54136c715ac8defa4bf3080ed7f66d979fbdd8a62f34470456215608ef46fcde494d87772309bec5cab21f1c48efff9efe50 SHA512 a3178530cb8f4e1739a70f4267a543ec285e83139f551a5aac664db38a560309e6e7b3cb6f65d74b7b23b69a4636c918f7eee71f107500b0bc5f6453ac340f0a +DIST knot-3.6.2.tar.xz.asc 833 BLAKE2B 1e1c31f0525c49ab99840a3e74995d356759853005e69c10b2da8951c690d58cd2f25680cda4495d5250874013bc0d42a487911f1981f46ca3e59dca54939155 SHA512 100a8cf3d72b215e5c67a17badf7af2f6b2dffaebb020ed92fe1e25d3a411172729668d826ec3d29f207c0e5106a112da107b2c034f84e2d6fb54177bb29dbf6 diff --git a/net-dns/knot/files/knot-3.init b/net-dns/knot/files/knot-3.init index d2be37b17893..c89efa78d60b 100644 --- a/net-dns/knot/files/knot-3.init +++ b/net-dns/knot/files/knot-3.init @@ -1,10 +1,9 @@ #!/sbin/openrc-run -# Copyright 2024 Gentoo Authors +# Copyright 2024-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 : ${KNOT_USER:=knot} : ${KNOT_GROUP:=knot} -: ${KNOT_PIDFILE:=/run/knot.pid} : ${KNOT_CONFIG:=/etc/knot/knot.conf} name="knot" @@ -13,7 +12,7 @@ command="/usr/sbin/knotd" command_args="-c ${KNOT_CONFIG}" command_user="${KNOT_USER}:${KNOT_GROUP}" command_background=true -pidfile="${KNOT_PIDFILE}" +pidfile="/run/${RC_SVCNAME}.pid" extra_commands="checkconfig" extra_started_commands="reload" diff --git a/net-dns/knot/files/knot.confd b/net-dns/knot/files/knot.confd index f4821e9d725d..8a375a9db0f1 100644 --- a/net-dns/knot/files/knot.confd +++ b/net-dns/knot/files/knot.confd @@ -1,7 +1,6 @@ -# Copyright 2024 Gentoo Authors +# Copyright 2024-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 #KNOT_USER=knot #KNOT_GROUP=knot -#KNOT_PIDFILE=/run/knot.pid #KNOT_CONFIG=/etc/knot/knot.conf diff --git a/net-dns/knot/knot-3.5.6.ebuild b/net-dns/knot/knot-3.5.10.ebuild index 569a3e11864a..44706041d444 100644 --- a/net-dns/knot/knot-3.5.6.ebuild +++ b/net-dns/knot/knot-3.5.10.ebuild @@ -4,7 +4,7 @@ EAPI=8 PYTHON_COMPAT=( python3_{13..14} ) -inherit autotools eapi9-ver python-r1 systemd tmpfiles verify-sig +inherit eapi9-ver python-r1 systemd tmpfiles verify-sig # subslot: libknot major.libdnssec major.libzscanner major KNOT_SUBSLOT="16.10.5" @@ -13,7 +13,6 @@ DESCRIPTION="High-performance authoritative-only DNS server" HOMEPAGE="https://www.knot-dns.cz/ https://gitlab.nic.cz/knot/knot-dns" SRC_URI=" https://knot-dns.nic.cz/release/${P}.tar.xz - !doc? ( https://raw.githubusercontent.com/PPN-SD/gentoo-manpages/refs/tags/${P}/${P}-manpages.tar.xz ) verify-sig? ( https://knot-dns.nic.cz/release/${P}.tar.xz.asc ) " LICENSE="GPL-2+" @@ -57,8 +56,8 @@ COMMON_DEPEND=" ) " RDEPEND=" - dev-db/lmdb:= - net-libs/gnutls:=[pkcs11?] + >=dev-db/lmdb-0.9.15:= + >=net-libs/gnutls-3.7.3:=[pkcs11?] daemon? ( ${COMMON_DEPEND} acct-group/knot @@ -68,14 +67,14 @@ RDEPEND=" dbus? ( sys-apps/dbus ) geoip? ( dev-libs/libmaxminddb:= ) redis? ( >=dev-libs/hiredis-1.1.0:= ) - systemd? ( sys-apps/systemd:= ) + systemd? ( >=sys-apps/systemd-221:= ) ) prometheus? ( dev-python/prometheus-client[${PYTHON_USEDEP}] dev-python/psutil[${PYTHON_USEDEP}] ) python? ( ${PYTHON_DEPS} ) - quic? ( net-libs/ngtcp2[gnutls] ) + quic? ( >=net-libs/ngtcp2-0.17.0[gnutls] ) selinux? ( sec-policy/selinux-knot ) utils? ( ${COMMON_DEPEND} @@ -132,23 +131,18 @@ src_unpack() { default } -src_prepare() { - default - # avoid the old ltmain.sh modified by upstream which causes a linking issue - # reproduced with test and musl - eautoreconf -} - src_configure() { - local u + use doc || export ac_cv_path_SPHINXBUILD= + local my_conf=( --with-storage="${EPREFIX}/var/lib/${PN}" - --with-rundir="${EPREFIX}/var/run/${PN}" + --with-rundir="${EPREFIX}/run/${PN}" $(use_enable caps cap_ng) $(use_enable daemon) # enable-dnstap defines support for kdig only $(use_enable dnstap dnstap $(usex utils)) - $(use_enable doc documentation) + # always enable for manpages + --enable-documentation $(use_with doh libnghttp2) $(use_enable fastparser) $(use_enable geoip maxminddb) @@ -162,6 +156,7 @@ src_configure() { ) # modules are only used by daemon # module-dnstap defines support for knotd only + local u if use daemon; then for u in "${KNOT_MODULES[@]}"; do my_conf+=( --with-module-${u} ) @@ -193,41 +188,7 @@ src_compile() { } src_install() { - if use doc; then - local HTML_DOCS=( doc/_build/html/{*.html,*.js,_sources,_static} ) - else - # install the manpages only for the activated options, order from doc/Makefile.am - pushd "${WORKDIR}"/man/ >/dev/null || die - local manp=() - if use daemon; then - manp+=( - knot.conf.5 - knotc.8 - knotd.8 - ) - fi - if use utils; then - use daemon && manp+=( - kcatalogprint.8 - keymgr.8 - kjournalprint.8 - kzonecheck.1 - kzonesign.1 - ) - manp+=( - kdig.1 - khost.1 - knsupdate.1 - knsec3hash.1 - ) - use xdp && manp+=( - kxdpgun.8 - ) - fi - [[ "${#manp[@]}" -ne 0 ]] && doman "${manp[@]}" - popd >/dev/null || die - fi - + use doc && local HTML_DOCS=( doc/_build/html/{*.html,*.js,_sources,_static} ) if use python; then # install only .py files, not build system leftovers (e.g. .in) python_moduleinto libknot @@ -247,16 +208,26 @@ src_install() { default if use daemon; then - rm -r "${ED}"/var/run/ || die + rm -r "${ED}"/run/ || die - keepdir /var/lib/knot + keepdir /var/lib/${PN} + + fowners -R :knot /etc/${PN} + fperms 750 /etc/${PN} + fperms 640 /etc/${PN}/${PN}.sample.conf + fperms 640 /etc/${PN}/example.com.zone newinitd "${FILESDIR}"/knot-3.init knot newconfd "${FILESDIR}"/knot.confd knot + systemd_dounit distro/common/knot.service + newtmpfiles "${FILESDIR}"/${PN}.tmpfile ${PN}.conf - use systemd && systemd_newunit distro/common/knot.service knot.service + if use dbus; then + insinto /usr/share/dbus-1/system.d + doins distro/common/cz.nic.knotd.conf + fi fi find "${D}" -name '*.la' -delete || die diff --git a/net-dns/knot/knot-3.5.5-r2.ebuild b/net-dns/knot/knot-3.6.2.ebuild index 4340d70ec3a9..4545f48cfc00 100644 --- a/net-dns/knot/knot-3.5.5-r2.ebuild +++ b/net-dns/knot/knot-3.6.2.ebuild @@ -4,21 +4,21 @@ EAPI=8 PYTHON_COMPAT=( python3_{13..14} ) -inherit autotools eapi9-ver python-r1 systemd tmpfiles verify-sig +inherit eapi9-ver python-r1 systemd tmpfiles verify-sig -# subslot: libknot major.libdnssec major.libzscanner major -KNOT_SUBSLOT="16.10.5" +# subslot: libknot major.libzscanner major +KNOT_SUBSLOT="17.5" DESCRIPTION="High-performance authoritative-only DNS server" HOMEPAGE="https://www.knot-dns.cz/ https://gitlab.nic.cz/knot/knot-dns" SRC_URI=" https://knot-dns.nic.cz/release/${P}.tar.xz - !doc? ( https://raw.githubusercontent.com/PPN-SD/gentoo-manpages/refs/tags/${P}/${P}-manpages.tar.xz ) verify-sig? ( https://knot-dns.nic.cz/release/${P}.tar.xz.asc ) " -LICENSE="GPL-2+" +# OPENLDAP: include code from lmdb-0.9 for automatic migration to lmdb-1 +LICENSE="GPL-2+ OPENLDAP" SLOT="0/${KNOT_SUBSLOT}" -KEYWORDS="amd64 ~arm64 ~riscv x86" +KEYWORDS="~amd64 ~arm64 ~riscv ~x86" # Modules without dep and enabled by default. Built unconditionally. KNOT_MODULES=( @@ -27,6 +27,7 @@ KNOT_MODULES=( "cookies" "dnserr" "dnsproxy" + "ecs" "noudp" "onlinesign" "probe" @@ -57,8 +58,8 @@ COMMON_DEPEND=" ) " RDEPEND=" - dev-db/lmdb:= - net-libs/gnutls:=[pkcs11?] + >=dev-db/lmdb-0.9.15:= + >=net-libs/gnutls-3.7.3:=[pkcs11?] daemon? ( ${COMMON_DEPEND} acct-group/knot @@ -68,14 +69,14 @@ RDEPEND=" dbus? ( sys-apps/dbus ) geoip? ( dev-libs/libmaxminddb:= ) redis? ( >=dev-libs/hiredis-1.1.0:= ) - systemd? ( sys-apps/systemd:= ) + systemd? ( >=sys-apps/systemd-221:= ) ) prometheus? ( dev-python/prometheus-client[${PYTHON_USEDEP}] dev-python/psutil[${PYTHON_USEDEP}] ) python? ( ${PYTHON_DEPS} ) - quic? ( net-libs/ngtcp2[gnutls] ) + quic? ( >=net-libs/ngtcp2-0.17.0[gnutls] ) selinux? ( sec-policy/selinux-knot ) utils? ( ${COMMON_DEPEND} @@ -103,6 +104,7 @@ BDEPEND=" ) python? ( ${PYTHON_DEPS} ) test? ( + elibc_musl? ( sys-libs/timezone-data ) pkcs11? ( dev-libs/softhsm ) ) verify-sig? ( sec-keys/openpgp-keys-knot ) @@ -132,23 +134,18 @@ src_unpack() { default } -src_prepare() { - default - # avoid the old ltmain.sh modified by upstream which causes a linking issue - # reproduced with test and musl - eautoreconf -} - src_configure() { - local u + use doc || export ac_cv_path_SPHINXBUILD= + local my_conf=( --with-storage="${EPREFIX}/var/lib/${PN}" - --with-rundir="${EPREFIX}/var/run/${PN}" + --with-rundir="${EPREFIX}/run/${PN}" $(use_enable caps cap_ng) $(use_enable daemon) # enable-dnstap defines support for kdig only $(use_enable dnstap dnstap $(usex utils)) - $(use_enable doc documentation) + # always enable for manpages + --enable-documentation $(use_with doh libnghttp2) $(use_enable fastparser) $(use_enable geoip maxminddb) @@ -162,6 +159,7 @@ src_configure() { ) # modules are only used by daemon # module-dnstap defines support for knotd only + local u if use daemon; then for u in "${KNOT_MODULES[@]}"; do my_conf+=( --with-module-${u} ) @@ -193,41 +191,7 @@ src_compile() { } src_install() { - if use doc; then - local HTML_DOCS=( doc/_build/html/{*.html,*.js,_sources,_static} ) - else - # install the manpages only for the activated options, order from doc/Makefile.am - pushd "${WORKDIR}"/man/ >/dev/null || die - local manp=() - if use daemon; then - manp+=( - knot.conf.5 - knotc.8 - knotd.8 - ) - fi - if use utils; then - use daemon && manp+=( - kcatalogprint.8 - keymgr.8 - kjournalprint.8 - kzonecheck.1 - kzonesign.1 - ) - manp+=( - kdig.1 - khost.1 - knsupdate.1 - knsec3hash.1 - ) - use xdp && manp+=( - kxdpgun.8 - ) - fi - [[ "${#manp[@]}" -ne 0 ]] && doman "${manp[@]}" - popd >/dev/null || die - fi - + use doc && local HTML_DOCS=( doc/_build/html/{*.html,*.js,_sources,_static} ) if use python; then # install only .py files, not build system leftovers (e.g. .in) python_moduleinto libknot @@ -247,16 +211,26 @@ src_install() { default if use daemon; then - rm -r "${ED}"/var/run/ || die + rm -r "${ED}"/run/ || die - keepdir /var/lib/knot + keepdir /var/lib/${PN} + + fowners -R :knot /etc/${PN} + fperms 750 /etc/${PN} + fperms 640 /etc/${PN}/${PN}.sample.conf + fperms 640 /etc/${PN}/example.com.zone newinitd "${FILESDIR}"/knot-3.init knot newconfd "${FILESDIR}"/knot.confd knot + systemd_dounit distro/common/knot.service + newtmpfiles "${FILESDIR}"/${PN}.tmpfile ${PN}.conf - use systemd && systemd_newunit distro/common/knot.service knot.service + if use dbus; then + insinto /usr/share/dbus-1/system.d + doins distro/common/cz.nic.knotd.conf + fi fi find "${D}" -name '*.la' -delete || die @@ -270,8 +244,8 @@ pkg_postinst() { ewarn "must be loaded by an instance of Redis." fi - if ver_replacing -lt 3.5; then + if ver_replacing -lt 3.6; then elog "See documentation for migration:" - elog "https://www.knot-dns.cz/docs/3.5/html/migration.html#upgrade-3-4-x-to-3-5-x" + elog "https://www.knot-dns.cz/docs/3.6/html/migration.html#upgrade-3-5-x-to-3-6-x" fi } diff --git a/net-dns/pdns-recursor/Manifest b/net-dns/pdns-recursor/Manifest index a618d5f98967..275ebc4d4bce 100644 --- a/net-dns/pdns-recursor/Manifest +++ b/net-dns/pdns-recursor/Manifest @@ -40,6 +40,7 @@ DIST mio-1.1.1.crate 105630 BLAKE2B db279c3e46108375f5391a5ebc3ae07e8ccbcc9f6a71 DIST once_cell-1.21.3.crate 34534 BLAKE2B 3578aaef305cad2fdffdc40c392775a3540bfab3f3aeafd22466d9507bf8346b9fcc200929d48525b051070c0aaa423ecbcaa12868b34dca007991effb224166 SHA512 32a87506c6f4598f3ca2c88556014ef2093d5db9a08602335e847caa537a866492fa74c894e7e1da2e4289a1d3dbffcb90a9e37a4a1453203832f434b8206990 DIST pdns-recursor-5.4.6.tar.xz 1397776 BLAKE2B 5348817622a35eacf1cc3697c5e0a870a8fb9ba5b1bd6d20dce8579be7ae87c3ef627ba6f457bab2bc224f5288c5d75dafebd6d0ccfcd3f8001e56d80ba58f2a SHA512 e998430bf1637d33552b2baea6430aab87cbecae3e4a1634e02fe19f0997a2213b0266b748e41c6c28fbacb6d80d1cc8141c807cd0dec0d70aedbec436da78f0 DIST pdns-recursor-5.4.7.tar.xz 1398536 BLAKE2B 5133789e245c823dd54b6940d65f6a8e12d6731754132866e18f7b87f68b9046372973add31bc68e845e90012edfe9cbb2d0ea82c7da7a31204b0ca8d4a7f44e SHA512 8d0e026400ac0f14832a5a14e13a06a5f94c44c4771266d1bda59b8d69441e121aa43ec3d3fe70f85bbc06ee7c0bb45ab48035f317739064934bc711db29a730 +DIST pdns-recursor-5.4.8.tar.xz 1399720 BLAKE2B 8af7903bbf66f92a367af411f7f726bc531d15ca32cc2098ff43573f90228ee5699693b3d52815de46a026cadefa853b82f7b3f5d46263a8f398f228f9ff773b SHA512 92b79015316d48aff2bb55a664ad99013e0f31fa02ec686a8b7c8b1f7cfd59fc18f774962bd9ce9f5fe925cdcaa5aae7187dc8e2f525c279c8226528956ab7db DIST percent-encoding-2.3.2.crate 11583 BLAKE2B 58925243b23a6a1decc3d92c4ce17d06af5206844ad283b49e69e523ac0db2c36c3f4407af4104ab92c029c2581c228bdf416a054a2531191a9e912448ba4408 SHA512 495b4aac88a7a46420811671d6bd82817e4eb2fe51d5068d11c5b50d0d927895d54c855cb07d4f4ad5a2d42b22f9419ad0183c14781dbfa240804fd49761035a DIST pin-project-lite-0.2.16.crate 30504 BLAKE2B efd0b426fcc6ea8852bce499fac61f9755a11c6a2999cbec514f093ba7b3f94b1f2d437ee9abb243e31f3838ac1c74491a212851d7798eb249e209b35e015332 SHA512 971adfe54cfed304647fd944c1c915e78b37eaf0de3a582fb984a5e91f1b7d4db2cf0f53a9a64b64427062d4b41c0a36baddef782411a76ae3be0f8ca45f0718 DIST pin-utils-0.1.0.crate 7580 BLAKE2B 457e1287202f16d1873b24bf4f1de1828300128c4ba3131758e64f9784d36d47365f22d85493c3a85d854f0d8dbb4c6cef3a0f5b064014dc03943e58b7ba9178 SHA512 828422b8440cc82ac6b0743e0112fa7540d437aed457564999092b1462cd7672cd6b1f0201b67075431aeedd3d9c5127468a3dd028744109944f7f023c82fd70 diff --git a/net-dns/pdns-recursor/files/pdns-recursor-r3 b/net-dns/pdns-recursor/files/pdns-recursor-r3 index 02f8408e275b..a744a3078d62 100644 --- a/net-dns/pdns-recursor/files/pdns-recursor-r3 +++ b/net-dns/pdns-recursor/files/pdns-recursor-r3 @@ -24,6 +24,10 @@ depend() { need net } +start_pre() { + checkpath -q -d -m 0700 -o pdns:pdns /run/pdns-recursor +} + ping() { ebegin "Pinging ${name}" ${control_command} ${control_command_args} ping >/dev/null 2>&1 diff --git a/net-dns/pdns-recursor/pdns-recursor-5.4.8.ebuild b/net-dns/pdns-recursor/pdns-recursor-5.4.8.ebuild new file mode 100644 index 000000000000..b50658cad6df --- /dev/null +++ b/net-dns/pdns-recursor/pdns-recursor-5.4.8.ebuild @@ -0,0 +1,197 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI="8" + +LUA_COMPAT=( lua5-{1,3,4} luajit ) + +RUST_MIN_VER="1.85.0" +CRATES=" + anstyle@1.0.13 + atomic-waker@1.1.2 + base64@0.22.1 + bytes@1.11.1 + cc@1.2.49 + cfg-if@1.0.4 + clap@4.5.53 + clap_builder@4.5.53 + clap_lex@0.7.6 + codespan-reporting@0.13.1 + cxx@1.0.196 + cxx-build@1.0.196 + cxxbridge-cmd@1.0.196 + cxxbridge-flags@1.0.196 + cxxbridge-macro@1.0.196 + equivalent@1.0.2 + find-msvc-tools@0.1.5 + foldhash@0.2.0 + form_urlencoded@1.2.2 + futures-channel@0.3.31 + futures-core@0.3.31 + futures-task@0.3.31 + futures-util@0.3.31 + getrandom@0.2.16 + hashbrown@0.16.1 + http@1.4.0 + http-body@1.0.1 + http-body-util@0.1.3 + httparse@1.10.1 + httpdate@1.0.3 + hyper@1.8.1 + hyper-rustls@0.27.7 + hyper-util@0.1.19 + indexmap@2.12.1 + ipnet@2.11.0 + itoa@1.0.15 + libc@0.2.178 + link-cplusplus@1.0.12 + mio@1.1.1 + once_cell@1.21.3 + percent-encoding@2.3.2 + pin-project-lite@0.2.16 + pin-utils@0.1.0 + proc-macro2@1.0.103 + quote@1.0.42 + ring@0.17.14 + rustls@0.23.35 + rustls-pki-types@1.13.1 + rustls-webpki@0.103.13 + ryu@1.0.20 + scratch@1.0.9 + serde@1.0.228 + serde_core@1.0.228 + serde_derive@1.0.228 + serde_yaml@0.9.34+deprecated + shlex@1.3.0 + smallvec@1.15.1 + socket2@0.6.1 + strsim@0.11.1 + subtle@2.6.1 + syn@2.0.111 + termcolor@1.4.1 + tokio@1.48.0 + tokio-rustls@0.26.4 + tower-service@0.3.3 + tracing@0.1.43 + tracing-core@0.1.35 + try-lock@0.2.5 + unicode-ident@1.0.22 + unicode-width@0.2.2 + unsafe-libyaml@0.2.11 + untrusted@0.9.0 + want@0.3.1 + wasi@0.11.1+wasi-snapshot-preview1 + winapi-util@0.1.11 + windows-link@0.2.1 + windows-sys@0.52.0 + windows-sys@0.60.2 + windows-sys@0.61.2 + windows-targets@0.52.6 + windows-targets@0.53.5 + windows_aarch64_gnullvm@0.52.6 + windows_aarch64_gnullvm@0.53.1 + windows_aarch64_msvc@0.52.6 + windows_aarch64_msvc@0.53.1 + windows_i686_gnu@0.52.6 + windows_i686_gnu@0.53.1 + windows_i686_gnullvm@0.52.6 + windows_i686_gnullvm@0.53.1 + windows_i686_msvc@0.52.6 + windows_i686_msvc@0.53.1 + windows_x86_64_gnu@0.52.6 + windows_x86_64_gnu@0.53.1 + windows_x86_64_gnullvm@0.52.6 + windows_x86_64_gnullvm@0.53.1 + windows_x86_64_msvc@0.52.6 + windows_x86_64_msvc@0.53.1 + zeroize@1.8.2 +" + +inherit cargo flag-o-matic lua-single + +DESCRIPTION="The PowerDNS Recursor" +HOMEPAGE="https://www.powerdns.com/" +SRC_URI="https://downloads.powerdns.com/releases/${P/_/-}.tar.xz ${CARGO_CRATE_URIS}" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~amd64 ~arm ~x86" +IUSE="debug dns-over-tls dnstap snmp sodium systemd test valgrind" +REQUIRED_USE="${LUA_REQUIRED_USE}" +RESTRICT="!test? ( test )" + +DEPEND="${LUA_DEPS} + dnstap? ( dev-libs/fstrm ) + systemd? ( sys-apps/systemd:0= ) + snmp? ( net-analyzer/net-snmp ) + sodium? ( dev-libs/libsodium:= ) + elibc_glibc? ( + arm? ( >=sys-libs/glibc-2.34 ) + x86? ( >=sys-libs/glibc-2.34 ) + ) + dev-libs/openssl:= + >=dev-libs/boost-1.54:=[context]" +RDEPEND="${DEPEND} + !<net-dns/pdns-2.9.20-r1 + acct-user/pdns + acct-group/pdns" +DEPEND="${DEPEND} + valgrind? ( dev-debug/valgrind )" +BDEPEND="virtual/pkgconfig" + +S="${WORKDIR}"/${P/_/-} + +pkg_setup() { + lua-single_pkg_setup + filter-flags -ftree-vectorize + filter-lto + rust_pkg_setup +} + +src_configure() { + econf \ + --enable-experimental-64bit-time_t-support-on-glibc \ + --sysconfdir=/etc/powerdns \ + --with-nod-cache-dir=/var/lib/powerdns \ + --with-service-user=pdns \ + --with-service-group=pdns \ + --with-lua="${ELUA}" \ + $(use_enable debug verbose-logging) \ + $(use_enable systemd) \ + $(use_enable dns-over-tls) \ + $(use_enable dnstap dnstap) \ + $(use_enable test unit-tests) \ + $(use_enable valgrind) \ + $(use_with sodium libsodium) \ + $(use_with snmp net-snmp) +} + +src_compile() { + default +} + +src_install() { + default + + # rename .yml to .conf to facilitate easy upgrade and switch to .yml + mv "${D}"/etc/powerdns/recursor.{yml-dist,conf} || die + + sed -i \ + -e 's/^# set\([ug]\)id: '\'\''$/ set\1id: '\''pdns'\''/' \ + -e 's/^# chroot: '\'\''$/ chroot: '\''\/var\/lib\/powerdns'\''/' \ + "${D}"/etc/powerdns/recursor.conf || die + + newinitd "${FILESDIR}"/pdns-recursor-r3 pdns-recursor + + keepdir /var/lib/powerdns +} + +src_test() { + default +} + +pkg_postinst() { + einfo "Starting with 5.1.x we default to using the new yaml configuration file format." + einfo "The old configuration file format is still supported for now, but please update" + einfo "your recursor.conf to yaml using 'rec_control show-yaml'." +} diff --git a/net-dns/pdns/Manifest b/net-dns/pdns/Manifest index 7db3b1c05dae..20f09e9e2aee 100644 --- a/net-dns/pdns/Manifest +++ b/net-dns/pdns/Manifest @@ -1 +1,2 @@ DIST pdns-5.1.4.tar.bz2 1536977 BLAKE2B 3c0a4487f1e00925459b71d1ca6a06b21ed2489f209ed599b7e6f6068fb41e434b89474f46644ef7eca09aaf541164708b4be49ec9821ceb1728955101094e2a SHA512 63289f93af4c50176565a6cbcf387ee40645b9a16166eb9cb9fad2d743aba989839e9a8476388c027fa752762d02215095ec2803eca846fbcdbd0fcd40a2a6d9 +DIST pdns-5.1.5.tar.bz2 1556885 BLAKE2B 3bbcca509d440b20c257d5f73a813ab9571645ca8ce83d755e1b0edba8887c77f9c70f30a61943e888367bf8d5e24ef7fe18065e29392118903688bfba8a6ef0 SHA512 8b45a69dbbb40ecd2fc328d2aaabd1cb5a36c3ee3525b42ef499c0e9343c442279139f0eafd5d7d9853169ad70db8662af60e84e1b3df318c9e74306624337fd diff --git a/net-dns/pdns/pdns-5.1.5.ebuild b/net-dns/pdns/pdns-5.1.5.ebuild new file mode 100644 index 000000000000..b92d9d7b95e1 --- /dev/null +++ b/net-dns/pdns/pdns-5.1.5.ebuild @@ -0,0 +1,145 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +LUA_COMPAT=( lua5-{1,3,4} luajit ) +PYTHON_COMPAT=( python3_{13..14} ) + +inherit lua-single python-any-r1 + +DESCRIPTION="The PowerDNS Daemon" +HOMEPAGE="https://www.powerdns.com/" +SRC_URI="https://downloads.powerdns.com/releases/${P/_/-}.tar.bz2" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~amd64 ~x86" + +IUSE="debug doc geoip ldap lmdb lua-records maxminddb mysql odbc postgres remote sodium sqlite systemd tools tinydns test" +RESTRICT="!test? ( test )" + +REQUIRED_USE="${LUA_REQUIRED_USE}" + +DEPEND="${LUA_DEPS} + dev-libs/openssl:= + dev-libs/boost:= + lmdb? ( >=dev-db/lmdb-0.9.29 ) + lua-records? ( >=net-misc/curl-7.21.3 ) + mysql? ( dev-db/mysql-connector-c:= ) + postgres? ( dev-db/postgresql:= ) + ldap? ( >=net-nds/openldap-2.0.27-r4:= app-crypt/mit-krb5 ) + odbc? ( dev-db/unixODBC ) + sqlite? ( dev-db/sqlite:3 ) + geoip? ( >=dev-cpp/yaml-cpp-0.5.1:= dev-libs/geoip ) + maxminddb? ( >=dev-cpp/yaml-cpp-0.5.1:= dev-libs/libmaxminddb:= ) + sodium? ( dev-libs/libsodium:= ) + tinydns? ( >=dev-db/tinycdb-0.77 ) + elibc_glibc? ( x86? ( >=sys-libs/glibc-2.34 ) )" +RDEPEND="${DEPEND} + acct-user/pdns + acct-group/pdns" + +BDEPEND="${PYTHON_DEPS} + virtual/pkgconfig + doc? ( app-text/doxygen[dot] )" + +S="${WORKDIR}"/${P/_/-} + +pkg_setup() { + lua-single_pkg_setup + python-any-r1_pkg_setup +} + +geoipbackend_disable() { + local lib="${1}" + sed -i \ + -e "/^${lib}_\(CFLAGS\|LIBS\) = / s/ = .*/ = /" \ + modules/geoipbackend/Makefile || die + echo "#undef HAVE_${lib}" >>config.h || die +} + +src_configure() { + local cnf_dynmodules="bind lua2 pipe" # the default backends, always enabled + + use geoip || use maxminddb && cnf_dynmodules+=" geoip" + use ldap && cnf_dynmodules+=" ldap" + use lmdb && cnf_dynmodules+=" lmdb" + use mysql && cnf_dynmodules+=" gmysql" + use odbc && cnf_dynmodules+=" godbc" + use postgres && cnf_dynmodules+=" gpgsql" + use remote && cnf_dynmodules+=" remote" + use sqlite && cnf_dynmodules+=" gsqlite3" + use tinydns && cnf_dynmodules+=" tinydns" + + econf \ + --enable-experimental-64bit-time_t-support-on-glibc \ + --disable-static \ + --sysconfdir=/etc/powerdns \ + --libdir=/usr/$(get_libdir)/powerdns \ + --with-service-user=pdns \ + --with-service-group=pdns \ + --with-modules= \ + --with-dynmodules="${cnf_dynmodules}" \ + --with-mysql-lib=/usr/$(get_libdir) \ + --with-lua="${ELUA}" \ + $(use_enable debug verbose-logging) \ + $(use_enable lua-records) \ + $(use_enable test unit-tests) \ + $(use_enable tools) \ + $(use_enable systemd) \ + $(use_with sodium libsodium) + + # Force automagic detected support to be disabled + use geoip || geoipbackend_disable GEOIP + use maxminddb || geoipbackend_disable MMDB +} + +src_compile() { + default + use doc && emake -C codedocs codedocs +} + +src_install() { + default + + mv "${D}"/etc/powerdns/pdns.conf{-dist,} + + fperms 0700 /etc/powerdns + fperms 0600 /etc/powerdns/pdns.conf + + # set defaults: setuid=pdns, setgid=pdns + sed -i \ + -e 's/^# set\([ug]\)id=$/set\1id=pdns/g' \ + "${D}"/etc/powerdns/pdns.conf + + newinitd "${FILESDIR}"/pdns-r1 pdns + + keepdir /var/empty + + if use doc; then + docinto html + dodoc -r codedocs/html/. + fi + + # Install development headers + insinto /usr/include/pdns + doins pdns/*.hh + insinto /usr/include/pdns/backends/gsql + doins pdns/backends/gsql/*.hh + + if use ldap ; then + insinto /etc/openldap/schema + doins "${FILESDIR}"/dnsdomain2.schema + fi + + find "${D}" -name '*.la' -delete || die +} + +pkg_postinst() { + elog "PowerDNS provides multiple instances support. You can create more instances" + elog "by symlinking the pdns init script to another name." + elog + elog "The name must be in the format pdns.<suffix> and PowerDNS will use the" + elog "/etc/powerdns/pdns-<suffix>.conf configuration file instead of the default." +} diff --git a/net-dns/redis-knot/Manifest b/net-dns/redis-knot/Manifest index e0d9155cbf1c..e1e73a054196 100644 --- a/net-dns/redis-knot/Manifest +++ b/net-dns/redis-knot/Manifest @@ -1,8 +1,8 @@ -DIST knot-3.5.5.tar.xz 1734192 BLAKE2B 5ee478109a26e3b163da169e7e22a03780727528f84953e399f25670d48392b6b407388c38b152bc90399f517b70676168735c1aba4028326237b92cdf904938 SHA512 983789c7fbbb191d4bd8e9f13050a5d5310a8bc69184b6d3440711fff88fa631ca6703b0800dd5581bd84850b4253dddfb36c2bdd46f6f158333b5ad934fdfdc -DIST knot-3.5.5.tar.xz.asc 833 BLAKE2B 478aa1fba542f1e5cf00ea35af2d99cf2fe1b88e566dbc056696542e74508f58790c14a88e74a09bb06247a811a58bd4534e7d36cea6825f2111b9020c21e190 SHA512 b20be4fde7d62954898e8e1e9b9d81cad5a543d7d7636fcccebaa313905491c900f7c02c0782b8287f6262736bd08a1a208cda5557776dbeed8aaab589ef1b68 -DIST knot-3.5.6.tar.xz 1734776 BLAKE2B f03a2da9390e89bd7955505d3f59ca54689e2183b1ea0f82efbf5abedd71f16902c6d09f77adceaedd29573d52135c168441dbf46721709b370269220b68f49b SHA512 bd1818552ab076a007aae2d14fdae053e7ffabb37bcfd6cd4485b866fe2d80675f3a4367a87afe4c4a32ea529a503f2b6857632eb74f7cac0598f15b093d286b -DIST knot-3.5.6.tar.xz.asc 833 BLAKE2B e50abb5e54db106e965ea01d8bed5ee183cc98922368d58144552cb2a6a55af738abd44195624397fc965fead72a687c8cd76554f60ea34013a6b2e6e94652ef SHA512 5647f932b145d6a4fd12e205fe895fe18df67a3e9c33a8bb3fc93b93762bbe46a359b86cf2482e0089eddb8fbe9fe22f306df94527804c9b3bf35631914c1101 +DIST knot-3.5.10.tar.xz 1772508 BLAKE2B 6dac5723600c11b78a58a6986f922c6235489620ae66f70339bb021fb9d3d0c66e523cd38ca2c4cf2864804a4190058b9f1c8754ebe5e66b607abcb09edef0b4 SHA512 1e3c65e8f336b54c73b3fa5ee0b600c28243ba0a8909b814658d5a87388f518e5340e10562d913d1c999ea969cefda64b01354cf792fb0661751b9c4e93a66a1 +DIST knot-3.5.10.tar.xz.asc 833 BLAKE2B 84df2f415667704bf076e8d989d9de84c9dd44df153878e5032ff9b863ac11257e3f63a8ea7924ed4c33d25230aede1c65a76f1c3afe41da78b534de587134b7 SHA512 51904a6c8632b27b643a94bcfb83c1074f029c8ca9ed86fe1e41da76220a5075b836b5d45652c58bdefdeffe865cdb5b214d912b5de4e27702d6247ce4ebcd24 DIST knot-3.5.8.tar.xz 1736408 BLAKE2B e7793b469c7782a3aa14c8e02fb038d8d1d943283a9cd4dde7bee6a7296d5c4149e0752f8741317dabadbe4dfe930c715a8c82b6c27bb483b8f358c17acac3c4 SHA512 de7212d4b2d6b5868e848db55a3b276ff650bfe46b1fb8df5a6702d6e1408d85ad25b692a95eb26bd5165e8ebf2b2b9bed6fac46d8f6b870698bb32a97c179f3 DIST knot-3.5.8.tar.xz.asc 833 BLAKE2B d1bf720b9b20478884ec6336722ac09c0e40a401a8b5c1adb58ae0c43adca5ecd8b941f85b53e2b6b4808bfc768315291f33d3a542ca44b75e7e9159269574b6 SHA512 9f85e7db04d310fed38811b7ee86da80bff4571ea974bc39e81d737a1fd4a587a5ff3fdcb6cea7a04c8ab2590d814411f6fc8b3b8e352ce4a7471c24901f26b4 DIST knot-3.6.0.tar.xz 1852000 BLAKE2B 454d12deb35c91fd3c3e71cfd4021bd0d36fb39446e3cef41a47df3bbc0362950de1a21eb742d936dbce13a822891b313da335b8bb21026b88d4d985d8c763c0 SHA512 83369a94ce7f35f41b08d599844e4c9586c29ba8efcf841e90560734b81df72054ddd62df609b84149944a36672bb71219c66e6e0c8ee7e41d5c421fc256e64a DIST knot-3.6.0.tar.xz.asc 833 BLAKE2B e8e9ca59ba1586854ab8de727dcd29fca23e7bb9a7572015f67e8e0173d2824419c91e6bc44d25ff4771bad59f15d03411dd4f8732174eff9e7d25e582d0b415 SHA512 31985e81306275b1cf1c382b0a97317a7220a1166ae680f1ed6dc02bb16dfe0789ccec3f98947d89d5966494132686169eb70c9222d0beada2f6c02072fa2c99 +DIST knot-3.6.2.tar.xz 1857304 BLAKE2B 185c8a9c3935dfd7a214b40b38fd54136c715ac8defa4bf3080ed7f66d979fbdd8a62f34470456215608ef46fcde494d87772309bec5cab21f1c48efff9efe50 SHA512 a3178530cb8f4e1739a70f4267a543ec285e83139f551a5aac664db38a560309e6e7b3cb6f65d74b7b23b69a4636c918f7eee71f107500b0bc5f6453ac340f0a +DIST knot-3.6.2.tar.xz.asc 833 BLAKE2B 1e1c31f0525c49ab99840a3e74995d356759853005e69c10b2da8951c690d58cd2f25680cda4495d5250874013bc0d42a487911f1981f46ca3e59dca54939155 SHA512 100a8cf3d72b215e5c67a17badf7af2f6b2dffaebb020ed92fe1e25d3a411172729668d826ec3d29f207c0e5106a112da107b2c034f84e2d6fb54177bb29dbf6 diff --git a/net-dns/redis-knot/redis-knot-3.5.6.ebuild b/net-dns/redis-knot/redis-knot-3.5.10.ebuild index d8199f90a76b..d8199f90a76b 100644 --- a/net-dns/redis-knot/redis-knot-3.5.6.ebuild +++ b/net-dns/redis-knot/redis-knot-3.5.10.ebuild diff --git a/net-dns/redis-knot/redis-knot-3.5.5.ebuild b/net-dns/redis-knot/redis-knot-3.6.2.ebuild index 66a569e2c362..d8199f90a76b 100644 --- a/net-dns/redis-knot/redis-knot-3.5.5.ebuild +++ b/net-dns/redis-knot/redis-knot-3.6.2.ebuild @@ -17,7 +17,7 @@ S="${WORKDIR}/${MY_P}" LICENSE="LGPL-2.1+" SLOT="0" -KEYWORDS="amd64 ~arm64 ~riscv x86" +KEYWORDS="~amd64 ~arm64 ~riscv ~x86" # no test, it requires a Redis instance and RLTest (not packaged) BDEPEND="verify-sig? ( sec-keys/openpgp-keys-knot )" diff --git a/net-im/fractal/Manifest b/net-im/fractal/Manifest index 9ea2443771b5..fea67a98f593 100644 --- a/net-im/fractal/Manifest +++ b/net-im/fractal/Manifest @@ -1,4 +1,2 @@ -DIST fractal-14-crates.tar.xz 54106720 BLAKE2B 62d91fa524f5ef5d312dc3fa1a87ae598d4575d23a9561940309fead4761321737b561f8f7f95036923f463e5d6bebf27337e7a34aee5b18f93dd4cb4499bb8f SHA512 ee01d6681889d64459a469f80be67394a6c98d6708aa13a50a7fb264c8b51a136e5c6045caf3aa6360c1dce241c00a87efbecef20afb9cc67d8fe56f21c62004 -DIST fractal-14.1.tar.bz2 2170717 BLAKE2B ef25f36fb289d22973a3e4d1094a5e84e74740401ec866e0340d19b632b23282e5a650d77295ab57438c790761bb3518eaa57a655b2c8f5d0edac4a710ae74c4 SHA512 6b53760f19c01ed56da4da0ae265f36fd6e6b1c72da52fe03a3c7e039493fb86aa38c1e0c06aba2b6ab9fa1a326e522a5cb61adb152be64f6d3dcaa6df9c7015 DIST fractal-14.2-crates.tar.xz 52818320 BLAKE2B 3701fbf7aa68c6025f221ba9701f02699119e918f1d0a160b73d5598178e436ca3f46224286fef27a89d30a7844f4f2eb0a1102db642a858a5208db2046562ad SHA512 b3045b462b277d022f34a57fd5bc4b15e41b8833187fe42069253ca3d8101bb5f0c26935fcb4f4e39a68033af3738609b482c028b3c43c7fb242ceb54ca5c776 DIST fractal-14.2.tar.bz2 2170651 BLAKE2B d44f0108041f1a1438daec99c812eb56518e1afff0bf909e411430ddde1f52a0c1df1e23764c5be4ffc3acf0d8f577d891d1f5bce255876e32a6f56cf65a6e90 SHA512 34508c5f6044fff4521e91509096efb629204add0ad99dbc07d12c5c33c8e148fe4b40eaa5419b6048c86bca889fb2f3706f37edecf691fe8e51066e678584ce diff --git a/net-im/fractal/fractal-14.1.ebuild b/net-im/fractal/fractal-14.1.ebuild deleted file mode 100644 index d34af4bfd12c..000000000000 --- a/net-im/fractal/fractal-14.1.ebuild +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright 2023-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -CRATES=" -" -RUST_MIN_VER="1.93.0" - -inherit cargo gnome2-utils meson - -MY_P=${P/_/.} -CRATE_PV=14 - -DESCRIPTION="Matrix messaging app for GNOME written in Rust" -HOMEPAGE=" - https://wiki.gnome.org/Apps/Fractal - https://gitlab.gnome.org/World/fractal/ -" -SRC_URI=" - https://gitlab.gnome.org/World/fractal/-/archive/${PV/_/.}/${MY_P}.tar.bz2 - ${CARGO_CRATE_URIS} -" -if [[ ${PKGBUMPING} != ${PVR} ]]; then - SRC_URI+=" - https://github.com/gentoo-crate-dist/fractal/releases/download/${CRATE_PV}/fractal-${CRATE_PV}-crates.tar.xz - " -fi -S=${WORKDIR}/${MY_P} - -LICENSE="GPL-3+" -# Dependent crate licenses -LICENSE+=" - Apache-2.0 Apache-2.0-with-LLVM-exceptions BSD-2 BSD Boost-1.0 - CDLA-Permissive-2.0 GPL-3+ ISC MIT MPL-2.0 MPL-2.0 Unicode-3.0 ZLIB -" -SLOT="0" -KEYWORDS="amd64 arm64" - -DEPEND=" - >=dev-libs/glib-2.86 - >=gui-libs/gtk-4.20.2:4 - >=gui-libs/libadwaita-1.8.0:1 - - >=media-libs/glycin-2.0.0:2[gtk] - >=media-libs/gstreamer-1.20:1.0 - >=media-libs/gst-plugins-bad-1.20:1.0 - >=media-libs/gst-plugins-base-1.20:1.0 - - >=gui-libs/gtksourceview-5.0.0:5 - >=media-libs/libwebp-1.0.0:= - >=media-libs/libshumate-1.2.0:1.0 - >=dev-db/sqlite-3.24.0:3 - >=sys-apps/xdg-desktop-portal-1.14.1 -" -RDEPEND=" - ${DEPEND} - media-libs/glycin-loaders:2 - media-plugins/gst-plugin-gtk4 - virtual/secret-service -" -# clang needed by bindgen -# glib: glib-compile-resources -BDEPEND=" - llvm-core/clang - dev-lang/grass - dev-libs/glib - dev-util/blueprint-compiler -" - -# Rust -QA_FLAGS_IGNORED="usr/bin/fractal" - -src_prepare() { - default - - # upstream overrides are just wrong - sed -i -e 's:profile:ignore:' Cargo.toml || die - - # force dev build - if use debug; then - sed -i -e "s:profile == 'Devel':true:" src/meson.build || die - fi -} - -src_configure() { - meson_src_configure - ln -s "${CARGO_HOME}" "${BUILD_DIR}/cargo-home" || die -} - -pkg_postinst() { - gnome2_schemas_update - xdg_desktop_database_update - xdg_icon_cache_update -} - -pkg_postrm() { - gnome2_schemas_update - xdg_desktop_database_update - xdg_icon_cache_update -} diff --git a/net-libs/nodejs/Manifest b/net-libs/nodejs/Manifest index f7b2612163b0..7b175682e445 100644 --- a/net-libs/nodejs/Manifest +++ b/net-libs/nodejs/Manifest @@ -1,12 +1,8 @@ DIST node-v20.18.2.tar.xz 41933624 BLAKE2B afe01b8df6f89254eca3b849a3603cba0eda809ba0ed37c0a401323f09402ef52be4b2b15e571fd1a876ec0b5168b46c72144f23fd30f4a96793c54ce37f4317 SHA512 c0b294a7dba42d1b898a552b66d6cdc6e5a55a2ef951f272e9614578c656d72bf727c9ebb39e27de671cfecc36bc8bf60330fb00285de012f256dbd69142868c DIST node-v20.20.2.tar.xz 42662736 BLAKE2B 3f7c6d7c0acf40f8df9c3326a54d76e70aa253159f187dff966bf3fa73341915bebbe4f78a387786489e1e8b016dd0644f8831247a681df93cb11f31fdfc8a0f SHA512 5f6977a8e678c334362535f6f6f87d397b4dcae4c3e215b77652094259bb2d83fb2c24fdc968693c89679039217b166090e560473b58321ef3325083bfb3a0ad DIST node-v22.13.1.tar.xz 48041964 BLAKE2B 9695cc6a9b9606a615cdcff42e9b8d5f15c2d12fd0f6c894596a7c6526b3267ab9ea4f7362f8c03039c728cb5f7a3a334cce7fda9293cba06922000542a2800c SHA512 711fbeacceec16b8808e5a41ead496347fee2304fc6ce095ad26c4413f52253c9f3a6cd78080e808c03c3dbe958d64359d2d6bb97a51e1b41e1cff903ce2aeb5 -DIST node-v22.23.2.tar.xz 51362684 BLAKE2B ddf19407ada0f94f4cc08814f633099f38044eb9709143d06509eff76b481736a26712c55fe3abc67e2ee18cfd524841b976ba40074699fe13c05d55a265d771 SHA512 92b959098fb7fcfe80776a983d2ae52479692a63c49087c97a0ee2423147a5222b1e1994e98ff650483322cf3a1f1cca8a51f3389993c0ff5fc13b6d1f4ef4ef +DIST node-v22.23.4.tar.xz 51413508 BLAKE2B b94c3fbd178696be4852f8e483e6a2d0b6f4a788432808b1e46ecbe1dcfae2b677e2ff3a4268754f8903f903c3e290bb56299af6f1bc8e1c9caa4ae23a824073 SHA512 b35ec69ff34f12ea79de6f95522e69c15d44f39ff2f58f5b4e56dc5ab853db4e9ca7ae2605554b4fdd9bdcea64cf923f3c3aef31a72a78838b9d68e0fc41965b DIST node-v24.21.0.tar.xz 57357488 BLAKE2B a907f5f23668d4a2839aebb9520417c0c4ee155881546ad8bbd8df7249decd070e4826a2d2465467c4295d8b9e9c5032df5ce337275df562cf02593419030401 SHA512 bb25a4fb9e5d9bc7ce05d2dfe48d9cb3524ce2e61f61105e0a769c56ac18f051825251a72fa5abe8ab5c596b0f2706bafec4d55d0a8a1bfef019f77e46520f03 -DIST node-v24.22.0.tar.xz 57606320 BLAKE2B dcf9469d3ea9c6afc56a70b055d4a9a05a2323e6b7b1efd706e3fd85d67061bcaea8ffa0e0bef5a370eda0ec68d4774c522d858da0ac5f338eb1733c5218d07e SHA512 407c6fa7790fa589f3871343aec97c1413cf4fda8006a28d09f4a4c3172c86796ca2bef7efe05010a03e581a5edbf90bdbb6425ef6f8b3a5f3ad4e0190c6bdd0 DIST node-v24.22.1.tar.xz 57669336 BLAKE2B b9397cc1fc7dd349e25a4db0b05d237ff7b5f59d2d4684ac4c16a035c56e744455d11c81f0a7e9f905c1e395270650f33be77ff362c22bd0ff93b71c63257283 SHA512 f60c250adafbddfc45d867bf644150bfbc3350483e788d1b3cff19aa05df7cd88642ae89205ba16ca14b0bb76df7c860aa2b07e61e2ee4de79f2a1b42949988e -DIST node-v26.10.0.tar.xz 62055788 BLAKE2B 8018d789113d5d9594532a607f6e3daa292799a9dfb8b0c659bbb6163d0b9198ddb9e40197b30f7e1b62c073aca80ee3411d1701670384155b3330c7e6077fd0 SHA512 03b8d85bc2ec964db3e738be4682782935221b9c0fdd9a48f39a0241acc7b92bc3c6ef4f89707e451406e9f0a350b821d905b777bcbd5ed9a2d46ecb0d7328e2 -DIST node-v26.11.1.tar.xz 63107044 BLAKE2B 3c15e4cbc08c1223a2617154a429c1e478d52f40e41a2429ac4e1f05de2ce0f8236cd50c6a10207e2e556f5fce686f534d5cc525bed806d230af29fedefba485 SHA512 a98c245732fef21dc6c0bafaa032ab1331a5efaed8d7758310dd65bf9d2bd16d5305ab06e1149b7fffe0ad0f0d945b874e30e1b9a72069a0ab064f357a225294 -DIST node-v26.12.0.tar.xz 62434260 BLAKE2B 07cdf5c515fee3e6c312a4b4bcb3b7b2aca66df66a709eed4c48695e04e5413970cee04ce1f8ad43fd2053baf7d1fad4f7cbbeb0920fed2fcecc76333b978f56 SHA512 c3c41462198c6bedc1f2136a7f421a7cc757996ae49486c4c82b7ee75b2294b298c14f7e838b83da04a789f50722247ab448dd3bac8a07ac0d49047ee33c1c05 DIST node-v26.12.1.tar.xz 62570464 BLAKE2B 6f1dc8f87256c45c3194bab785fe1d6ae5727a30776f37583a14738d97ed62d9d1d2970cef394fb768349b23cebe3c21c78648180a555c81c49f487b78cda607 SHA512 f6f76e258af33f7f877c65a2141d281ea307aae017a90bb8a75a7d4df271ad9d17c7d0edfd5751a977edd6bdb03c704d727628c4c7215c878d421787a35240f2 DIST node-v26.3.0.tar.xz 59811728 BLAKE2B a69f067e63883239a38397484b3f92ff4ca8a1ae651cb4f3328e6f994f16260751f3f64ff6c914aeef837d1f6deade8649979f091a044c86c8467058bb0065d2 SHA512 464a24d2536f3ead35325cfb268355fcc66a95260519174cba847efa4037ae2529c24781595b11aae45810570f41c0d2f2d7db2ed365baccda1017a7131a7efe diff --git a/net-libs/nodejs/nodejs-22.23.2.ebuild b/net-libs/nodejs/nodejs-22.23.4.ebuild index 3a877e8b4181..66156b0a4d70 100644 --- a/net-libs/nodejs/nodejs-22.23.2.ebuild +++ b/net-libs/nodejs/nodejs-22.23.4.ebuild @@ -34,7 +34,7 @@ REQUIRED_USE="inspector? ( icu ssl ) RESTRICT="!test? ( test )" -COMMON_DEPEND=">=app-arch/brotli-1.1.0:= +COMMON_DEPEND=">=app-arch/brotli-1.2.1:= dev-db/sqlite:3 >=dev-libs/libuv-1.51.0:= >=dev-libs/simdjson-3.13.0:= diff --git a/net-libs/nodejs/nodejs-24.22.0.ebuild b/net-libs/nodejs/nodejs-24.22.1-r1.ebuild index ac3ea0349ed8..769cf59947ad 100644 --- a/net-libs/nodejs/nodejs-24.22.0.ebuild +++ b/net-libs/nodejs/nodejs-24.22.1-r1.ebuild @@ -36,21 +36,21 @@ REQUIRED_USE="inspector? ( icu ssl ) RESTRICT="!test? ( test )" -COMMON_DEPEND=">=app-arch/brotli-1.1.0:= +COMMON_DEPEND=">=app-arch/brotli-1.2.0-r1:= dev-db/sqlite:3 - >=dev-cpp/ada-3.3.0:= + >=dev-cpp/ada-4.0.0:= >=dev-cpp/simdutf-7.3.4:= >=dev-libs/libuv-1.52.1:= - >=dev-libs/simdjson-4.6.1:= - >=net-dns/c-ares-1.34.5:= - >=net-libs/nghttp2-1.69.0:= - >=net-libs/nghttp3-1.14.0:= + >=dev-libs/simdjson-4.6.7:= + >=net-dns/c-ares-1.34.8:= + >=net-libs/nghttp2-1.70.0:= + >=net-libs/nghttp3-1.18.0:= virtual/zlib:= corepack? ( !sys-apps/yarn ) system-icu? ( >=dev-libs/icu-73:= ) system-ssl? ( - >=net-libs/ngtcp2-1.14.0:= - >=dev-libs/openssl-3.5.6:0= + >=net-libs/ngtcp2-1.25.0:= + >=dev-libs/openssl-3.5.8:0= ) !system-ssl? ( >=net-libs/ngtcp2-1.14.0:=[-gnutls] ) || ( @@ -296,6 +296,7 @@ src_test() { ) rm -f "${drop_tests[@]}" || die "disabling tests failed" + local -x LC_ALL=en_US.UTF-8 out/${BUILDTYPE}/cctest || die "${EPYTHON}" tools/test.py --mode=${BUILDTYPE,,} --flaky-tests=dontcare -J message parallel sequential || die } diff --git a/net-libs/nodejs/nodejs-26.10.0-r3.ebuild b/net-libs/nodejs/nodejs-26.10.0-r3.ebuild deleted file mode 100644 index b654b6732149..000000000000 --- a/net-libs/nodejs/nodejs-26.10.0-r3.ebuild +++ /dev/null @@ -1,334 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -CONFIG_CHECK="~ADVISE_SYSCALLS" -PYTHON_COMPAT=( python3_{13..14} ) -PYTHON_REQ_USE="threads(+)" -RUST_OPTIONAL=1 -RUST_MIN_VER="1.86.0" - -inherit check-reqs flag-o-matic linux-info ninja-utils pax-utils -inherit python-any-r1 rust shell-completion toolchain-funcs xdg-utils - -DESCRIPTION="A JavaScript runtime built on Chrome's V8 JavaScript engine" -HOMEPAGE="https://nodejs.org/" -LICENSE="Apache-1.1 Apache-2.0 BlueOak-1.0.0 BSD BSD-2 MIT npm? ( Artistic-2 )" - -if [[ ${PV} == *9999 ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/nodejs/node" - SLOT="0" -else - SRC_URI="https://nodejs.org/dist/v${PV}/node-v${PV}.tar.xz" - SLOT="0/$(ver_cut 1)" - KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc64 ~riscv ~x86 ~x64-macos" - S="${WORKDIR}/node-v${PV}" -fi - -IUSE="cpu_flags_x86_sse2 debug doc +icu +inspector lto +npm pax-kernel +snapshot +ssl system-icu +system-ssl test temporal" -REQUIRED_USE="inspector? ( icu ssl ) - npm? ( ssl ) - system-icu? ( icu ) - system-ssl? ( ssl ) - temporal? ( icu !system-icu ) - x86? ( cpu_flags_x86_sse2 )" - -RESTRICT="!test? ( test )" - -COMMON_DEPEND=">=app-arch/brotli-1.1.0:= - dev-db/sqlite:3 - >=dev-cpp/ada-4.0.0:= - >=dev-cpp/simdutf-9.2.1:=[atomic-base64] - >=dev-libs/libuv-1.52.1:= - >=dev-libs/simdjson-4.6.11:= - >=net-dns/c-ares-1.34.8:= - >=net-libs/nghttp2-1.70.0:= - >=net-libs/nghttp3-1.18.0:= - virtual/zlib:= - system-icu? ( >=dev-libs/icu-78:= ) - system-ssl? ( - >=net-libs/ngtcp2-1.25.0:= - >=dev-libs/openssl-3.5.8:0= - ) - !system-ssl? ( >=net-libs/ngtcp2-1.25.0:=[-gnutls] ) - || ( - sys-devel/gcc:* - llvm-runtimes/libatomic-stub - )" -BDEPEND="${PYTHON_DEPS} - app-alternatives/ninja - pax-kernel? ( sys-apps/elfix ) - sys-apps/coreutils - temporal? ( ${RUST_DEPEND} ) - test? ( net-misc/curl ) - virtual/pkgconfig" -DEPEND="${COMMON_DEPEND}" -RDEPEND="${COMMON_DEPEND}" - -# These are measured on a loong machine with -ggdb on, and only checked -# if debugging flags are present in CFLAGS. -# -# The final link consumed a little more than 7GiB alone, so 8GiB is the lower -# limit for memory usage. Disk usage was 19.1GiB for the build directory and -# 1.2GiB for the installed image, so we leave some room for architectures with -# fatter binaries and set the disk requirement to 22GiB. -CHECKREQS_MEMORY="8G" -CHECKREQS_DISK_BUILD="22G" - -PATCHES=( - "${FILESDIR}"/${PN}-26.6.0-gcc17.patch - "${FILESDIR}"/${PN}-26.6.0-format-cstdlib.patch - "${FILESDIR}"/${PN}-26.6.0-v8-climits.patch - "${FILESDIR}"/${PN}-26.8.2-add-missing-funcational-inc.patch - "${FILESDIR}"/${PN}-26.10.0-x86-SSE2-fix.patch - "${FILESDIR}"/${PN}-26.10.0-simdutf-use-system-header.patch -) - -pkg_pretend() { - if [[ ${MERGE_TYPE} != "binary" ]]; then - if is-flagq "-g*" && ! is-flagq "-g*0" ; then - einfo "Checking for sufficient disk space and memory to build ${PN} with debugging CFLAGS" - check-reqs_pkg_pretend - fi - fi -} - -pkg_setup() { - python-any-r1_pkg_setup - linux-info_pkg_setup - # https://github.com/nodejs/node/issues/62676 - Can't build Temporal with system ICU. - if use system-icu; then - ewarn "The ES2026 'Temporal' datetime feature/object is not available when using system-icu." - ewarn "Please consider whether this is desirable for your use case." - ewarn "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal" - fi - - use temporal && rust_pkg_setup -} - -src_prepare() { - tc-export AR CC CXX PKG_CONFIG - export V=1 - export BUILDTYPE=Release - - # fix compilation on Darwin - # https://code.google.com/p/gyp/issues/detail?id=260 - sed -i -e '/append("-arch")/{N;d;}' tools/gyp/pylib/gyp/xcode_emulation.py || die - - # proper libdir, hat tip @ryanpcmcquen https://github.com/iojs/io.js/issues/504 - local LIBDIR=$(get_libdir) - sed -i -e "s|lib/|${LIBDIR}/|g" tools/install.py || die - sed -i -e "s/'lib'/'${LIBDIR}'/" deps/npm/lib/npm.js || die - - # Avoid writing a depfile, not useful - sed -i -e "/DEPFLAGS =/d" tools/gyp/pylib/gyp/generator/make.py || die - - sed -i -e "/'-O3'/d" common.gypi node.gypi || die - - # debug builds. change install path, remove optimisations and override buildtype - if use debug; then - sed -i -e "s|out/Release/|out/Debug/|g" tools/install.py || die - BUILDTYPE=Debug - fi - - # We need to disable mprotect on two files when it builds Bug 694100. - use pax-kernel && - PATCHES+=( "${FILESDIR}"/${PN}-24.1.0-paxmarking.patch ) - - use ppc64 && - PATCHES+=( "${FILESDIR}/${PN}-24.11.1-restore-ppc64be.patch" ) - - default -} - -src_configure() { - xdg_environment_reset - - # LTO compiler flags are handled by configure.py itself - filter-lto - # The warnings are *so* noisy and make build.logs massive - append-cxxflags $(test-flags-CXX -Wno-template-id-cdtor) - # https://bugs.gentoo.org/931514 - use arm64 && append-flags $(test-flags-CXX -mbranch-protection=none) - - local myconf=( - --ninja - --shared-ada - --shared-brotli - --shared-cares - --shared-libuv - --shared-nghttp2 - --shared-nghttp3 - --shared-ngtcp2 - --shared-simdjson - --shared-simdutf - --shared-sqlite - --shared-zlib - ) - use debug && myconf+=( --debug ) - use lto && myconf+=( --enable-lto ) - if use system-icu; then - # No Temporal support: https://github.com/nodejs/node/issues/62676 - myconf+=( --with-intl=system-icu ) - elif use icu; then - # Full embedded ICU (Temporal works) - myconf+=( --with-intl=full-icu ) - else - # Default embedded subset (Temporal works, saves space over full-icu) - myconf+=( --with-intl=small-icu ) - fi - use inspector || myconf+=( --without-inspector ) - use npm || myconf+=( --without-npm ) - use snapshot || myconf+=( --without-node-snapshot ) - if use ssl; then - use system-ssl && myconf+=( --shared-openssl --openssl-use-def-ca-store ) - else - myconf+=( --without-ssl ) - fi - ! use temporal && myconf+=( --v8-disable-temporal-support ) - - local myarch="" - case "${ARCH}:${ABI}" in - *:amd64) myarch="x64";; - *:arm) myarch="arm";; - *:arm64) myarch="arm64";; - loong:lp64*) myarch="loong64";; - riscv:lp64*) myarch="riscv64";; - *:ppc64) myarch="ppc64";; - *:x32) myarch="x32";; - *:x86) myarch="ia32";; - *) myarch="${ABI}";; - esac - - GYP_DEFINES="linux_use_gold_flags=0 - linux_use_bundled_binutils=0 - linux_use_bundled_gold=0" \ - "${EPYTHON}" configure.py \ - --prefix="${EPREFIX}"/usr \ - --dest-cpu=${myarch} \ - "${myconf[@]}" || die -} - -src_compile() { - eninja -C out/Release - # There's just a plain make target for the FFI tests, and it doesn't support ninja. - use test && emake build-ffi-tests -} - -src_install() { - local LIBDIR="${ED}/usr/$(get_libdir)" - default - - pax-mark -m "${ED}"/usr/bin/node - - if use doc; then - docinto html - dodoc -r "${S}"/doc/* - fi - - if use npm; then - keepdir /etc/npm - echo "NPM_CONFIG_GLOBALCONFIG=${EPREFIX}/etc/npm/npmrc" > "${T}"/50npm - doenvd "${T}"/50npm - - # Install bash completion for `npm` - local tmp_npm_completion_file="$(TMPDIR="${T}" mktemp -t npm.XXXXXXXXXX)" - "${ED}/usr/bin/npm" completion > "${tmp_npm_completion_file}" - newbashcomp "${tmp_npm_completion_file}" npm - - # Move man pages - doman "${LIBDIR}"/node_modules/npm/man/man{1,5,7}/* - - # Clean up - rm -f "${LIBDIR}"/node_modules/npm/{.mailmap,.npmignore,Makefile} - - local find_exp="-or -name" - local find_name=() - for match in "AUTHORS*" "CHANGELOG*" "CONTRIBUT*" "README*" \ - ".travis.yml" ".eslint*" ".wercker.yml" ".npmignore" \ - "*.bat" "*.cmd"; do - find_name+=( ${find_exp} "${match}" ) - done - - # Remove various development and/or inappropriate files and - # useless docs of dependend packages. - # Strict match for LICENSE files to avoid purging actual logic (e.g. license.js) - # which breaks npm. - find "${LIBDIR}"/node_modules \ - \( -type d -name examples \) -or \( -type f \( \ - \( -iname "LICEN?E*" -not -name "*.js" -not -name "*.json" \) \ - "${find_name[@]}" \ - \) \) -exec rm -rf "{}" \; - fi - - mv "${ED}"/usr/share/doc/node "${ED}"/usr/share/doc/${PF} || die -} - -src_test() { - local drop_tests=( - test/parallel/test-dns.js - test/parallel/test-dns-resolveany-bad-ancount.js - test/parallel/test-dns-setserver-when-querying.js - test/parallel/test-dotenv.js - test/parallel/test-fs-mkdir.js - test/parallel/test-fs-read-stream.js - test/parallel/test-fs-utimes-y2K38.js - test/parallel/test-fs-watch-recursive-add-file.js - test/parallel/test-http2-client-set-priority.js - test/parallel/test-http2-priority-event.js - test/parallel/test-process-euid-egid.js - test/parallel/test-process-get-builtin.mjs - test/parallel/test-process-initgroups.js - test/parallel/test-process-setgroups.js - test/parallel/test-process-uid-gid.js - test/parallel/test-release-npm.js - # Unreliable when using system libraries and ASLR. - # The variations between snapshot generation passes appear limited to memory - # allocation pointers leaking into the serialised V8 blob array; the actual - # bytecode and engine structures remain consistently... consistent. - test/parallel/test-snapshot-reproducible.js - test/parallel/test-socket-write-after-fin-error.js - test/parallel/test-strace-openat-openssl.js - test/sequential/test-tls-session-timeout.js - test/sequential/test-util-debug.js - ) - - if has_version "sys-libs/zlib-ng" ; then - elog "Some tests will be skipped due to being known to fail with sys-libs/zlib-ng" - elog - drop_tests+=( test/parallel/test-process-versions.js ) - drop_tests+=( test/parallel/test-zlib-zip-interop.js ) - elog - fi - - use inspector || - drop_tests+=( - test/parallel/test-inspector-emit-protocol-event.js - test/parallel/test-inspector-network-arbitrary-data.js - test/parallel/test-inspector-network-domain.js - test/parallel/test-inspector-network-fetch.js - test/parallel/test-inspector-network-http.js - test/sequential/test-watch-mode.mjs - ) - rm -f "${drop_tests[@]}" || die "disabling tests failed" - - out/${BUILDTYPE}/cctest || die - "${EPYTHON}" tools/test.py --mode=${BUILDTYPE,,} --flaky-tests=dontcare -J message parallel sequential || die -} - -pkg_postinst() { - if use npm; then - ewarn "remember to run: source /etc/profile if you plan to use nodejs" - ewarn " in your current shell" - fi - - if use system-icu; then - ewarn "" - ewarn "Node.js was built with USE='system-icu'." - ewarn "The JavaScript 'Temporal' API has been disabled." - ewarn "If you need full ES2026+ compliance, rebuild with USE='-system-icu'." - ewarn "" - fi -} diff --git a/net-libs/nodejs/nodejs-26.11.1-r1.ebuild b/net-libs/nodejs/nodejs-26.11.1-r1.ebuild deleted file mode 100644 index febdb9e46cfc..000000000000 --- a/net-libs/nodejs/nodejs-26.11.1-r1.ebuild +++ /dev/null @@ -1,348 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -CONFIG_CHECK="~ADVISE_SYSCALLS" -PYTHON_COMPAT=( python3_{13..14} ) -PYTHON_REQ_USE="threads(+)" -RUST_OPTIONAL=1 -RUST_MIN_VER="1.86.0" - -inherit check-reqs flag-o-matic linux-info ninja-utils pax-utils -inherit python-any-r1 rust shell-completion toolchain-funcs xdg-utils - -DESCRIPTION="A JavaScript runtime built on Chrome's V8 JavaScript engine" -HOMEPAGE="https://nodejs.org/" -LICENSE="Apache-1.1 Apache-2.0 BlueOak-1.0.0 BSD BSD-2 MIT npm? ( Artistic-2 )" - -if [[ ${PV} == *9999 ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/nodejs/node" - SLOT="0" -else - SRC_URI="https://nodejs.org/dist/v${PV}/node-v${PV}.tar.xz" - SLOT="0/$(ver_cut 1)" - KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc64 ~riscv ~x86 ~x64-macos" - S="${WORKDIR}/node-v${PV}" -fi - -IUSE="cpu_flags_x86_sse2 debug doc +icu +inspector lto +npm pax-kernel +snapshot +ssl system-icu +system-ssl test temporal" -REQUIRED_USE="inspector? ( icu ssl ) - npm? ( ssl ) - system-icu? ( icu ) - system-ssl? ( ssl ) - temporal? ( icu !system-icu ) - x86? ( cpu_flags_x86_sse2 )" - -RESTRICT="!test? ( test )" - -COMMON_DEPEND=">=app-arch/brotli-1.1.0:= - dev-db/sqlite:3 - >=dev-cpp/ada-4.0.0:= - >=dev-cpp/simdutf-9.2.1:=[atomic-base64] - >=dev-libs/libuv-1.52.1:= - >=dev-libs/simdjson-4.6.11:= - >=net-dns/c-ares-1.34.8:= - >=net-libs/nghttp2-1.70.0:= - >=net-libs/nghttp3-1.18.0:= - virtual/zlib:= - system-icu? ( >=dev-libs/icu-78:= ) - system-ssl? ( - >=net-libs/ngtcp2-1.25.0:= - >=dev-libs/openssl-3.5.9:0= - ) - !system-ssl? ( >=net-libs/ngtcp2-1.25.0:=[-gnutls] ) - || ( - sys-devel/gcc:* - llvm-runtimes/libatomic-stub - )" -BDEPEND="${PYTHON_DEPS} - app-alternatives/ninja - pax-kernel? ( sys-apps/elfix ) - sys-apps/coreutils - temporal? ( ${RUST_DEPEND} ) - test? ( net-misc/curl ) - virtual/pkgconfig" -DEPEND="${COMMON_DEPEND}" -RDEPEND="${COMMON_DEPEND}" - -# These are measured on a loong machine with -ggdb on, and only checked -# if debugging flags are present in CFLAGS. -# -# The final link consumed a little more than 7GiB alone, so 8GiB is the lower -# limit for memory usage. Disk usage was 19.1GiB for the build directory and -# 1.2GiB for the installed image, so we leave some room for architectures with -# fatter binaries and set the disk requirement to 22GiB. -CHECKREQS_MEMORY="8G" -CHECKREQS_DISK_BUILD="22G" - -PATCHES=( - "${FILESDIR}"/${PN}-26.6.0-format-cstdlib.patch - "${FILESDIR}"/${PN}-26.6.0-v8-climits.patch - "${FILESDIR}"/${PN}-26.11.1-add-missing-funcational-inc.patch - "${FILESDIR}"/${PN}-26.10.0-x86-SSE2-fix.patch - "${FILESDIR}"/${PN}-26.10.0-simdutf-use-system-header.patch - "${FILESDIR}"/${PN}-26.11.1-include-EACCES-in-mkdir-test.patch -) - -pkg_pretend() { - if [[ ${MERGE_TYPE} != "binary" ]]; then - if is-flagq "-g*" && ! is-flagq "-g*0" ; then - einfo "Checking for sufficient disk space and memory to build ${PN} with debugging CFLAGS" - check-reqs_pkg_pretend - fi - fi -} - -pkg_setup() { - python-any-r1_pkg_setup - linux-info_pkg_setup - # https://github.com/nodejs/node/issues/62676 - Can't build Temporal with system ICU. - if use system-icu; then - ewarn "The ES2026 'Temporal' datetime feature/object is not available when using system-icu." - ewarn "Please consider whether this is desirable for your use case." - ewarn "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal" - fi - - use temporal && rust_pkg_setup -} - -src_prepare() { - tc-export AR CC CXX PKG_CONFIG - export V=1 - export BUILDTYPE=Release - - # fix compilation on Darwin - # https://code.google.com/p/gyp/issues/detail?id=260 - sed -i -e '/append("-arch")/{N;d;}' tools/gyp/pylib/gyp/xcode_emulation.py || die - - # proper libdir, hat tip @ryanpcmcquen https://github.com/iojs/io.js/issues/504 - local LIBDIR=$(get_libdir) - sed -i -e "s|lib/|${LIBDIR}/|g" tools/install.py || die - sed -i -e "s/'lib'/'${LIBDIR}'/" deps/npm/lib/npm.js || die - - # Avoid writing a depfile, not useful - sed -i -e "/DEPFLAGS =/d" tools/gyp/pylib/gyp/generator/make.py || die - - sed -i -e "/'-O3'/d" common.gypi node.gypi || die - - # debug builds. change install path, remove optimisations and override buildtype - if use debug; then - sed -i -e "s|out/Release/|out/Debug/|g" tools/install.py || die - BUILDTYPE=Debug - fi - - # We need to disable mprotect on two files when it builds Bug 694100. - use pax-kernel && - PATCHES+=( "${FILESDIR}"/${PN}-24.1.0-paxmarking.patch ) - - use ppc64 && - PATCHES+=( "${FILESDIR}/${PN}-24.11.1-restore-ppc64be.patch" ) - - default -} - -src_configure() { - xdg_environment_reset - - # LTO compiler flags are handled by configure.py itself - filter-lto - # The warnings are *so* noisy and make build.logs massive - append-cxxflags $(test-flags-CXX -Wno-template-id-cdtor) - # https://bugs.gentoo.org/931514 - use arm64 && append-flags $(test-flags-CXX -mbranch-protection=none) - - # Highway compilation error on riscv - # See: https://bugs.gentoo.org/983938 - # - # Upstream Highway fixed the issue, but V8 has not picked up a Highway - # version that includes it, so Node still hits the old headers. - # this workaround will stop Highway from compiling RVV, and stop the host - # compiler from emitting RVV in non-V8 code. V8 can still emit RVV at runtime - # on machines that have it. - # - # Additionally, force the EMU128 (128-bit scalar emulation) for boards - # without RVV (VisionFive V2 / JH7110). - # This will ensure V8's FixedTag<..., 16> in the JSON stringifier is legal. - use riscv && append-cxxflags -DHWY_DISABLED_TARGETS=HWY_RVV -DHWY_BROKEN_EMU128=0 - - local myconf=( - --ninja - --shared-ada - --shared-brotli - --shared-cares - --shared-libuv - --shared-nghttp2 - --shared-nghttp3 - --shared-ngtcp2 - --shared-simdjson - --shared-simdutf - --shared-sqlite - --shared-zlib - ) - use debug && myconf+=( --debug ) - use lto && myconf+=( --enable-lto ) - if use system-icu; then - # No Temporal support: https://github.com/nodejs/node/issues/62676 - myconf+=( --with-intl=system-icu ) - elif use icu; then - # Full embedded ICU (Temporal works) - myconf+=( --with-intl=full-icu ) - else - # Default embedded subset (Temporal works, saves space over full-icu) - myconf+=( --with-intl=small-icu ) - fi - use inspector || myconf+=( --without-inspector ) - use npm || myconf+=( --without-npm ) - use snapshot || myconf+=( --without-node-snapshot ) - if use ssl; then - use system-ssl && myconf+=( --shared-openssl --openssl-use-def-ca-store ) - else - myconf+=( --without-ssl ) - fi - ! use temporal && myconf+=( --v8-disable-temporal-support ) - - local myarch="" - case "${ARCH}:${ABI}" in - *:amd64) myarch="x64";; - *:arm) myarch="arm";; - *:arm64) myarch="arm64";; - loong:lp64*) myarch="loong64";; - riscv:lp64*) myarch="riscv64";; - *:ppc64) myarch="ppc64";; - *:x32) myarch="x32";; - *:x86) myarch="ia32";; - *) myarch="${ABI}";; - esac - - GYP_DEFINES="linux_use_gold_flags=0 - linux_use_bundled_binutils=0 - linux_use_bundled_gold=0" \ - "${EPYTHON}" configure.py \ - --prefix="${EPREFIX}"/usr \ - --dest-cpu=${myarch} \ - "${myconf[@]}" || die -} - -src_compile() { - eninja -C out/Release - # There's just a plain make target for the FFI tests, and it doesn't support ninja. - use test && emake build-ffi-tests -} - -src_install() { - local LIBDIR="${ED}/usr/$(get_libdir)" - default - - pax-mark -m "${ED}"/usr/bin/node - - if use doc; then - docinto html - dodoc -r "${S}"/doc/* - fi - - if use npm; then - keepdir /etc/npm - echo "NPM_CONFIG_GLOBALCONFIG=${EPREFIX}/etc/npm/npmrc" > "${T}"/50npm - doenvd "${T}"/50npm - - # Install bash completion for `npm` - local tmp_npm_completion_file="$(TMPDIR="${T}" mktemp -t npm.XXXXXXXXXX)" - "${ED}/usr/bin/npm" completion > "${tmp_npm_completion_file}" - newbashcomp "${tmp_npm_completion_file}" npm - - # Move man pages - doman "${LIBDIR}"/node_modules/npm/man/man{1,5,7}/* - - # Clean up - rm -f "${LIBDIR}"/node_modules/npm/{.mailmap,.npmignore,Makefile} - - local find_exp="-or -name" - local find_name=() - for match in "AUTHORS*" "CHANGELOG*" "CONTRIBUT*" "README*" \ - ".travis.yml" ".eslint*" ".wercker.yml" ".npmignore" \ - "*.bat" "*.cmd"; do - find_name+=( ${find_exp} "${match}" ) - done - - # Remove various development and/or inappropriate files and - # useless docs of dependend packages. - # Strict match for LICENSE files to avoid purging actual logic (e.g. license.js) - # which breaks npm. - find "${LIBDIR}"/node_modules \ - \( -type d -name examples \) -or \( -type f \( \ - \( -iname "LICEN?E*" -not -name "*.js" -not -name "*.json" \) \ - "${find_name[@]}" \ - \) \) -exec rm -rf "{}" \; - fi - - mv "${ED}"/usr/share/doc/node "${ED}"/usr/share/doc/${PF} || die -} - -src_test() { - local drop_tests=( - test/parallel/test-dns.js - test/parallel/test-dns-resolveany-bad-ancount.js - test/parallel/test-dns-setserver-when-querying.js - test/parallel/test-dotenv.js - test/parallel/test-fs-mkdir.js - test/parallel/test-fs-read-stream.js - test/parallel/test-fs-utimes-y2K38.js - test/parallel/test-fs-watch-recursive-add-file.js - test/parallel/test-http2-client-set-priority.js - test/parallel/test-http2-priority-event.js - test/parallel/test-process-euid-egid.js - test/parallel/test-process-get-builtin.mjs - test/parallel/test-process-initgroups.js - test/parallel/test-process-setgroups.js - test/parallel/test-process-uid-gid.js - test/parallel/test-release-npm.js - # Unreliable when using system libraries and ASLR. - # The variations between snapshot generation passes appear limited to memory - # allocation pointers leaking into the serialised V8 blob array; the actual - # bytecode and engine structures remain consistently... consistent. - test/parallel/test-snapshot-reproducible.js - test/parallel/test-socket-write-after-fin-error.js - test/parallel/test-strace-openat-openssl.js - test/sequential/test-tls-session-timeout.js - test/sequential/test-util-debug.js - ) - - if has_version "sys-libs/zlib-ng" ; then - elog "Some tests will be skipped due to being known to fail with sys-libs/zlib-ng" - elog - drop_tests+=( test/parallel/test-process-versions.js ) - drop_tests+=( test/parallel/test-zlib-zip-interop.js ) - elog - fi - - use inspector || - drop_tests+=( - test/parallel/test-inspector-emit-protocol-event.js - test/parallel/test-inspector-network-arbitrary-data.js - test/parallel/test-inspector-network-domain.js - test/parallel/test-inspector-network-fetch.js - test/parallel/test-inspector-network-http.js - test/sequential/test-watch-mode.mjs - ) - rm -f "${drop_tests[@]}" || die "disabling tests failed" - - out/${BUILDTYPE}/cctest || die - "${EPYTHON}" tools/test.py --mode=${BUILDTYPE,,} --flaky-tests=dontcare -J message parallel sequential || die -} - -pkg_postinst() { - if use npm; then - ewarn "remember to run: source /etc/profile if you plan to use nodejs" - ewarn " in your current shell" - fi - - if use system-icu; then - ewarn "" - ewarn "Node.js was built with USE='system-icu'." - ewarn "The JavaScript 'Temporal' API has been disabled." - ewarn "If you need full ES2026+ compliance, rebuild with USE='-system-icu'." - ewarn "" - fi -} diff --git a/net-libs/nodejs/nodejs-26.11.1.ebuild b/net-libs/nodejs/nodejs-26.11.1.ebuild deleted file mode 100644 index 4b5e17587d9c..000000000000 --- a/net-libs/nodejs/nodejs-26.11.1.ebuild +++ /dev/null @@ -1,334 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -CONFIG_CHECK="~ADVISE_SYSCALLS" -PYTHON_COMPAT=( python3_{13..14} ) -PYTHON_REQ_USE="threads(+)" -RUST_OPTIONAL=1 -RUST_MIN_VER="1.86.0" - -inherit check-reqs flag-o-matic linux-info ninja-utils pax-utils -inherit python-any-r1 rust shell-completion toolchain-funcs xdg-utils - -DESCRIPTION="A JavaScript runtime built on Chrome's V8 JavaScript engine" -HOMEPAGE="https://nodejs.org/" -LICENSE="Apache-1.1 Apache-2.0 BlueOak-1.0.0 BSD BSD-2 MIT npm? ( Artistic-2 )" - -if [[ ${PV} == *9999 ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/nodejs/node" - SLOT="0" -else - SRC_URI="https://nodejs.org/dist/v${PV}/node-v${PV}.tar.xz" - SLOT="0/$(ver_cut 1)" - KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc64 ~riscv ~x86 ~x64-macos" - S="${WORKDIR}/node-v${PV}" -fi - -IUSE="cpu_flags_x86_sse2 debug doc +icu +inspector lto +npm pax-kernel +snapshot +ssl system-icu +system-ssl test temporal" -REQUIRED_USE="inspector? ( icu ssl ) - npm? ( ssl ) - system-icu? ( icu ) - system-ssl? ( ssl ) - temporal? ( icu !system-icu ) - x86? ( cpu_flags_x86_sse2 )" - -RESTRICT="!test? ( test )" - -COMMON_DEPEND=">=app-arch/brotli-1.1.0:= - dev-db/sqlite:3 - >=dev-cpp/ada-4.0.0:= - >=dev-cpp/simdutf-9.2.1:=[atomic-base64] - >=dev-libs/libuv-1.52.1:= - >=dev-libs/simdjson-4.6.11:= - >=net-dns/c-ares-1.34.8:= - >=net-libs/nghttp2-1.70.0:= - >=net-libs/nghttp3-1.18.0:= - virtual/zlib:= - system-icu? ( >=dev-libs/icu-78:= ) - system-ssl? ( - >=net-libs/ngtcp2-1.25.0:= - >=dev-libs/openssl-3.5.9:0= - ) - !system-ssl? ( >=net-libs/ngtcp2-1.25.0:=[-gnutls] ) - || ( - sys-devel/gcc:* - llvm-runtimes/libatomic-stub - )" -BDEPEND="${PYTHON_DEPS} - app-alternatives/ninja - pax-kernel? ( sys-apps/elfix ) - sys-apps/coreutils - temporal? ( ${RUST_DEPEND} ) - test? ( net-misc/curl ) - virtual/pkgconfig" -DEPEND="${COMMON_DEPEND}" -RDEPEND="${COMMON_DEPEND}" - -# These are measured on a loong machine with -ggdb on, and only checked -# if debugging flags are present in CFLAGS. -# -# The final link consumed a little more than 7GiB alone, so 8GiB is the lower -# limit for memory usage. Disk usage was 19.1GiB for the build directory and -# 1.2GiB for the installed image, so we leave some room for architectures with -# fatter binaries and set the disk requirement to 22GiB. -CHECKREQS_MEMORY="8G" -CHECKREQS_DISK_BUILD="22G" - -PATCHES=( - "${FILESDIR}"/${PN}-26.6.0-format-cstdlib.patch - "${FILESDIR}"/${PN}-26.6.0-v8-climits.patch - "${FILESDIR}"/${PN}-26.11.1-add-missing-funcational-inc.patch - "${FILESDIR}"/${PN}-26.10.0-x86-SSE2-fix.patch - "${FILESDIR}"/${PN}-26.10.0-simdutf-use-system-header.patch - "${FILESDIR}"/${PN}-26.11.1-include-EACCES-in-mkdir-test.patch -) - -pkg_pretend() { - if [[ ${MERGE_TYPE} != "binary" ]]; then - if is-flagq "-g*" && ! is-flagq "-g*0" ; then - einfo "Checking for sufficient disk space and memory to build ${PN} with debugging CFLAGS" - check-reqs_pkg_pretend - fi - fi -} - -pkg_setup() { - python-any-r1_pkg_setup - linux-info_pkg_setup - # https://github.com/nodejs/node/issues/62676 - Can't build Temporal with system ICU. - if use system-icu; then - ewarn "The ES2026 'Temporal' datetime feature/object is not available when using system-icu." - ewarn "Please consider whether this is desirable for your use case." - ewarn "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal" - fi - - use temporal && rust_pkg_setup -} - -src_prepare() { - tc-export AR CC CXX PKG_CONFIG - export V=1 - export BUILDTYPE=Release - - # fix compilation on Darwin - # https://code.google.com/p/gyp/issues/detail?id=260 - sed -i -e '/append("-arch")/{N;d;}' tools/gyp/pylib/gyp/xcode_emulation.py || die - - # proper libdir, hat tip @ryanpcmcquen https://github.com/iojs/io.js/issues/504 - local LIBDIR=$(get_libdir) - sed -i -e "s|lib/|${LIBDIR}/|g" tools/install.py || die - sed -i -e "s/'lib'/'${LIBDIR}'/" deps/npm/lib/npm.js || die - - # Avoid writing a depfile, not useful - sed -i -e "/DEPFLAGS =/d" tools/gyp/pylib/gyp/generator/make.py || die - - sed -i -e "/'-O3'/d" common.gypi node.gypi || die - - # debug builds. change install path, remove optimisations and override buildtype - if use debug; then - sed -i -e "s|out/Release/|out/Debug/|g" tools/install.py || die - BUILDTYPE=Debug - fi - - # We need to disable mprotect on two files when it builds Bug 694100. - use pax-kernel && - PATCHES+=( "${FILESDIR}"/${PN}-24.1.0-paxmarking.patch ) - - use ppc64 && - PATCHES+=( "${FILESDIR}/${PN}-24.11.1-restore-ppc64be.patch" ) - - default -} - -src_configure() { - xdg_environment_reset - - # LTO compiler flags are handled by configure.py itself - filter-lto - # The warnings are *so* noisy and make build.logs massive - append-cxxflags $(test-flags-CXX -Wno-template-id-cdtor) - # https://bugs.gentoo.org/931514 - use arm64 && append-flags $(test-flags-CXX -mbranch-protection=none) - - local myconf=( - --ninja - --shared-ada - --shared-brotli - --shared-cares - --shared-libuv - --shared-nghttp2 - --shared-nghttp3 - --shared-ngtcp2 - --shared-simdjson - --shared-simdutf - --shared-sqlite - --shared-zlib - ) - use debug && myconf+=( --debug ) - use lto && myconf+=( --enable-lto ) - if use system-icu; then - # No Temporal support: https://github.com/nodejs/node/issues/62676 - myconf+=( --with-intl=system-icu ) - elif use icu; then - # Full embedded ICU (Temporal works) - myconf+=( --with-intl=full-icu ) - else - # Default embedded subset (Temporal works, saves space over full-icu) - myconf+=( --with-intl=small-icu ) - fi - use inspector || myconf+=( --without-inspector ) - use npm || myconf+=( --without-npm ) - use snapshot || myconf+=( --without-node-snapshot ) - if use ssl; then - use system-ssl && myconf+=( --shared-openssl --openssl-use-def-ca-store ) - else - myconf+=( --without-ssl ) - fi - ! use temporal && myconf+=( --v8-disable-temporal-support ) - - local myarch="" - case "${ARCH}:${ABI}" in - *:amd64) myarch="x64";; - *:arm) myarch="arm";; - *:arm64) myarch="arm64";; - loong:lp64*) myarch="loong64";; - riscv:lp64*) myarch="riscv64";; - *:ppc64) myarch="ppc64";; - *:x32) myarch="x32";; - *:x86) myarch="ia32";; - *) myarch="${ABI}";; - esac - - GYP_DEFINES="linux_use_gold_flags=0 - linux_use_bundled_binutils=0 - linux_use_bundled_gold=0" \ - "${EPYTHON}" configure.py \ - --prefix="${EPREFIX}"/usr \ - --dest-cpu=${myarch} \ - "${myconf[@]}" || die -} - -src_compile() { - eninja -C out/Release - # There's just a plain make target for the FFI tests, and it doesn't support ninja. - use test && emake build-ffi-tests -} - -src_install() { - local LIBDIR="${ED}/usr/$(get_libdir)" - default - - pax-mark -m "${ED}"/usr/bin/node - - if use doc; then - docinto html - dodoc -r "${S}"/doc/* - fi - - if use npm; then - keepdir /etc/npm - echo "NPM_CONFIG_GLOBALCONFIG=${EPREFIX}/etc/npm/npmrc" > "${T}"/50npm - doenvd "${T}"/50npm - - # Install bash completion for `npm` - local tmp_npm_completion_file="$(TMPDIR="${T}" mktemp -t npm.XXXXXXXXXX)" - "${ED}/usr/bin/npm" completion > "${tmp_npm_completion_file}" - newbashcomp "${tmp_npm_completion_file}" npm - - # Move man pages - doman "${LIBDIR}"/node_modules/npm/man/man{1,5,7}/* - - # Clean up - rm -f "${LIBDIR}"/node_modules/npm/{.mailmap,.npmignore,Makefile} - - local find_exp="-or -name" - local find_name=() - for match in "AUTHORS*" "CHANGELOG*" "CONTRIBUT*" "README*" \ - ".travis.yml" ".eslint*" ".wercker.yml" ".npmignore" \ - "*.bat" "*.cmd"; do - find_name+=( ${find_exp} "${match}" ) - done - - # Remove various development and/or inappropriate files and - # useless docs of dependend packages. - # Strict match for LICENSE files to avoid purging actual logic (e.g. license.js) - # which breaks npm. - find "${LIBDIR}"/node_modules \ - \( -type d -name examples \) -or \( -type f \( \ - \( -iname "LICEN?E*" -not -name "*.js" -not -name "*.json" \) \ - "${find_name[@]}" \ - \) \) -exec rm -rf "{}" \; - fi - - mv "${ED}"/usr/share/doc/node "${ED}"/usr/share/doc/${PF} || die -} - -src_test() { - local drop_tests=( - test/parallel/test-dns.js - test/parallel/test-dns-resolveany-bad-ancount.js - test/parallel/test-dns-setserver-when-querying.js - test/parallel/test-dotenv.js - test/parallel/test-fs-mkdir.js - test/parallel/test-fs-read-stream.js - test/parallel/test-fs-utimes-y2K38.js - test/parallel/test-fs-watch-recursive-add-file.js - test/parallel/test-http2-client-set-priority.js - test/parallel/test-http2-priority-event.js - test/parallel/test-process-euid-egid.js - test/parallel/test-process-get-builtin.mjs - test/parallel/test-process-initgroups.js - test/parallel/test-process-setgroups.js - test/parallel/test-process-uid-gid.js - test/parallel/test-release-npm.js - # Unreliable when using system libraries and ASLR. - # The variations between snapshot generation passes appear limited to memory - # allocation pointers leaking into the serialised V8 blob array; the actual - # bytecode and engine structures remain consistently... consistent. - test/parallel/test-snapshot-reproducible.js - test/parallel/test-socket-write-after-fin-error.js - test/parallel/test-strace-openat-openssl.js - test/sequential/test-tls-session-timeout.js - test/sequential/test-util-debug.js - ) - - if has_version "sys-libs/zlib-ng" ; then - elog "Some tests will be skipped due to being known to fail with sys-libs/zlib-ng" - elog - drop_tests+=( test/parallel/test-process-versions.js ) - drop_tests+=( test/parallel/test-zlib-zip-interop.js ) - elog - fi - - use inspector || - drop_tests+=( - test/parallel/test-inspector-emit-protocol-event.js - test/parallel/test-inspector-network-arbitrary-data.js - test/parallel/test-inspector-network-domain.js - test/parallel/test-inspector-network-fetch.js - test/parallel/test-inspector-network-http.js - test/sequential/test-watch-mode.mjs - ) - rm -f "${drop_tests[@]}" || die "disabling tests failed" - - out/${BUILDTYPE}/cctest || die - "${EPYTHON}" tools/test.py --mode=${BUILDTYPE,,} --flaky-tests=dontcare -J message parallel sequential || die -} - -pkg_postinst() { - if use npm; then - ewarn "remember to run: source /etc/profile if you plan to use nodejs" - ewarn " in your current shell" - fi - - if use system-icu; then - ewarn "" - ewarn "Node.js was built with USE='system-icu'." - ewarn "The JavaScript 'Temporal' API has been disabled." - ewarn "If you need full ES2026+ compliance, rebuild with USE='-system-icu'." - ewarn "" - fi -} diff --git a/net-libs/nodejs/nodejs-26.12.0.ebuild b/net-libs/nodejs/nodejs-26.12.0.ebuild deleted file mode 100644 index ad92b0cc4a75..000000000000 --- a/net-libs/nodejs/nodejs-26.12.0.ebuild +++ /dev/null @@ -1,347 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -CONFIG_CHECK="~ADVISE_SYSCALLS" -PYTHON_COMPAT=( python3_{13..14} ) -PYTHON_REQ_USE="threads(+)" -RUST_OPTIONAL=1 -RUST_MIN_VER="1.86.0" - -inherit check-reqs flag-o-matic linux-info ninja-utils pax-utils -inherit python-any-r1 rust shell-completion toolchain-funcs xdg-utils - -DESCRIPTION="A JavaScript runtime built on Chrome's V8 JavaScript engine" -HOMEPAGE="https://nodejs.org/" -LICENSE="Apache-1.1 Apache-2.0 BlueOak-1.0.0 BSD BSD-2 MIT npm? ( Artistic-2 )" - -if [[ ${PV} == *9999 ]]; then - inherit git-r3 - EGIT_REPO_URI="https://github.com/nodejs/node" - SLOT="0" -else - SRC_URI="https://nodejs.org/dist/v${PV}/node-v${PV}.tar.xz" - SLOT="0/$(ver_cut 1)" - KEYWORDS="~amd64 ~arm ~arm64 ~loong ~ppc64 ~riscv ~x86 ~x64-macos" - S="${WORKDIR}/node-v${PV}" -fi - -IUSE="cpu_flags_x86_sse2 debug doc +icu +inspector lto +npm pax-kernel +snapshot +ssl system-icu +system-ssl test temporal" -REQUIRED_USE="inspector? ( icu ssl ) - npm? ( ssl ) - system-icu? ( icu ) - system-ssl? ( ssl ) - temporal? ( icu !system-icu ) - x86? ( cpu_flags_x86_sse2 )" - -RESTRICT="!test? ( test )" - -COMMON_DEPEND=">=app-arch/brotli-1.1.0:= - dev-db/sqlite:3 - >=dev-cpp/ada-4.0.0:= - >=dev-cpp/simdutf-9.2.1:=[atomic-base64] - >=dev-libs/libuv-1.52.1:= - >=dev-libs/simdjson-5.0.3:= - >=net-dns/c-ares-1.34.8:= - >=net-libs/nghttp2-1.70.0:= - >=net-libs/nghttp3-1.18.0:= - virtual/zlib:= - system-icu? ( >=dev-libs/icu-78:= ) - system-ssl? ( - >=net-libs/ngtcp2-1.25.0:= - >=dev-libs/openssl-3.5.9:0= - ) - !system-ssl? ( >=net-libs/ngtcp2-1.25.0:=[-gnutls] ) - || ( - sys-devel/gcc:* - llvm-runtimes/libatomic-stub - )" -BDEPEND="${PYTHON_DEPS} - app-alternatives/ninja - pax-kernel? ( sys-apps/elfix ) - sys-apps/coreutils - temporal? ( ${RUST_DEPEND} ) - test? ( net-misc/curl ) - virtual/pkgconfig" -DEPEND="${COMMON_DEPEND}" -RDEPEND="${COMMON_DEPEND}" - -# These are measured on a loong machine with -ggdb on, and only checked -# if debugging flags are present in CFLAGS. -# -# The final link consumed a little more than 7GiB alone, so 8GiB is the lower -# limit for memory usage. Disk usage was 19.1GiB for the build directory and -# 1.2GiB for the installed image, so we leave some room for architectures with -# fatter binaries and set the disk requirement to 22GiB. -CHECKREQS_MEMORY="8G" -CHECKREQS_DISK_BUILD="22G" - -PATCHES=( - "${FILESDIR}"/${PN}-26.12.0-v8-climits.patch - "${FILESDIR}"/${PN}-26.11.1-add-missing-funcational-inc.patch - "${FILESDIR}"/${PN}-26.10.0-x86-SSE2-fix.patch - "${FILESDIR}"/${PN}-26.10.0-simdutf-use-system-header.patch - "${FILESDIR}"/${PN}-26.11.1-include-EACCES-in-mkdir-test.patch -) - -pkg_pretend() { - if [[ ${MERGE_TYPE} != "binary" ]]; then - if is-flagq "-g*" && ! is-flagq "-g*0" ; then - einfo "Checking for sufficient disk space and memory to build ${PN} with debugging CFLAGS" - check-reqs_pkg_pretend - fi - fi -} - -pkg_setup() { - python-any-r1_pkg_setup - linux-info_pkg_setup - # https://github.com/nodejs/node/issues/62676 - Can't build Temporal with system ICU. - if use system-icu; then - ewarn "The ES2026 'Temporal' datetime feature/object is not available when using system-icu." - ewarn "Please consider whether this is desirable for your use case." - ewarn "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal" - fi - - use temporal && rust_pkg_setup -} - -src_prepare() { - tc-export AR CC CXX PKG_CONFIG - export V=1 - export BUILDTYPE=Release - - # fix compilation on Darwin - # https://code.google.com/p/gyp/issues/detail?id=260 - sed -i -e '/append("-arch")/{N;d;}' tools/gyp/pylib/gyp/xcode_emulation.py || die - - # proper libdir, hat tip @ryanpcmcquen https://github.com/iojs/io.js/issues/504 - local LIBDIR=$(get_libdir) - sed -i -e "s|lib/|${LIBDIR}/|g" tools/install.py || die - sed -i -e "s/'lib'/'${LIBDIR}'/" deps/npm/lib/npm.js || die - - # Avoid writing a depfile, not useful - sed -i -e "/DEPFLAGS =/d" tools/gyp/pylib/gyp/generator/make.py || die - - sed -i -e "/'-O3'/d" common.gypi node.gypi || die - - # debug builds. change install path, remove optimisations and override buildtype - if use debug; then - sed -i -e "s|out/Release/|out/Debug/|g" tools/install.py || die - BUILDTYPE=Debug - fi - - # We need to disable mprotect on two files when it builds Bug 694100. - use pax-kernel && - PATCHES+=( "${FILESDIR}"/${PN}-24.1.0-paxmarking.patch ) - - use ppc64 && - PATCHES+=( "${FILESDIR}/${PN}-24.11.1-restore-ppc64be.patch" ) - - default -} - -src_configure() { - xdg_environment_reset - - # LTO compiler flags are handled by configure.py itself - filter-lto - # The warnings are *so* noisy and make build.logs massive - append-cxxflags $(test-flags-CXX -Wno-template-id-cdtor) - # https://bugs.gentoo.org/931514 - use arm64 && append-flags $(test-flags-CXX -mbranch-protection=none) - - # Highway compilation error on riscv - # See: https://bugs.gentoo.org/983938 - # - # Upstream Highway fixed the issue, but V8 has not picked up a Highway - # version that includes it, so Node still hits the old headers. - # this workaround will stop Highway from compiling RVV, and stop the host - # compiler from emitting RVV in non-V8 code. V8 can still emit RVV at runtime - # on machines that have it. - # - # Additionally, force the EMU128 (128-bit scalar emulation) for boards - # without RVV (VisionFive V2 / JH7110). - # This will ensure V8's FixedTag<..., 16> in the JSON stringifier is legal. - use riscv && append-cxxflags -DHWY_DISABLED_TARGETS=HWY_RVV -DHWY_BROKEN_EMU128=0 - - local myconf=( - --ninja - --shared-ada - --shared-brotli - --shared-cares - --shared-libuv - --shared-nghttp2 - --shared-nghttp3 - --shared-ngtcp2 - --shared-simdjson - --shared-simdutf - --shared-sqlite - --shared-zlib - ) - use debug && myconf+=( --debug ) - use lto && myconf+=( --enable-lto ) - if use system-icu; then - # No Temporal support: https://github.com/nodejs/node/issues/62676 - myconf+=( --with-intl=system-icu ) - elif use icu; then - # Full embedded ICU (Temporal works) - myconf+=( --with-intl=full-icu ) - else - # Default embedded subset (Temporal works, saves space over full-icu) - myconf+=( --with-intl=small-icu ) - fi - use inspector || myconf+=( --without-inspector ) - use npm || myconf+=( --without-npm ) - use snapshot || myconf+=( --without-node-snapshot ) - if use ssl; then - use system-ssl && myconf+=( --shared-openssl --openssl-use-def-ca-store ) - else - myconf+=( --without-ssl ) - fi - ! use temporal && myconf+=( --v8-disable-temporal-support ) - - local myarch="" - case "${ARCH}:${ABI}" in - *:amd64) myarch="x64";; - *:arm) myarch="arm";; - *:arm64) myarch="arm64";; - loong:lp64*) myarch="loong64";; - riscv:lp64*) myarch="riscv64";; - *:ppc64) myarch="ppc64";; - *:x32) myarch="x32";; - *:x86) myarch="ia32";; - *) myarch="${ABI}";; - esac - - GYP_DEFINES="linux_use_gold_flags=0 - linux_use_bundled_binutils=0 - linux_use_bundled_gold=0" \ - "${EPYTHON}" configure.py \ - --prefix="${EPREFIX}"/usr \ - --dest-cpu=${myarch} \ - "${myconf[@]}" || die -} - -src_compile() { - eninja -C out/Release - # There's just a plain make target for the FFI tests, and it doesn't support ninja. - use test && emake build-ffi-tests -} - -src_install() { - local LIBDIR="${ED}/usr/$(get_libdir)" - default - - pax-mark -m "${ED}"/usr/bin/node - - if use doc; then - docinto html - dodoc -r "${S}"/doc/* - fi - - if use npm; then - keepdir /etc/npm - echo "NPM_CONFIG_GLOBALCONFIG=${EPREFIX}/etc/npm/npmrc" > "${T}"/50npm - doenvd "${T}"/50npm - - # Install bash completion for `npm` - local tmp_npm_completion_file="$(TMPDIR="${T}" mktemp -t npm.XXXXXXXXXX)" - "${ED}/usr/bin/npm" completion > "${tmp_npm_completion_file}" - newbashcomp "${tmp_npm_completion_file}" npm - - # Move man pages - doman "${LIBDIR}"/node_modules/npm/man/man{1,5,7}/* - - # Clean up - rm -f "${LIBDIR}"/node_modules/npm/{.mailmap,.npmignore,Makefile} - - local find_exp="-or -name" - local find_name=() - for match in "AUTHORS*" "CHANGELOG*" "CONTRIBUT*" "README*" \ - ".travis.yml" ".eslint*" ".wercker.yml" ".npmignore" \ - "*.bat" "*.cmd"; do - find_name+=( ${find_exp} "${match}" ) - done - - # Remove various development and/or inappropriate files and - # useless docs of dependend packages. - # Strict match for LICENSE files to avoid purging actual logic (e.g. license.js) - # which breaks npm. - find "${LIBDIR}"/node_modules \ - \( -type d -name examples \) -or \( -type f \( \ - \( -iname "LICEN?E*" -not -name "*.js" -not -name "*.json" \) \ - "${find_name[@]}" \ - \) \) -exec rm -rf "{}" \; - fi - - mv "${ED}"/usr/share/doc/node "${ED}"/usr/share/doc/${PF} || die -} - -src_test() { - local drop_tests=( - test/parallel/test-dns.js - test/parallel/test-dns-resolveany-bad-ancount.js - test/parallel/test-dns-setserver-when-querying.js - test/parallel/test-dotenv.js - test/parallel/test-fs-mkdir.js - test/parallel/test-fs-read-stream.js - test/parallel/test-fs-utimes-y2K38.js - test/parallel/test-fs-watch-recursive-add-file.js - test/parallel/test-http2-client-set-priority.js - test/parallel/test-http2-priority-event.js - test/parallel/test-process-euid-egid.js - test/parallel/test-process-get-builtin.mjs - test/parallel/test-process-initgroups.js - test/parallel/test-process-setgroups.js - test/parallel/test-process-uid-gid.js - test/parallel/test-release-npm.js - # Unreliable when using system libraries and ASLR. - # The variations between snapshot generation passes appear limited to memory - # allocation pointers leaking into the serialised V8 blob array; the actual - # bytecode and engine structures remain consistently... consistent. - test/parallel/test-snapshot-reproducible.js - test/parallel/test-socket-write-after-fin-error.js - test/parallel/test-strace-openat-openssl.js - test/sequential/test-tls-session-timeout.js - test/sequential/test-util-debug.js - ) - - if has_version "sys-libs/zlib-ng" ; then - elog "Some tests will be skipped due to being known to fail with sys-libs/zlib-ng" - elog - drop_tests+=( test/parallel/test-process-versions.js ) - drop_tests+=( test/parallel/test-zlib-zip-interop.js ) - elog - fi - - use inspector || - drop_tests+=( - test/parallel/test-inspector-emit-protocol-event.js - test/parallel/test-inspector-network-arbitrary-data.js - test/parallel/test-inspector-network-domain.js - test/parallel/test-inspector-network-fetch.js - test/parallel/test-inspector-network-http.js - test/sequential/test-watch-mode.mjs - ) - rm -f "${drop_tests[@]}" || die "disabling tests failed" - - out/${BUILDTYPE}/cctest || die - "${EPYTHON}" tools/test.py --mode=${BUILDTYPE,,} --flaky-tests=dontcare -J message parallel sequential || die -} - -pkg_postinst() { - if use npm; then - ewarn "remember to run: source /etc/profile if you plan to use nodejs" - ewarn " in your current shell" - fi - - if use system-icu; then - ewarn "" - ewarn "Node.js was built with USE='system-icu'." - ewarn "The JavaScript 'Temporal' API has been disabled." - ewarn "If you need full ES2026+ compliance, rebuild with USE='-system-icu'." - ewarn "" - fi -} diff --git a/net-misc/airouter/Manifest b/net-misc/airouter/Manifest index 25bb7ba00823..b8ccf5e2bfd0 100644 --- a/net-misc/airouter/Manifest +++ b/net-misc/airouter/Manifest @@ -12,3 +12,4 @@ DIST airouter-0.1.2-r9.tar.zst 87680153 BLAKE2B 66cef0000fdb9101dec8c96aaefd5a90 DIST mime_guess-2.0.5.crate 27166 BLAKE2B a8ec67e16a336e7d07c9aaa55025c24c3d4a62aae25213ce768786d9f292b76930132a880d662cd9a5d63aa899e9d488ab76fa9d99350e9f448ef484705ba4d9 SHA512 433c876e28394282cfbe14bb9543616cd296879f253ed331c15ace8a7d9f3629f5b596c2d5a64823a97ca9b774acc711b65006c46d54d1e9ee0553cbf1f13b7c DIST airouter-1.0.0_pre20261012.tar.gz 685451 BLAKE2B a07b1dd5d20ffc86d959394b74431cfc4bf3aee4d76a161e97ece035ba3460cded3cbd7df695e0b94956ae54cb4e5a9e98d47ee69277684664451a000b90d448 SHA512 c6110b5749cfc8ec7ab827303e537c0d5690ba803f022f1380de4d2fe5220590282d6d055e1de9f3809d00d1462c12d1ead276aabb09ecadb519bdaa8d005bb7 DIST airouter-1.0.0_pre20261012_p6.tar.gz 796203 BLAKE2B c9f233815c85e3b17a10ba23d43f5e856f725b1e6222a8924b8d7a90bca2a72c26bda5a80c13f56ac458d7e350cd9f4f57cf2b2898bfbdbc4290005a34ed709d SHA512 b10b9433d04cc94c1ec13cb50c51bbacb27e3e5df813df351fad6788c3746e3dd5bfec980c45efbbc22ca81c2b33101a156a9bda337e417769cae1b4faba2e7f +DIST airouter-1.0.0_pre20261022_p1.tar.gz 1190068 BLAKE2B 8df5da14210d801bb18046ce1eeb01b7c4fb9cf2275a806e922105fa192ddd7ba2c86d33f733566390661ddb113050a8e536b764246b016e3b37f35e350be5c5 SHA512 0ec3f3194d182035c0dbff883b69ce21f0edce98d89aeca7dc2110e608ceb21310285770be6c5cb28b341dc59bc14c03e7902d26b2af04ef1eb1793b4d4a706f diff --git a/net-misc/airouter/airouter-1.0.0_pre20261022_p1.ebuild b/net-misc/airouter/airouter-1.0.0_pre20261022_p1.ebuild new file mode 100644 index 000000000000..b68fda2cdd95 --- /dev/null +++ b/net-misc/airouter/airouter-1.0.0_pre20261022_p1.ebuild @@ -0,0 +1,78 @@ +EAPI=8 + +inherit cmake flag-o-matic + +DESCRIPTION="AIRouter runtime (C++; replaces the Rust 0.1.x airouterd)" +HOMEPAGE="https://trunkmasters.com/airouter" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz" + +# Mirrors the Rust net-misc/airouter; the license declaration is an owner decision. +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64" + +# The build pins exact dependency versions (M0 host pins). Compiler pins are +# enforced by CMake itself (GCC 14.4.1 or Clang 23.1.3). +DEPEND=" + =dev-libs/boost-1.91.0* + =dev-libs/openssl-3.6.5* + =dev-db/postgresql-18.6* + =dev-cpp/nlohmann_json-3.12.0* + =dev-cpp/tomlplusplus-3.4.0* + =dev-cpp/yaml-cpp-0.9.0* +" +# chronyc reports are parsed in their verified CSV shapes (ADR-05 [time]). +RDEPEND=" + ${DEPEND} + >=acct-group/airouter-1 + >=acct-user/airouter-1 + app-admin/sudo + =net-misc/chrony-4.9* + >=dev-lang/python-3.11 +" +BDEPEND=" + =dev-cpp/catch-3.15.3* + >=dev-lang/python-3.11 +" + +src_configure() { + # The project refuses in-source builds and requires an absolute build root. + export AIROUTER_CPP_BUILD_ROOT="${WORKDIR}/build" + # GCC 14 at -O2 reports -Wmismatched-new-delete (a -Werror error here) inside + # Boost.Asio's recycling awaitable-frame allocator: operator new and operator delete + # are a matched pair, but GCC sees the inlined aligned_alloc. The CMake presets + # (-O0, -O3) do not trigger it. Only this one diagnostic is disabled; -Werror stays. + append-cxxflags -Wno-mismatched-new-delete + local mycmakeargs=( -DAIROUTER_SANITIZER=none ) + cmake_src_configure +} + +src_test() { + # Unit and digest-vector tests run here. The PostgreSQL/bwrap end-to-end and the + # install-layout tests run in CI, outside the Portage sandbox. + cmake_src_test -R '^m1-(unit|digest-vectors)$' +} + +src_install() { + cmake_src_install + newinitd "${ED}"/usr/share/airouter/openrc/airouterd.initd airouterd + newconfd "${ED}"/usr/share/airouter/openrc/airouterd.confd airouterd + insinto /etc/airouter + newins packaging/airouterd.toml.example airouterd.toml.example + # /var/lib/airouter is the acct-user home (owned there); the service's + # start_pre creates its receipts, run and log directories with checkpath. +} + +pkg_postinst() { + elog "Copy /etc/airouter/airouterd.toml.example to airouterd.toml," + elog "apply the schema with airouter-migrate (as the migrator role, from" + elog "/usr/share/airouter/sql), keep execution.cgroup equal to AIROUTERD_CGROUP" + elog "in /etc/conf.d/airouterd (the service delegates it at start)," + elog "create the [history] directory on its own filesystem (for example a ZFS dataset" + elog "outside the database's snapshots and backups; the daemon refuses a shared one)," + elog "set [time] to the NTS-authenticated chronyd (its socket, uid, config, the" + elog "config's sha256sum and the addresses its NTS server name resolves to), install" + elog "/usr/share/airouter/sudoers.airouter as /etc/sudoers.d/airouterd with" + elog "those addresses (it lets the service read chronyd's reports as ntp)," + elog "then: rc-service airouterd start." +} diff --git a/net-misc/passt/Manifest b/net-misc/passt/Manifest index f121ea173dbb..9ccaafe656db 100644 --- a/net-misc/passt/Manifest +++ b/net-misc/passt/Manifest @@ -1,4 +1,3 @@ -DIST passt-2025.12.15.tar.xz 278312 BLAKE2B d681deb9107190b80e697ce813ad2c97c99c39d721e1f459f8687f5c024599bfb0b49d46b261cf8868c43a62f721638a219c236ef7d4c4debedb7728f4a389d6 SHA512 be34c6f88b7bbe1b051cd49b7577e1b9718913600061e01f9d3f623d19044707ece1c72800897dd1e07fbad21d970c2a89496ef99565f2a973a8aa5f75fb9711 -DIST passt-2026.05.26.tar.xz 295340 BLAKE2B e35fa2eaec4e2419f957447fda86327987187a741720c606ebad69b3982d82c84144c32efee143bb022719d8e62b3656d30d6078737ce8561f306aabd348b0e1 SHA512 7600fb95eb4227ef86e873c9ae45769e4c0fdeda8553b4e29edb5cd65d8a826bce875118e025b4a27c5d12eb5b82a267ac5dd1ea73e651fbb0c8c0adfecca02f DIST passt-2026.07.16.tar.xz 298216 BLAKE2B 99884c2df2425029362e12b7738e695c780b3eb351bed2a008f618325204e4776322434897d9a65ed99ad9229bfd8edaca026e573a28d708f43ac18b56400e98 SHA512 548d898484ee7d2b1835c019bef1e3fc1dc77a1debc279759da3ab666ebc111ee5790d66b1821a7671c1dbb7ab307cea258a187c6ea8878112fb1221a0bcb2dc DIST passt-2026.07.28.tar.xz 295268 BLAKE2B bb4e3cded9d7baa771138c1b6eb41b3cfe73615ad379bca9c1f598657b68c1e3d1c1e1e0d50079ec85534f6a60bbe175e48dd6630f91d97bd973e8fed5680455 SHA512 b37c401f8b83ad57bd150668c5a685477b8d186e605cb5897a50a29e02009b87f0a955fa17bf60ccb94867e325d8ba310b3328d9c31f4f4a106230c6742b3921 +DIST passt-2026.10.02.tar.xz 296380 BLAKE2B b20c0c7e01dfa042a8139dfcd729242b0c47c4b75e8b1f6dbc9f1e10527586e3b27848932f9ab5079f74ba8ad41945255bfecc692eda232b2ce98512322eb615 SHA512 b8bf17f71bd573a2eafd638c7dd11841e6d05182931b9083a62fb2802ebe5d5afca6ddc59545177370d1ac765ffc24e18e8e245d4e4a43173c36a128762d36d6 diff --git a/net-misc/passt/passt-2025.12.15.ebuild b/net-misc/passt/passt-2025.12.15.ebuild deleted file mode 100644 index a73aa0c52d41..000000000000 --- a/net-misc/passt/passt-2025.12.15.ebuild +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 1999-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -inherit toolchain-funcs - -DESCRIPTION="User-mode networking daemons for VMs and namespaces, replacement for Slirp" -HOMEPAGE="https://passt.top/" - -RELEASE_COMMIT="b40f5cd" -MY_PV=${P//./_}.${RELEASE_COMMIT} - -if [[ ${PV} == 9999* ]]; then - inherit git-r3 - EGIT_REPO_URI="https://passt.top/passt" -else - SRC_URI="https://passt.top/passt/snapshot/${MY_PV}.tar.xz -> ${P}.tar.xz" - S=${WORKDIR}/${MY_PV} - KEYWORDS="amd64 arm64 ~loong ~riscv" -fi - -LICENSE="BSD GPL-2+" -SLOT="0" -IUSE="static" - -src_prepare() { - default - tc-export CC - # Do not install doc/demo.sh - sed -i -e "/demo/d" Makefile || die -} - -src_compile() { - [[ ${PV} != 9999* ]] && export VERSION="${PV}" - export prefix="${EPREFIX}/usr" docdir="${EPREFIX}/usr/share/doc/${P}" - - emake $(usev static) -} diff --git a/net-misc/passt/passt-2026.05.26.ebuild b/net-misc/passt/passt-2026.10.02.ebuild index 23a60c5f0cce..1865091db38b 100644 --- a/net-misc/passt/passt-2026.05.26.ebuild +++ b/net-misc/passt/passt-2026.10.02.ebuild @@ -8,15 +8,14 @@ inherit toolchain-funcs DESCRIPTION="User-mode networking daemons for VMs and namespaces, replacement for Slirp" HOMEPAGE="https://passt.top/" -RELEASE_COMMIT="038c51e" -MY_PV=${P//./_}.${RELEASE_COMMIT} +RELEASE_COMMIT="cba3570" if [[ ${PV} == 9999* ]]; then inherit git-r3 EGIT_REPO_URI="https://passt.top/passt" else - SRC_URI="https://passt.top/passt/snapshot/${MY_PV}.tar.xz -> ${P}.tar.xz" - S=${WORKDIR}/${MY_PV} + SRC_URI="https://passt.top/passt/snapshot/passt-${RELEASE_COMMIT}.tar.xz -> ${P}.tar.xz" + S="${WORKDIR}/${PN}-${RELEASE_COMMIT}" KEYWORDS="~amd64 ~arm64 ~loong ~riscv" fi diff --git a/net-misc/passt/passt-9999.ebuild b/net-misc/passt/passt-9999.ebuild index fca17865c85e..1865091db38b 100644 --- a/net-misc/passt/passt-9999.ebuild +++ b/net-misc/passt/passt-9999.ebuild @@ -8,7 +8,7 @@ inherit toolchain-funcs DESCRIPTION="User-mode networking daemons for VMs and namespaces, replacement for Slirp" HOMEPAGE="https://passt.top/" -RELEASE_COMMIT="f8df3f1" +RELEASE_COMMIT="cba3570" if [[ ${PV} == 9999* ]]; then inherit git-r3 @@ -23,8 +23,6 @@ LICENSE="BSD GPL-2+" SLOT="0" IUSE="static" -PATCHES=( "${FILESDIR}/${PN}-2026.07.28-musl.patch" ) - src_prepare() { default tc-export CC diff --git a/net-misc/smbc/smbc-1.2.2-r4.ebuild b/net-misc/smbc/smbc-1.2.2-r4.ebuild index ab194c51b4ed..dda2d23746c6 100644 --- a/net-misc/smbc/smbc-1.2.2-r4.ebuild +++ b/net-misc/smbc/smbc-1.2.2-r4.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -14,7 +14,10 @@ SLOT="0" KEYWORDS="~amd64 ~ppc ~x86" IUSE="nls debug" -BDEPEND="virtual/pkgconfig" +BDEPEND=" + sys-devel/gettext + virtual/pkgconfig +" DEPEND=" dev-libs/popt net-fs/samba @@ -45,6 +48,7 @@ src_prepare() { mv configure.{in,ac} || die # for some reason some build 32bit x86 objects are bundled rm src/*.o + eautopoint --force || die # bug #944362 eautoreconf } diff --git a/net-proxy/mitmproxy-linux/Manifest b/net-proxy/mitmproxy-linux/Manifest index 81cd24c72b1d..879e2d424699 100644 --- a/net-proxy/mitmproxy-linux/Manifest +++ b/net-proxy/mitmproxy-linux/Manifest @@ -1,55 +1,42 @@ -DIST addr2line-0.25.0.crate 41982 BLAKE2B 0401c84ba99e49f1879e65f94b288f4f170f1fe9636647000461144c22f96ee8e222068318002c9d8202a21e338c41986fc008e5b2eac572e409a719c0a62591 SHA512 98c3184fb0d57968b1d27526002d476fbffa21493335d254d5d14dd975a7f579c14c8aa3cde196c63f2344093f3aaeb840222de54b0c09787c130f1b643f9c03 DIST addr2line-0.25.1.crate 43134 BLAKE2B 1538b50e123fe0a3fa0940ba4718cc86720e63f874fc7d949917720feb57e6daefa471caaee4c14e200ca385af738effd388861982c128933390b6a6227b7d2d SHA512 c400f3b6ccaff8a1c0d7d82c708a1784b8fd5ba64b6e420e85819d59b1cadb55fa59c852925093b921469f272d1d5140cf12f013ae75d1e1bc85021097df8fc2 -DIST adler2-2.0.1.crate 13366 BLAKE2B 3b28bbd7f89424567ff1bb36b984bf4fd767370459f5548545218cbe1c9f082fedd932fccc7e6acd83a1dfa06a371124801a772e5555ede7d6c2cd1ac57930a4 SHA512 555b2b7ba6f8116acccd0bcd16ed34cc78162c81023cff31a8566ffcd456c03832089fca2d5b668ceaac4fe8f922d31aa9c487f226a36cace294ff4a219bd91d -DIST boringtun-2f3c85f5c4a601018c10b464b1ca890d9504bf6e.gh.tar.gz 219297 BLAKE2B af42c888259ce9d0d36c8b6925459e86fac14059e6b8a3532783050d77627f91c4cd7a0b61bd290e86bbe37ab155d9f798f5478b0f09650dabba08cdc9f60696 SHA512 485675e3fa9317d09814a67162199691df87d04b970c4f69536607584b07f6ab8d2379ef89b22d674e569b8a874a1298bc59a8537c077c81658594bbd502470e -DIST cfg-if-1.0.1.crate 8683 BLAKE2B 7d7f98e46b98e0f6fe069fff5dede72390adb7522f13be35c37f99713641e98dff87bd58e594908565a6f42ec898f11597e8a82a01ca2e8e65b38563e09956a8 SHA512 56af0d562fe60022e8df9c5b4e9b2eed6d7ca1874c4d3bbb52148d6f3c6f7d30e0b63d4b1e652ccfc8881a663fb6f450a48447d5a8cce0c2e5a2ac12c6ac655f -DIST compiler_builtins-0.1.160.crate 242364 BLAKE2B c85996e22b19e08eb4fb0d679b0b357d60f072ed2611b332d042f6d02d02f5c8fad6bad32baa5c86d5142ddeb79ff939fbb25ed68a9f8134f4a25c6cb961387c SHA512 b8a10cf1b1b42fdc070ba7b3ea4a018c912beb1540b1aef2622302e6ad876b1d488c8ef08b90d2fcab4d32d9c836c0fa552c52c1fe96afc2ad0e1be95c3ef6f6 -DIST dlmalloc-0.2.10.crate 86645 BLAKE2B e0b0369c83a5853ccc361439c33cce90b0448cfc00550d76bf49fd29095ad4528b65c513c77c2208ab09684c24721c4abf5f0dce378605da8361dc3e3f2cdc9e SHA512 5e04bfae17685a5b55f2d2550a790e73c5b377cde82c113275cf80e60c258746cf8f236942bc0717c96fd753332a7cb051ff773b7af6cc410061200b88857a81 +DIST addr2line-0.27.1.crate 43584 BLAKE2B 6fff579e446d40e1e5244cef6bc5ae39353938fa6d18dd0bd829c6aacf070efa6e1e6ccb99be334ff722ec1504d3f898963076bca8e7f4dae7f00d2efdceb473 SHA512 0a5c5495da259972662a70028786af8ecaae2ab1d996757530fe8f6d5e72e609b802fb1961bad933a62b49cbffaa3de4831f28e1dcdefcf82e035d4f8cd59798 DIST dlmalloc-0.2.11.crate 86742 BLAKE2B 78c25b9fc71c2827d81325f454060c533c113ef7d60c4d9ee7676c088ebd2814c6d13a9afc0f33844265a0f66e783da5bad77d129acd2768ca491642d5bb0ef5 SHA512 d53ccf5558782cd8f83a9e81576918c824f7f24be9b57edc60e7d865388d2766c93a4e9901dce2d63423f351bf63adb7e6c61661b27470b608997bee0a58b118 DIST dlmalloc-0.2.13.crate 27636 BLAKE2B 98827337808476d78259cf7a17870a2cf7ad4a84e25a8fe00a27f7e2778ba2e62b6e9f038e4d7a6cbee76a6bc0801ab904ee8966c33c959b8d3ececec210dafe SHA512 b62571ded7e5fb0a62e4da7f419b78167addbbdcd8e4fad7e43968394d8030b932c9f23dc6052b106abf9cdd3ef5858c43300235d734989301250743e8e501ae -DIST dlmalloc-0.2.9.crate 85868 BLAKE2B c9b3f2b2ad3d43d4559ef0c390de5eb77c2e83c08bfb5a1a050a49d0e6f45391f4597efec8150267d346b09aa26e4b9f244b5d09333ec4154a386daef3f2c413 SHA512 5a9397b0d65f1f10d8e3e7bd84266129dd6a4e2a7c2822e84249121bb0badff15fb530242984efaa78744d919f49658d3c425d9f89dc2741b139eafa71b14ade +DIST dlmalloc-0.2.14.crate 33285 BLAKE2B 605adfb88a4208eded6c75f66b85a8e2481f6b2caf700e51dbaf0efd79b7ff4393ab973513c9ae1ddb1f474c405fd230fa93fd6723d50ab2c6f52d71de0a8d3d SHA512 48014190fd00ce900138baac576002fef1976acb2d5c58016c27280e709d29a359bff4bf13a309cb2509034e30ae437b86c1c7b16c3e5b8d63acbf00f28a4f66 DIST foldhash-0.2.0.crate 23329 BLAKE2B 49af83a79ca7954f05d8a52566d7d20cbe057c89c2c767ef76b4c8d83bf099196d053054c8fbaac74da7fd5eb0224fe43731102f2f0bcb18ccdbc0c8150774c3 SHA512 1495281089a8a1b7bbacf8004a5e8051bb00bf37718e9ddce9d43e4e198f529135f864b6850ee4422456b0d4731ae58647f0a1aaae0d67921c389f5d108fc163 -DIST fortanix-sgx-abi-0.5.0.crate 13334 BLAKE2B ccdfdd2e7a0592b0efcd4269695b7367e1e6fd2da7aac3b6e2b174375e787ec00134c3156286656155839367c031aabb748e50c15aa539ab3b3da22de3fef132 SHA512 4239bd0fe3a2c9e0b4a0e0eb82bacb417e1f798fb4d908bb5cc021ddfe6e41688f8ee16f9a18cda692ef78afb264a440af7444cfde43a0c29d0299781ea43ccb DIST fortanix-sgx-abi-0.6.1.crate 13691 BLAKE2B e97992a0e93a290bef2adc7115a158385c0c8b59f50744a2a6c7a3b73d164c3dc7b6278118062c64cac98234d9ffffdb94365e79bb489ead5f48c222d87c8771 SHA512 422cee56dfc9066c2c9de032ba92ea5b9cd572193a10b94ce2a9886fb0875375497e7c46b4bc3684d14293b4f82733b519663c807bf73ded62e9c5b1cdd08b64 -DIST getopts-0.2.23.crate 21409 BLAKE2B fbe2c229f7aff2bdaac17ff5c670363c135e69651cb924af1950d79c7eace1616a30425a181cd0116004ca8167bcb1364e208d92ad16a2280f1e77798d680657 SHA512 8d4c08e88a8532d107052c34305b6381e8759958a12eafe8791816fbed85e405fe901aa11c8a8e0110a6d44f87cd1f55a332d2c76c7c12d13a86e00731842e43 DIST getopts-0.2.24.crate 21467 BLAKE2B e5468cb141bc8be43ec84606c873fa6e16a49e902e247f7c38a08c6e8088d36964d99b41ae3a98a5350cd8fd15ee8d46762b818c79d1198c0aed576de191b640 SHA512 3b0fd721340f732d3ed3dee38c61a46d83c920d24d96fdc780927c2a02fb9a6910eca4caa57db83d500d76bfb0e7f424a166204fcc4c5a4d87ca3ed30008f5aa -DIST gimli-0.32.0.crate 286715 BLAKE2B 761fa6ceaf967f03b235e07f05d596ea0535cbb38678f6c543f8ca682058450e29e82ffaf3960689b945646db0d2b7afe503ac4d6bdaa52c3ad4de7315295d73 SHA512 9ba4a781c98507fb5a4cd3a8a0fccc03cae5a78eb7e67d001f02396e157b340d6aabf96b7ce54e89bbc00ab61921d3434c355aa0b0b7b968056db2f526392c96 DIST gimli-0.32.3.crate 289789 BLAKE2B d2794b527c4e0fc8ba10d12711deba8606bbbbe3625227d9b886b3d6846760d9dbc7ec258fbc13673017bb0706a9cdcd573b760d21857fc55deaec7b4be30994 SHA512 e3ec768bcec1e4232b46d7c0a63470d5d966098017d344001ff600587df9ff8419b133b2037fd0869946414718da0da9285b1255468dd5375e46bed3d3a95503 -DIST hashbrown-0.15.4.crate 140447 BLAKE2B f0b1aeaa46dc1354a9558d405bd1cbf72723e8a61a3322743f8bd6d2c17b336b9eedad27756799a054633797373b56b2b957d5ad923b19751074f0294f63d0b2 SHA512 bc96bfe26897240a370fe221515b5ad1dbd07c11de0cbfc1f7721ffbd97546b43a82a88461b3390a6edce9faeb156ca369ed95eecb29fa82c35726a325c26b52 +DIST gimli-0.34.0.crate 335386 BLAKE2B a9e8deef05c4150409d6d4328f1de3b898f6624d21474e99ea32635211ead3617c68b849a6d1a39d2a6b8ba8dca0243bb82658c4f028525d1fc2d0cb35b87baa SHA512 f2de1b87b77ac60cc6e96b56c1e6892dacc6d78817d5cb1bd3d748f900d8ecc01e63947efcf31fd8838ba062ba03dee59be38a786f9e8a8e9b2d29c7a38281d0 DIST hashbrown-0.16.1.crate 147785 BLAKE2B c506e64340b65fb462545c52e85a192b21e33025c91adf1d8255e799e8a5743814c60be971db56c0b9b1208dff8147ed4a28d8e37caf8f5646fd367d8d0bc9d7 SHA512 0739f1b2101488f88778e221d86738ce557b8ae12470e2a4444099eb90d876382030d00e789dd4ee44dcd7833a87a6e570fa11ba5823beb1cab3a56784ec2669 DIST hermit-abi-0.5.2.crate 17435 BLAKE2B 695192149342273199ffb026de57e2d906249757b9b4bbceb6b40d3b2298a942a75390aeaa3ad4cad71e5d44687379dfc49598bacdb2cf989ab48864c4b2b517 SHA512 0392a8350288e5d500967bee8e98626024b47a7da331f833b9528bd7318cf43f41eefa46aa6aebd19febfadcbcb2d7c9f70d6ca3631c1618d63c60366dbe7c15 -DIST libc-0.2.174.crate 779933 BLAKE2B 20ffcbbcad6961e732521940f0595039dc0576d85b2d3b2ad3f1decd37571132a4cd7505b2e108078a4379fc4cec46290b5633bcbcc6e7009c3b474c1a852628 SHA512 243f2ffd5741ef859e4dc5b7ee0396071943b1d10429efcaecb322957e256c4be88a7a649029dfd47e6456acc73ee257893fb6c4e8b4d0fe66a12bc5d1c13c73 DIST libc-0.2.178.crate 783720 BLAKE2B 72f5fecacf0494b3962ff893d647155af88e9942b14d110e521b25e5d65d95d41dc00f83431b82632a53c6e0ae7fd537d018d7bf29df65d5e56b908a893d77d0 SHA512 968b954e2edde88d14135decd9eaadc31ca1aad80529a6696591c06c4414d615c97ce0873f77bfebe08b862faa5f3cb79a6648ab90734d462bef06e06ea43dde DIST libc-0.2.183.crate 816918 BLAKE2B d0c26ca25b0808575aa540a5f0f43acaaa5a972269c5f101122538a1733b37b6aac3696a0d7298fe16974b06efb0b92072252eef40b17b977af24cc857963daf SHA512 017986d4fa8c362f8900c1c82c7381066382a7272cfe114243b8d47637d042c87b5ebef7881cf46727e00e541a0a8270a32db694ce8d78372672cf08224a90b2 DIST libc-0.2.185.crate 821775 BLAKE2B e0e93466a29c9d19f7d9ab3581b491908d06873a9c57a96b0cae2eed0f0d1cb184ebe80bc61e36d5ab316ab35dca529a322b29e6895f7f339e72c79cca7f8ca2 SHA512 c8dbcaaf2e5d8377a4d1003af4a7210fbfe4f9b1e8497f07d88a231a8d5c9f67014e650a5e18e913e7b4d6a1835c4cbc9c31ce0cd224ea28acbdbb39034a300e -DIST memchr-2.7.5.crate 97603 BLAKE2B ee41ef722d90265064db3ffe22d52eff8f7627ac1ae9b0bad6ca9fa06c1027dca8d67e594e03b03c944dbf85930d2dbf030c5aaed369020ca1a1ba00f153d91a SHA512 2606ee16a7dc02db95dac17cebd2cac9deb2e23d7ea62972cdb19145c06eaf75631347a49c4e6ceeff89fb6e90e992f9bca0a9c955eb5130f89ba2bd865936e1 DIST memchr-2.7.6.crate 97616 BLAKE2B 3429786e97f1aa078892e417c8ed8198ec727969517309049b842e44097e58038583508aa974e6246888c18e11f9d23863c1bb012b542c5685250fe34e250d7b SHA512 38843817334da88ad74ce679b4401cf948cf8138815f3acc5eb0d005da3aabceb2216e20887e79344396569a2fa136e3aa1f7f3251a00d07dd6934cee79f4fad -DIST miniz_oxide-0.8.9.crate 67132 BLAKE2B 1a11c5aeef43ac93635942e0c4f17538150f8307fb280b0d66436e4b6697638f0b1bafbdebb377a5dba14a75f213024201e325747419623e5f7d57060bba4445 SHA512 3f6fd786c5fa5b4abafaf21244dd4830d26688d5b6975522bda595206517f354d92d13fb95235667cbc39c0275371c737164d9c7a58e32e09da86eed2f239242 -DIST mitmproxy_linux-0.12.10.tar.gz 1297842 BLAKE2B 5de458088ff7e3026e491735bc0a556cc4e272a3b63a10ba74b95f624238e0664cdb8081daa093ee21a745b81fc275f3e6deb5278ca35189cde1dae4cc48b324 SHA512 230cf9a6712cce6ed9e6af26cf3961622a168617958bcbab1d2dae414f96b1c4d4da52960cdb197d44aec948baed3d9f4f2a065a8565befbc3c7f0e27564d08e +DIST miniz_oxide-0.9.1.crate 70519 BLAKE2B 4ae01532c37a6b745bc7d8a7ad5e61bf83a389f98244ac8d6d20f44b1ef157fef7ade9b8ff2609ef69db4dd94cd31589cd5c59d04ca6c682a2be0e24af15c214 SHA512 6f3688acb94d702264f9a244dce31a6e0ed6ffa4ad198aedc04ce6da3a573df96de51c64f1d13ee0af59a7102450c420710eca99d1be8a15cdd2ed5a66819864 DIST mitmproxy_linux-0.12.11.tar.gz 1297950 BLAKE2B 09bd7d12103306a1e8db28cf1ce99572b1887984383cf33181452f8fe8e951bf8cb80f12cb928614ff10f546049696c3486066058c5abd5254926c8a361bee88 SHA512 2e658fa7d3ae52334f87906f003a6ebaee88071416a776f8fa2fe5eaf4907302525ab0cdca38e975875e81188ff77b4b44b2b8e868881e53f760f7cd76ced6d6 -DIST mitmproxy_linux-0.12.9.tar.gz 1299779 BLAKE2B 2fe79a859c8e15c21dc7b554dd0087c4f6d268f8fc7c2f4f9790fd6ef2dacae8126a55c666ab04477fb7e9ef1b62a2d814c1ee8ced427b8be89f3b1a59c1b4d2 SHA512 dafe7d2d92088ed05aacb505d22732e6d62d852464225cec712df2d2a9edf20a4cc53ad547e1f4a602396ca9d11b29128f38b7b076bedb905a86cace1e9fb127 -DIST mitmproxy_rs-0.12.10-crates.tar.xz 41559496 BLAKE2B b84fc9a618b8abe8b79b1757d66872ce3dfe44048c200fd217ecd234abe6114af417987360d4fcdf691a35a19164116690343be537b8dd8936a2cb57253871fc SHA512 df72556e9c94fbb3a48d3f3bc31a65a3ad5767d94294004111df4428da610fb26f53b06f77a0ba9a51a9a3e99ddbd0aae5609cf42cb44ff125f0761266d3d803 +DIST mitmproxy_linux-0.12.15.tar.gz 1298806 BLAKE2B 14225c1f936369d09ed95082862a2184f6bb29e845e94da7d73a62d51355b0cc3955108904796292f593f6966cb4ee7a52e5b3353dba1fa9222e0db9dde89de8 SHA512 bce700514831e47ed2e8a7b34c63ad0e172e028597b521fadd283ccd8c3a771131c4d086e4ae7373becabedbf676fba5daae3da30d4a36acb21ea71e4340c800 DIST mitmproxy_rs-0.12.11-crates.tar.xz 42414204 BLAKE2B 26a76a54c9e4d9ae4bee50f2de0f1802ce3f0b8869a0d3738b95c6e26697173e47bd6f6fe0b81671a0f433060b682d9fef24fab3e56b0f17439cb38ebc5d15a1 SHA512 c629a2c21de7f818b333f18574975ee91cdf38d7f40de130153af45718bedbdca0417ccd6d263508d4d362f3025e5a9dc52f396930c8ba990b95438c71ef5aa1 -DIST mitmproxy_rs-0.12.9-crates.tar.xz 45242028 BLAKE2B b34979c3a573701a2f6f2f029a1cec05d4bc59013dc94c87a0e2b7d340c6bc8f7b2415d171b944f63a7d881ae87941a1e6fa61265218a3eea46fb7a127ef286d SHA512 ed4d48dd443eb777e5b387d38bf36ec6ee1d51e1c0aa650f0b27f8e34ae13a5c1eabfa766565879a661b2c4d6e458800e668f3a80a693eb9d5527ee0eb52314c +DIST mitmproxy_rs-0.12.15-crates.tar.xz 42546604 BLAKE2B 2f4406433c20df9af2deddfec6406c918daf95b00c32669c799dbf246308947a2497d6c5d29b91810c0652834db1ca97fa3a07591072576577de91507d7182a5 SHA512 bd8fa43d960c261cc944a70aa470f2c007513a7aa8cf872240f685b1c9d64afb8e059d1739a3f0535697b9f370d5e3c37b2a9c5371eea9cd565c8be9761da1f8 DIST moto-rt-0.16.0.crate 25412 BLAKE2B 6b18ae96975a037b1e1cee217a0c01df8d03fbb7defb633f4846d3ee5f9f6625ff5ac3dc9e33fdb02880d4f2dd3ee5e2cec9ccad6e3b51c93280abce5b99a148 SHA512 a46283badf03aae0b9bfc01699c9a09e7a8ebc1f2d88393df9da6eb133173f09e5640043fd015e6915b1abbb339939e7f37f427f36b4d8fc4c656cf20436cd78 -DIST object-0.37.1.crate 338929 BLAKE2B b2bc4f27968ec308b8d569e71de449adf5cf545aa2054d65cda03cfa44e3d3b108bd39e1288e1c983d08656b4c50a384c2101f2bf049b7d67d42e60c95160cba SHA512 ca9c7857185b6fb7f81a594d73df10cace40b47d7475f8d4ac0c6be9c3f327dc98a86fd4fc8f70da762eb978a0427ecff1e3c181bd3efeefbf3d2f51615b9745 +DIST moto-rt-0.16.4.crate 26411 BLAKE2B f1a1558db4a34d5f312b15b719e61acbee775b59ff583649897b7856f8bc65bd4ac7b698a363f87049c788419365b3645baeb9f3fa5660193ac6cb1d9db20be7 SHA512 cae7d7bd6c86700f1f46693ace0f04d4cd895c854be7dd4578aa6dcf4c4aadaddbec977d7393594ee4aa4f59635d7978ba98e2753c44313ffa3c87b72ee06f8c DIST object-0.37.3.crate 344032 BLAKE2B 7cef4fb0adfe2c0ce56705404b1e1b2b22afbd741cdc16bb5a2c76c9c2df03eca801b0cbd9077a4ad153b97c37d20637608018f532918e82d1f27c75f603dbac SHA512 e6a72ff6ed5066e0b6fd4b5c37dc0c3d334052f3d123fa0aea2dfa45c8ba5700fd6a66f4f76efbd8823f0935c6d1bf9cb3aadcb8eec1370b0126511606038aa8 DIST r-efi-5.3.0.crate 64532 BLAKE2B 54bc882e333f7ba52b6dd6ac92b8a282581c10b47de91a4c4b71b15a27b2df885ab52ae2fcc8a546367ee1ce98300dcaec3c33cbd7393b134383528540bb7d14 SHA512 e748db9729dbddf215d5c37630fa272197dd2921297800209ca7bfcd6ffd8530b598b944d95547ad9723655cafdfb69b1c1fade9ba5f8c2ac5c55ec38258fdad DIST r-efi-alloc-2.1.0.crate 14475 BLAKE2B b316af45913ed4fe3b3efd01f800aaafcad9d1d0d05a8ed3685a85246f458cf65f4da50229322ce059a9eea0d341af41b765ef321be31161ec07c3768d6f88ed SHA512 9213827d81f2809341b9ec101bb6d94dc5d4a0e8b87252a34bf21dc200dcc13d7b20013c7ec87ad0e9c24384f4b4f31b37f4bc2126bb8479db7bb1118ab7acef -DIST rustc-demangle-0.1.25.crate 29590 BLAKE2B affc3a85edfae0bd4a3a0165a9fa8d0d8c5be7958b70f98769d24eb6fe44c33bb05c33fc312bf35c887ce927c2f8feeb287b50f4f230d8dd74109be8faa34999 SHA512 d130a885b62470f4429dd5a9b11cb81e045c42e093125c2fed1eb79d07dd0990cd38e049a314babd1fd8b8ffab0bbcb0453fc7aae3e3adb7006e943d416162a8 DIST rustc-demangle-0.1.27.crate 30448 BLAKE2B c301ebc7d127c679d95dc77798f738263aed592a9d08cc6a5e3f53a4b48fcdb8ddc824550c35c6274093315d71dd720588e1bb7d6145d1e3975dcdef72c45932 SHA512 da827b28e0be62af85b4b5dc15de06d78058bdf19888ebc7c55a46b533d81e2d8ba922bf032bcfa5334c6c43dd5bb272ae9510ffcf9ca2711a284bfbe245fcc4 -DIST rustc-literal-escaper-0.0.2.crate 11073 BLAKE2B fe7423ab43605fabf26788a98629a1dc37da5ded1339ed13d63e00f7e4d6167284e82da42c439db3d09d580e8fd1a5c12b8192bb9a676de09f89f9ccd9912525 SHA512 01b3a3b918133bedd6ce02a182a1eeb1459d33cb538808951eb64dcdc3ced542768cfe76a226b825f827d8506d9bb967c562df5697c9b48f471494710c676db3 -DIST rustc-literal-escaper-0.0.5.crate 14220 BLAKE2B f871e30a7015cc0e498dfbd75d322d4263ca3a986cea898d86ea50c696421e8922d112719631d429bf51d19bec2fa3f1b7a2aa8dbff0f011a4e71d81db511371 SHA512 a14118bdf2c5024b48a2ee2c06b371e94d951eff50db37b8727d25dd8ac37249c7bdf6c7d450873e5810778e28d0da950e8c630ffe81d01b46860db94164fac4 +DIST rustc-demangle-0.1.28.crate 30857 BLAKE2B 31b6d585541db03aca743eca652bdc082de8f9c8231e2304b7c4fd5c902aa6084e5812bd229a859e871b2794d542a87e2129f37fd84edebd642cbf8e2fa84b54 SHA512 dba9b342306c4b1115497a058e605c291476a5ac6745a2ddd1704cc9eec0c5175fcd0c0c937595c043e4e77f70977b014cd5bbd25f9c446f5600e83035631e91 DIST rustc-literal-escaper-0.0.7.crate 14206 BLAKE2B 35fed836884a7cfe1aea188626b9f9ec1e58924a2a37a39f455567c4df06313c516abed00d4ec6cc6dc1a1b2d8a30bda9606579621a16cda17201392e0a4bf1c SHA512 c259bb0c807db7ada27c2b7919d015ae8672d0f92a281fff733dba9e65b4a045bd65885df59a4ee6779778786426e08c1e856fe8286e5ffe886a15f5e8a4c37c -DIST unicode-width-0.2.1.crate 279344 BLAKE2B d4128c165016a76196e0eda9cc4a0edccf326cc3c7ecf0e1b43848446e758c703e9a9e843dbd256a523e1654ae3faaf6c172e21b0b6324757610fc5333c56e8e SHA512 705ce2ca2964761d9fcf014ef5ad9af2973b5f7c0c3561fece10378d817135afc5f24b9ac16290b98edecab77a01ddee9ec8dca5bd2b4eaec1f3124065ab8708 -DIST unwinding-0.2.7.crate 28125 BLAKE2B 5feb8262e1e7297311678525c7ba275fa2892d9cc05a2283fcfce424eb1d565be929e306d9c4ec9234771316f943578e2621abb4489b0ff40c85588f3c448f24 SHA512 a9d9988295fe5ecd0a06967714a41e3488aac4699748660baa91fb05cd34b34871ad37d38533d3e0baa64fb1ab9ba592edcf5fcb4441994c76de18b125c6e57e +DIST rustc-literal-escaper-0.0.8.crate 14104 BLAKE2B 670f82d3d62ff7c5492145af230b798849ddc1e68a81ecdc11981bc934f3ea020f61bdecb0ec6ff5c0c6e882a3c933c18ce57ae0cfcc201ed8f42ee8c0411220 SHA512 fcaf927c34d05cf706678c8b021f349f14daab1c00621dbf3d8ceab2790a148d8e760db317685bd32e883b61590fad96ab33681481f91086f86a2dbee40572f8 +DIST unwinding-0.2.10.crate 29566 BLAKE2B 4f9807a5739fa234c8ab1805cdc763305d6fa3e6f97debdc562c80008ff5f36e228a19e8d6a63c17534b6c96f0b75fb27aa3b3b3943801bcbfadd5dd2df54fa4 SHA512 97c1b10ed42d4b16f670fe03104c1c1bb94e99f362b568bdb6a618e776dd72c562de14a84b54042c3550fa819750196edccc2e1d6f84c830b8645394fe83a6a7 DIST unwinding-0.2.8.crate 27968 BLAKE2B 50a0730e27e060276798c8a18bae204e8c406d9ebdfea1d51bfa90fefd73e30bf1f84ad3c1fb7cf7085ac43f365813a9385f7d8cb569c97cd9cc1b549ed48b12 SHA512 38e39047febadfb04677fc46179125740bce28d2bd60adb9384b9f4cfe44719e151f03844f8620a9a18c994889ff30e6a27b78151422a3f348588a7770e7110b DIST vex-sdk-0.27.1.crate 11762 BLAKE2B c00330dcae5207197dfba07c0713210652140c9a36baadbc76efab3d4287023b79cf1763f7226e96e6fbd0b2c7531b7b6ad3c00ee90ea5829b1c19d521e6aa67 SHA512 f8a9925aac64164f50b0b57a911f41266073323c8bb6697f622263d4bad5fa81348162895be9374c062b1121dc4f0b4217340fb17cb94895ad01e33d744ca51b -DIST wasi-0.11.1+wasi-snapshot-preview1.crate 28477 BLAKE2B bcc773ba28cbe8d820e9e9fbb275fb4451bc7f39b3bd88bcd93850bf2bdb5300489bd8eca27489ccc5f6269b00f4e1b505ae3a33a35d708cd5a414ad307be149 SHA512 29e3c903763db2580a15f50db487c5e1d4e90763e48d90a5b3f681e468a24f9df1a01773bd1ffa0e3cd293dd8cb4d6fbabfaa63c0e13889c811a56f4554ada49 DIST wasi-0.14.4+wasi-0.2.4.crate 145408 BLAKE2B 800151a792b2dc1b4676494e2c78b205e09ebe3ac5e7a960bce2797e1a1c09db9d0897413e45b7d3cb30e80e1cd61dbb53537eeb4f37761cc695eaa5d1365eba SHA512 77c1e1eb7a59ad3a0c9624db557f6e1a8aa21b70d2dd225e4f5eed20ecc231ac2fe62aa6f3c1a3435f59e3afcbd49c3913be4082cdf0ebd20bc6bcd7826b6eda DIST wasip1-1.0.0.crate 26437 BLAKE2B 0c6e0422b5b265c5d39ce8a2bef08d27b5da5b13eb891082bb43ba84ab8960dab8fb3e05946e5a36eb070d65e087d0fa70fd099ed2121791386b10655fba14cd SHA512 80e84db4c1c9f7d75ae04e9aa2f75cee3a7fb637d9531631bf48378a4c94150088a0b05079cafd7f3d86777fc8249541ae3d903afa6fc53c281acb389f0ad6be DIST wasip2-1.0.3+wasi-0.2.9.crate 134512 BLAKE2B f60f74c4132db0605658ec48ee0087992b82e8969d890be15d9b2610aa235f43dfa266863b3f86e323ded9bf17a585beae232defc087ffc01ad175169ff6e818 SHA512 74c4baa90ecb4c1a87a931e03dc0ed7b472fadaa837d286e28c1c948a08bd2af88ed6f4bcf2900bdc85bddb358026d676aaf200899ec395cb98883e2d1a5ae2c +DIST wasip2-1.0.4+wasi-0.2.12.crate 135311 BLAKE2B d016ad82ee36ba316bda8e13fab53007b812372d1baf8718c39013a6e46ba4be123b31ff117ca89d1f5152b537dff13dde37887542ee37fc5a6df1dece020a6e SHA512 42c79d515531dd74efcca7fc3518ec57a34d5a4c73cf36f462fa1fc87151dd22d227021667e7743aae587962806b96840fecda029e5469c69047a494b13e290b DIST wasip3-0.6.0+wasi-0.3.0-rc-2026-03-15.crate 141033 BLAKE2B d6db30e2b7c189857294800a5bc293d6dc2b51de85272480900f025d98e5daceba0c54e37d4c75e6e22d1808f2f489b12d348dc140ced0474fc8254294c78f3d SHA512 544708dbfea321077bcac33d974f45bce0151a4acfce18a5e72aea57654f36729bf32d74ecd4a1ac6fa1cf87c3221bb384be7bd736bcf27262cb294b06c5e8de +DIST wasip3-0.7.0+wasi-0.3.0.crate 140573 BLAKE2B 9e30d8de8b2d2b323db136778c4fd7c729f8b56b016d7a2a4c793aec9e2d9598cca53ba4b20d3bc9987144a74d1431f21f4504f27212e0f5556ddafaf33d63f4 SHA512 76aa66f908ee9dad630b689538427a357baad664d0fd07cb0da7cc4955452d6a04c97ad9cdedb17a37380d080b2810cc57a671260ab4d40c34cd713dd4f7d4f5 DIST windows-sys-0.60.2.crate 2518479 BLAKE2B c2e2f614066e5a9d7ec461d9d25bd441188797ad85e1e923baeb0193e82f5c138ae7fb4ad0255d483f60f7a94e3f5cd8373d01b7485c052c9498937bc40da992 SHA512 a48aa1b66479451e431032737b63dc62eac6f63c251534e258de75bbee4858829faf5e641756566f00e153b08548ba32beb437b5ea42e9531e3fcbd8eb25209d DIST windows-targets-0.53.5.crate 7126 BLAKE2B 17a0452fe72a42df4aec8b4ae7798bd611df4f3b093709aee7b8dc991fbc2351edd23e134d12412fb47e9930ae30476b55841d52be5e764fc42a7922e3134ad3 SHA512 285a2ad1c370d8485a84c3921b180416c79ad596ace5ce30a1100fe0d9725139e9302f5728d4874789d8abe5c53e9b30ff4738d969da1c07940bde2b01c7a203 DIST windows_aarch64_gnullvm-0.53.1.crate 787748 BLAKE2B 78729e1107cf64ffc6a3d70c14cc93e7867578be51d3f6f442914a5f2d065fa86be843b72cd24d707eb59b779e9a719eb28d025619cdc219e379827acb4a16c2 SHA512 5e480e54af09c142ff75e77f733327dd3862f32620e0b075e02bf8119e955ef2f3acfa00f7c7a4d6a2b034d03d70c45e191a40c8af628484d0e76a0b15870cdc diff --git a/net-proxy/mitmproxy-linux/mitmproxy-linux-0.12.10.ebuild b/net-proxy/mitmproxy-linux/mitmproxy-linux-0.12.15.ebuild index 24affee65cc1..b899ab81e286 100644 --- a/net-proxy/mitmproxy-linux/mitmproxy-linux-0.12.10.ebuild +++ b/net-proxy/mitmproxy-linux/mitmproxy-linux-0.12.15.ebuild @@ -3,29 +3,47 @@ EAPI=8 +CRATES=" +" + # Extra crates required at runtime via rustc-build-sysroot crate. # Note: versions are locked in /usr/lib/rust/*/lib/rustlib/src/rust/library/Cargo.lock. -CRATES=" +CRATES+=" addr2line@0.25.1 + addr2line@0.27.1 dlmalloc@0.2.11 + dlmalloc@0.2.13 + dlmalloc@0.2.14 foldhash@0.2.0 fortanix-sgx-abi@0.6.1 getopts@0.2.24 gimli@0.32.3 + gimli@0.34.0 hashbrown@0.16.1 hermit-abi@0.5.2 libc@0.2.178 libc@0.2.183 + libc@0.2.185 memchr@2.7.6 + miniz_oxide@0.9.1 moto-rt@0.16.0 + moto-rt@0.16.4 object@0.37.3 r-efi-alloc@2.1.0 r-efi@5.3.0 rustc-demangle@0.1.27 + rustc-demangle@0.1.28 rustc-literal-escaper@0.0.7 + rustc-literal-escaper@0.0.8 unwinding@0.2.8 + unwinding@0.2.10 vex-sdk@0.27.1 wasi@0.14.4+wasi-0.2.4 + wasip1@1.0.0 + wasip2@1.0.3+wasi-0.2.9 + wasip2@1.0.4+wasi-0.2.12 + wasip3@0.6.0+wasi-0.3.0-rc-2026-03-15 + wasip3@0.7.0+wasi-0.3.0 windows-sys@0.60.2 windows-targets@0.53.5 windows_aarch64_gnullvm@0.53.1 @@ -36,16 +54,13 @@ CRATES=" windows_x86_64_gnu@0.53.1 windows_x86_64_gnullvm@0.53.1 windows_x86_64_msvc@0.53.1 + wit-bindgen@0.57.1 wit-bindgen@0.45.1 " # Implied by crates above. RUST_MIN_VER=1.95.0 -RUST_MAX_VER=1.96.1 - -declare -A GIT_CRATES=( - [boringtun]='https://github.com/cloudflare/boringtun;2f3c85f5c4a601018c10b464b1ca890d9504bf6e;boringtun-%commit%/boringtun' -) +RUST_MAX_VER=1.99.0 DISTUTILS_EXT=1 DISTUTILS_USE_PEP517=maturin @@ -88,10 +103,6 @@ src_prepare() { distutils-r1_src_prepare - # replace upstream crate substitution with our crate substitution, sigh - local bor_dep=$(grep ^boringtun "${ECARGO_HOME}"/config.toml || die) - sed -i -e "/boringtun/s;^.*$;${bor_dep};" Cargo.toml || die - # this only implicitly builds mitmproxy-linux-ebpf, and is broken # https://github.com/mitmproxy/mitmproxy/issues/7663 rm mitmproxy-linux/build.rs || die diff --git a/net-proxy/mitmproxy-linux/mitmproxy-linux-0.12.9.ebuild b/net-proxy/mitmproxy-linux/mitmproxy-linux-0.12.9.ebuild deleted file mode 100644 index 1795c536f9a8..000000000000 --- a/net-proxy/mitmproxy-linux/mitmproxy-linux-0.12.9.ebuild +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright 2024-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -# Extra crates required at runtime via rustc-build-sysroot crate. -# Note: versions are locked in /usr/lib/rust/*/lib/rustlib/src/rust/library/Cargo.lock. -CRATES=" - addr2line@0.25.0 - adler2@2.0.1 - cfg-if@1.0.1 - compiler_builtins@0.1.160 - dlmalloc@0.2.9 - dlmalloc@0.2.10 - fortanix-sgx-abi@0.5.0 - fortanix-sgx-abi@0.6.1 - getopts@0.2.23 - gimli@0.32.0 - hashbrown@0.15.4 - hermit-abi@0.5.2 - libc@0.2.174 - memchr@2.7.5 - miniz_oxide@0.8.9 - object@0.37.1 - r-efi-alloc@2.1.0 - r-efi@5.3.0 - rustc-demangle@0.1.25 - rustc-literal-escaper@0.0.2 - rustc-literal-escaper@0.0.5 - unwinding@0.2.7 - unicode-width@0.2.1 - wasi@0.11.1+wasi-snapshot-preview1 -" - -# Implied by crates above. -RUST_MIN_VER=1.89.0 -RUST_MAX_VER=1.90.0 - -declare -A GIT_CRATES=( - [boringtun]='https://github.com/cloudflare/boringtun;2f3c85f5c4a601018c10b464b1ca890d9504bf6e;boringtun-%commit%/boringtun' -) - -DISTUTILS_EXT=1 -DISTUTILS_USE_PEP517=maturin -PYTHON_COMPAT=( python3_{13..14} ) -RUST_REQ_USE="llvm_targets_BPF(+),rust-src" - -inherit cargo distutils-r1 pypi - -DESCRIPTION="mitmproxy's Rust bits" -HOMEPAGE=" - https://github.com/mitmproxy/mitmproxy_rs/ - https://pypi.org/project/mitmproxy-rs/ -" -SRC_URI+=" - ${CARGO_CRATE_URIS} - https://github.com/gentoo-crate-dist/mitmproxy_rs/releases/download/v${PV}/mitmproxy_rs-${PV}-crates.tar.xz -" - -LICENSE="MIT" -# Dependent crate licenses -LICENSE+=" - 0BSD Apache-2.0 Apache-2.0-with-LLVM-exceptions BSD-2 BSD ISC - LGPL-3+ MIT Unicode-3.0 WTFPL-2 ZLIB -" -SLOT="0" -KEYWORDS="amd64 ~arm64" - -BDEPEND=" - dev-util/bpf-linker -" - -src_prepare() { - local PATCHES=( - # aya-ebpf overwrites libc functions with incompatible - # implementations that break everything, and therefore - # can only be used for bpf* targets - # https://github.com/aya-rs/aya/issues/1254 - "${FILESDIR}/${PN}-0.11.5-no-host-aya-ebpf.patch" - ) - - distutils-r1_src_prepare - - # replace upstream crate substitution with our crate substitution, sigh - local bor_dep=$(grep ^boringtun "${ECARGO_HOME}"/config.toml || die) - sed -i -e "/boringtun/s;^.*$;${bor_dep};" Cargo.toml || die - - # this only implicitly builds mitmproxy-linux-ebpf, and is broken - # https://github.com/mitmproxy/mitmproxy/issues/7663 - rm mitmproxy-linux/build.rs || die -} - -src_configure() { - # first, we must build mitmproxy-linux-ebpf for the bpf target - # bpf-linker requires BPF target that is only available in nightly - local -x RUSTC_BOOTSTRAP=1 - # no clue why this needs to be explicit - local -x RUSTFLAGS="-C linker=bpf-linker" - - local cmd=( - "${CARGO}" build - -Z build-std=core - --bins - --release - --target=bpfel-unknown-none - ) - - pushd mitmproxy-linux-ebpf >/dev/null || die - echo "${cmd[*]}" >&2 - "${cmd[@]}" || die "${cmd[*]} failed" - popd >/dev/null || die - - export OUT_DIR="${S}/target/bpfel-unknown-none/release" -} - -python_test() { - cargo_src_test --manifest-path mitmproxy-linux/Cargo.toml -} diff --git a/net-proxy/mitmproxy-rs/Manifest b/net-proxy/mitmproxy-rs/Manifest index e760aef1e8d8..89fb823057c2 100644 --- a/net-proxy/mitmproxy-rs/Manifest +++ b/net-proxy/mitmproxy-rs/Manifest @@ -1,7 +1,4 @@ -DIST boringtun-2f3c85f5c4a601018c10b464b1ca890d9504bf6e.gh.tar.gz 219297 BLAKE2B af42c888259ce9d0d36c8b6925459e86fac14059e6b8a3532783050d77627f91c4cd7a0b61bd290e86bbe37ab155d9f798f5478b0f09650dabba08cdc9f60696 SHA512 485675e3fa9317d09814a67162199691df87d04b970c4f69536607584b07f6ab8d2379ef89b22d674e569b8a874a1298bc59a8537c077c81658594bbd502470e -DIST mitmproxy_rs-0.12.10-crates.tar.xz 41559496 BLAKE2B b84fc9a618b8abe8b79b1757d66872ce3dfe44048c200fd217ecd234abe6114af417987360d4fcdf691a35a19164116690343be537b8dd8936a2cb57253871fc SHA512 df72556e9c94fbb3a48d3f3bc31a65a3ad5767d94294004111df4428da610fb26f53b06f77a0ba9a51a9a3e99ddbd0aae5609cf42cb44ff125f0761266d3d803 -DIST mitmproxy_rs-0.12.10.tar.gz 1331077 BLAKE2B f839b15af573d01d00457ef08f8dcfe9f4f706d54a7a759116ed6b5ceda73b3c0eb20341891d689ab43e4a341d153a7617f9cb3655e83d112eeb79ce598975ff SHA512 f82c928f8f99d74ce58e526dae4477038100f9f5e3f36bec60c11156c93a2b2de1aa6ad0d35a43300daf380f99298f399bbc42d41b2cf4f426d13fb857610f18 DIST mitmproxy_rs-0.12.11-crates.tar.xz 42414204 BLAKE2B 26a76a54c9e4d9ae4bee50f2de0f1802ce3f0b8869a0d3738b95c6e26697173e47bd6f6fe0b81671a0f433060b682d9fef24fab3e56b0f17439cb38ebc5d15a1 SHA512 c629a2c21de7f818b333f18574975ee91cdf38d7f40de130153af45718bedbdca0417ccd6d263508d4d362f3025e5a9dc52f396930c8ba990b95438c71ef5aa1 DIST mitmproxy_rs-0.12.11.tar.gz 1329685 BLAKE2B 87faed980fceba45ddaf29050a36d956cf614dd5259af972ca882ba3577f124b42dae2d2ea59926789e8348b1f85c7464ddd208631e50785231dc481176c57b8 SHA512 f4dd2e40466e3c00a3816d005013585d172be16af5a7ac9875f23a781ccf8a0aaf8113832065f552f35c5823a4d57d9a3d472928e2d551f8c817a6b752d9cb19 -DIST mitmproxy_rs-0.12.9-crates.tar.xz 45242028 BLAKE2B b34979c3a573701a2f6f2f029a1cec05d4bc59013dc94c87a0e2b7d340c6bc8f7b2415d171b944f63a7d881ae87941a1e6fa61265218a3eea46fb7a127ef286d SHA512 ed4d48dd443eb777e5b387d38bf36ec6ee1d51e1c0aa650f0b27f8e34ae13a5c1eabfa766565879a661b2c4d6e458800e668f3a80a693eb9d5527ee0eb52314c -DIST mitmproxy_rs-0.12.9.tar.gz 1334424 BLAKE2B aed8aeebc1f6864ac31b6615491aa0fea6d5ee52c1f1dec2ffc68700fdeb3207ae504c4c9bc2c00c6b1531e66c58032200096c79c150944e667b22ce8088c0de SHA512 794211b9a3e623c0d59c172b8ea910f30031d000c41f470e5d78c1a87add01b20f15ec3dedd82924ce34ffcb1e7fcfd1a5557d638580f0e21b61aa3a522b286d +DIST mitmproxy_rs-0.12.15-crates.tar.xz 42546604 BLAKE2B 2f4406433c20df9af2deddfec6406c918daf95b00c32669c799dbf246308947a2497d6c5d29b91810c0652834db1ca97fa3a07591072576577de91507d7182a5 SHA512 bd8fa43d960c261cc944a70aa470f2c007513a7aa8cf872240f685b1c9d64afb8e059d1739a3f0535697b9f370d5e3c37b2a9c5371eea9cd565c8be9761da1f8 +DIST mitmproxy_rs-0.12.15.tar.gz 1332240 BLAKE2B 9afa00106f8b8b7f81215bb419845e7c37bf4750a0bf5aa7243c1528d4d2b01b01cc02da25edca6c4e655b22cf3b9443c22d5b8939642e73960e8df4442f8cac SHA512 9be4c22e8cc22b16e4186d4fd5b204a8ce6ddfb7ccb1b96af6e2c845414f2c3e0581ac8677e2b72b6deb3c21d8702fe03f917b6ebe6dc08422309ef8316c9197 diff --git a/net-proxy/mitmproxy-rs/mitmproxy-rs-0.12.10.ebuild b/net-proxy/mitmproxy-rs/mitmproxy-rs-0.12.15.ebuild index 31eeb36e74c7..4247bb8603d5 100644 --- a/net-proxy/mitmproxy-rs/mitmproxy-rs-0.12.10.ebuild +++ b/net-proxy/mitmproxy-rs/mitmproxy-rs-0.12.15.ebuild @@ -3,16 +3,13 @@ EAPI=8 -CRATES="" - -declare -A GIT_CRATES=( - [boringtun]='https://github.com/cloudflare/boringtun;2f3c85f5c4a601018c10b464b1ca890d9504bf6e;boringtun-%commit%/boringtun' -) +CRATES=" +" DISTUTILS_EXT=1 DISTUTILS_USE_PEP517=maturin PYTHON_COMPAT=( python3_{13..14} ) -RUST_MIN_VER=1.88.0 +RUST_MIN_VER=1.95.0 inherit cargo distutils-r1 pypi @@ -39,14 +36,6 @@ RDEPEND=" ~net-proxy/mitmproxy-linux-${PV}[${PYTHON_USEDEP}] " -src_prepare() { - distutils-r1_src_prepare - - # replace upstream crate substitution with our crate substitution, sigh - local bor_dep=$(grep ^boringtun "${ECARGO_HOME}"/config.toml || die) - sed -i -e "/boringtun/s;^.*$;${bor_dep};" Cargo.toml || die -} - python_test() { cargo_src_test --manifest-path mitmproxy-rs/Cargo.toml } diff --git a/net-proxy/mitmproxy-rs/mitmproxy-rs-0.12.9.ebuild b/net-proxy/mitmproxy-rs/mitmproxy-rs-0.12.9.ebuild deleted file mode 100644 index 81ff1669f261..000000000000 --- a/net-proxy/mitmproxy-rs/mitmproxy-rs-0.12.9.ebuild +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2024-2026 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -EAPI=8 - -CRATvES="" - -declare -A GIT_CRATES=( - [boringtun]='https://github.com/cloudflare/boringtun;2f3c85f5c4a601018c10b464b1ca890d9504bf6e;boringtun-%commit%/boringtun' -) - -DISTUTILS_EXT=1 -DISTUTILS_USE_PEP517=maturin -PYTHON_COMPAT=( python3_{13..14} ) -RUST_MIN_VER=1.89.0 -RUST_MAX_VER=1.90.0 - -inherit cargo distutils-r1 pypi - -DESCRIPTION="mitmproxy's Rust bits" -HOMEPAGE=" - https://github.com/mitmproxy/mitmproxy_rs/ - https://pypi.org/project/mitmproxy-rs/ -" -SRC_URI+=" - ${CARGO_CRATE_URIS} - https://github.com/gentoo-crate-dist/mitmproxy_rs/releases/download/v${PV}/${P/-/_}-crates.tar.xz -" - -LICENSE="MIT" -# Dependent crate licenses -LICENSE+=" - 0BSD Apache-2.0 Apache-2.0-with-LLVM-exceptions BSD-2 BSD ISC - LGPL-3+ MIT Unicode-3.0 WTFPL-2 ZLIB -" -SLOT="0" -KEYWORDS="amd64 ~arm64" - -RDEPEND=" - ~net-proxy/mitmproxy-linux-${PV}[${PYTHON_USEDEP}] -" - -src_prepare() { - distutils-r1_src_prepare - - # replace upstream crate substitution with our crate substitution, sigh - local bor_dep=$(grep ^boringtun "${ECARGO_HOME}"/config.toml || die) - sed -i -e "/boringtun/s;^.*$;${bor_dep};" Cargo.toml || die -} - -python_test() { - cargo_src_test --manifest-path mitmproxy-rs/Cargo.toml -} diff --git a/net-proxy/mitmproxy/Manifest b/net-proxy/mitmproxy/Manifest index 6d5988e81992..9d98bc4189f2 100644 --- a/net-proxy/mitmproxy/Manifest +++ b/net-proxy/mitmproxy/Manifest @@ -1 +1,2 @@ DIST mitmproxy-12.2.3.gh.tar.gz 31006972 BLAKE2B 0eafcd1ac654a103a9310436aa7454666b634019a337204b6e0b331a97cb63cd62578d2d70ff475b8acd2589cc8b066bb619d1ed3611048fe7446125773af7c2 SHA512 c9962e0420e0198fcf844377a679a14486ccc4bacbe85e7ed8942ccdfbdc4c55dc85357981503375245f9eac6f8d96e7e95415f844eee13228cd085383166e98 +DIST mitmproxy-12.3.0.gh.tar.gz 29977416 BLAKE2B c8ee4b995daad8e667c05dbb1aa8c3567b00356af091ca6347b3723d4b6bb7fee88ea11be6bcd56ee0c339b34f5e8a548c97f44969d67f8f8d8e4231e962089c SHA512 e01ab4ff63a5a142c98a6d18ed23e5c464c35254d2f3c0b5146b15847c1b43f9625353fadb977a311f7eff52cdca297ecc12c38bbc407427cf86a497b9a0f180 diff --git a/net-proxy/mitmproxy/mitmproxy-12.3.0.ebuild b/net-proxy/mitmproxy/mitmproxy-12.3.0.ebuild new file mode 100644 index 000000000000..180cf659f033 --- /dev/null +++ b/net-proxy/mitmproxy/mitmproxy-12.3.0.ebuild @@ -0,0 +1,86 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=setuptools +PYTHON_COMPAT=( python3_{13..14} ) +PYTHON_REQ_USE="sqlite" + +inherit distutils-r1 + +DESCRIPTION="An interactive, SSL-capable, man-in-the-middle HTTP proxy" +HOMEPAGE=" + https://www.mitmproxy.org/ + https://github.com/mitmproxy/mitmproxy/ + https://pypi.org/project/mitmproxy/ +" +SRC_URI=" + https://github.com/mitmproxy/mitmproxy/archive/v${PV}.tar.gz + -> ${P}.gh.tar.gz +" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64 ~arm64" + +# aioquic-1.3.0 introduced breaking changes +RDEPEND=" + >=app-arch/brotli-1.0.0[python,${PYTHON_USEDEP}] + =dev-python/aioquic-1.2*[${PYTHON_USEDEP}] + >=dev-python/argon2-cffi-23.1.0[${PYTHON_USEDEP}] + >=dev-python/asgiref-3.2.10[${PYTHON_USEDEP}] + $(python_gen_cond_dep ' + >=dev-python/backports-zstd-1.7.0[${PYTHON_USEDEP}] + ' 3.{12..13}) + >=dev-python/bcrypt-5.0.0[${PYTHON_USEDEP}] + >=dev-python/certifi-2026.10.19[${PYTHON_USEDEP}] + >=dev-python/cryptography-42.0[${PYTHON_USEDEP}] + >=dev-python/flask-3.0[${PYTHON_USEDEP}] + >=dev-python/h11-0.16.0[${PYTHON_USEDEP}] + >=dev-python/h2-4.1.0[${PYTHON_USEDEP}] + >=dev-python/hyperframe-6.0.0[${PYTHON_USEDEP}] + >=dev-python/kaitaistruct-0.10[${PYTHON_USEDEP}] + >=dev-python/ldap3-2.8[${PYTHON_USEDEP}] + <net-proxy/mitmproxy-rs-0.13[${PYTHON_USEDEP}] + >=net-proxy/mitmproxy-rs-0.12.15[${PYTHON_USEDEP}] + >=dev-python/msgpack-1.0.0[${PYTHON_USEDEP}] + >=dev-python/publicsuffix2-2.20190812[${PYTHON_USEDEP}] + >=dev-python/pyopenssl-24.3[${PYTHON_USEDEP}] + >=dev-python/pyparsing-3.3.2[${PYTHON_USEDEP}] + >=dev-python/pyperclip-1.9.0[${PYTHON_USEDEP}] + >=dev-python/ruamel-yaml-0.18.10[${PYTHON_USEDEP}] + >=dev-python/sortedcontainers-2.3.0[${PYTHON_USEDEP}] + >=dev-python/tornado-6.5.1[${PYTHON_USEDEP}] + $(python_gen_cond_dep ' + >=dev-python/typing-extensions-4.13.2[${PYTHON_USEDEP}] + ' 3.12) + >=dev-python/urwid-2.6.14[${PYTHON_USEDEP}] + >=dev-python/wsproto-1.0.0[${PYTHON_USEDEP}] +" + +BDEPEND=" + test? ( + >=dev-python/click-7.0[${PYTHON_USEDEP}] + >=dev-python/requests-2.9.1[${PYTHON_USEDEP}] + ) +" + +EPYTEST_PLUGINS=( hypothesis pytest-asyncio ) +EPYTEST_XDIST=1 +distutils_enable_tests pytest + +EPYTEST_DESELECT=( + # requires root? + test/mitmproxy/proxy/test_mode_servers.py::test_tun_mode +) + +python_prepare_all() { + distutils-r1_python_prepare_all + + # unpin dependencies + sed -i -r -e 's:,?<=?[0-9.]+,?::' pyproject.toml || die + + # hack pytest-9 compatibility in + sed -i -e 's:tool\.pytest\.individual_coverage:ignoreme:' pyproject.toml || die +} diff --git a/net-vpn/tor/tor-0.4.9.14.ebuild b/net-vpn/tor/tor-0.4.9.14.ebuild index 8788b0883035..b993702ac229 100644 --- a/net-vpn/tor/tor-0.4.9.14.ebuild +++ b/net-vpn/tor/tor-0.4.9.14.ebuild @@ -30,7 +30,7 @@ else S="${WORKDIR}/${MY_PF}" if [[ ${PV} != *_alpha* && ${PV} != *_beta* && ${PV} != *_rc* ]]; then - KEYWORDS="~amd64 ~arm ~arm64 ~hppa ~mips ~ppc ~ppc64 ~riscv ~sparc ~x86" + KEYWORDS="~amd64 arm arm64 ~hppa ~mips ppc ppc64 ~riscv ~sparc ~x86" fi BDEPEND="verify-sig? ( >=sec-keys/openpgp-keys-tor-20250713 )" diff --git a/net-wireless/soapyhackrf/Manifest b/net-wireless/soapyhackrf/Manifest index 42d0588d7e07..c43c9ed46941 100644 --- a/net-wireless/soapyhackrf/Manifest +++ b/net-wireless/soapyhackrf/Manifest @@ -1 +1,2 @@ DIST soapyhackrf-0.3.4.tar.gz 16475 BLAKE2B ee34c465e700ec93d02c51286224e8bc6f72c391867c1d3dc5bf54f28ef3230234937128eadf25409fe7c1561576a5a386178e0e94c820bd867c2fc5383a58c8 SHA512 950fc0d2d9b1a5946605d0f7f934a3d92facb6878251e8d9a673384db85e1b776ab27fd3499db11d482d599f78e705b9394b5e1238935d297252ab5acc8fb935 +DIST soapyhackrf-0.3.5.tar.gz 16406 BLAKE2B d268d4fe8a6142ef0d4c1afd10671bc55009d3004ac18dba5c1825634154a5c517e05d3e1eaf2e2cdef2c1e882b156f74fd268b18e1c433594afb415077d83ed SHA512 425a4effa6f908d57a52adcbf95a687c16893657d9cc681502330fb0639c96cb248ad96a8ea50e468d585dbe62c762f2b8aa0ebcbcdc16f935b30df82ea46036 diff --git a/net-wireless/soapyhackrf/soapyhackrf-0.3.5.ebuild b/net-wireless/soapyhackrf/soapyhackrf-0.3.5.ebuild new file mode 100644 index 000000000000..5d695a40cf47 --- /dev/null +++ b/net-wireless/soapyhackrf/soapyhackrf-0.3.5.ebuild @@ -0,0 +1,25 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake + +DESCRIPTION="SoapySDR HackRF module" +HOMEPAGE="https://github.com/pothosware/SoapyHackRF" + +if [ "${PV}" = "9999" ]; then + EGIT_REPO_URI="https://github.com/pothosware/SoapyHackRF.git" + inherit git-r3 +else + KEYWORDS="~amd64 ~arm ~riscv ~x86" + SRC_URI="https://github.com/pothosware/SoapyHackRF/archive/soapy-hackrf-${PV}.tar.gz -> ${P}.tar.gz" + S="${WORKDIR}"/SoapyHackRF-soapy-hackrf-"${PV}" +fi + +LICENSE="MIT" +SLOT="0" + +RDEPEND="net-wireless/soapysdr:= + net-libs/libhackrf:=" +DEPEND="${RDEPEND}" diff --git a/net-wireless/soapyhackrf/soapyhackrf-9999.ebuild b/net-wireless/soapyhackrf/soapyhackrf-9999.ebuild index 777ae3cc5c87..5d695a40cf47 100644 --- a/net-wireless/soapyhackrf/soapyhackrf-9999.ebuild +++ b/net-wireless/soapyhackrf/soapyhackrf-9999.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -23,8 +23,3 @@ SLOT="0" RDEPEND="net-wireless/soapysdr:= net-libs/libhackrf:=" DEPEND="${RDEPEND}" - -src_prepare() { - sed -i -e "s/2.8.7/3.5/" CMakeLists.txt || die - cmake_src_prepare -} diff --git a/net-wireless/soapyplutosdr/Manifest b/net-wireless/soapyplutosdr/Manifest index 144195fccf62..0745187b4242 100644 --- a/net-wireless/soapyplutosdr/Manifest +++ b/net-wireless/soapyplutosdr/Manifest @@ -1 +1,2 @@ DIST soapyplutosdr-0.2.2.tar.gz 25472 BLAKE2B 100b366edf268c76109be2d335b6d4d6a5723a219f1f160d481385e7ac7c89e3cd25860e9916b754f67708f83d90e6f82262572c6a62eb045483a2bba6a794b3 SHA512 dd5514cde4867bd915b2ef1f0257af85359986b93a7650f00f665fd83a7d7a2c8621f79fe91ce1c16867fdff0c0c14b700628cd9a94746c11b854da1afd7daf7 +DIST soapyplutosdr-0.2.4.tar.gz 27240 BLAKE2B d45de3afaf38babf033cf24ac477f727ce3661eb5551e432b158cbbf51e464f07e276534df907161488fc1536ab0f577695d53f3a3c0ae1ad0053f7670675d99 SHA512 be46759d33603081105accbdc6ec50498b0958080abb87cb411c90b0e0caca60c4d61fa3c1bff854ccefba387c0dacede979282205e7aa71769f0f6a5cc3b788 diff --git a/net-wireless/soapyplutosdr/soapyplutosdr-0.2.4.ebuild b/net-wireless/soapyplutosdr/soapyplutosdr-0.2.4.ebuild new file mode 100644 index 000000000000..d8b083321be2 --- /dev/null +++ b/net-wireless/soapyplutosdr/soapyplutosdr-0.2.4.ebuild @@ -0,0 +1,26 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake + +DESCRIPTION="Soapy SDR plugin for the Pluto SDR" +HOMEPAGE="https://github.com/pothosware/SoapyPlutoSDR" + +if [ "${PV}" = "9999" ]; then + EGIT_REPO_URI="https://github.com/pothosware/SoapyPlutoSDR.git" + inherit git-r3 +else + KEYWORDS="~amd64 ~arm ~riscv ~x86" + SRC_URI="https://github.com/pothosware/SoapyPlutoSDR/archive/soapy-plutosdr-${PV}.tar.gz -> ${P}.tar.gz" + S="${WORKDIR}"/SoapyPlutoSDR-soapy-plutosdr-${PV} +fi + +LICENSE="LGPL-2.1+" +SLOT="0" + +RDEPEND="net-wireless/soapysdr:= + net-libs/libiio:= + net-libs/libad9361-iio:=" +DEPEND="${RDEPEND}" diff --git a/net-wireless/soapyplutosdr/soapyplutosdr-9999.ebuild b/net-wireless/soapyplutosdr/soapyplutosdr-9999.ebuild index c185cc5749cf..d8b083321be2 100644 --- a/net-wireless/soapyplutosdr/soapyplutosdr-9999.ebuild +++ b/net-wireless/soapyplutosdr/soapyplutosdr-9999.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -13,9 +13,8 @@ if [ "${PV}" = "9999" ]; then inherit git-r3 else KEYWORDS="~amd64 ~arm ~riscv ~x86" - COMMIT="422a9b306f765499dd3e9a4c3400fa39816dcfdb" - SRC_URI="https://github.com/pothosware/SoapyPlutoSDR/archive/${COMMIT}.tar.gz -> ${P}.tar.gz" - S="${WORKDIR}"/SoapyPlutoSDR-${COMMIT} + SRC_URI="https://github.com/pothosware/SoapyPlutoSDR/archive/soapy-plutosdr-${PV}.tar.gz -> ${P}.tar.gz" + S="${WORKDIR}"/SoapyPlutoSDR-soapy-plutosdr-${PV} fi LICENSE="LGPL-2.1+" @@ -25,8 +24,3 @@ RDEPEND="net-wireless/soapysdr:= net-libs/libiio:= net-libs/libad9361-iio:=" DEPEND="${RDEPEND}" - -src_prepare() { - sed -i -e "s/2.8.9/3.10/" CMakeLists.txt || die - cmake_src_prepare -} diff --git a/net-wireless/soapyremote/Manifest b/net-wireless/soapyremote/Manifest index a9d65142b587..16ecf4301944 100644 --- a/net-wireless/soapyremote/Manifest +++ b/net-wireless/soapyremote/Manifest @@ -1 +1,2 @@ DIST soapyremote-0.5.2.tar.gz 72929 BLAKE2B 17b8a2f3f5400c4c6d88e726471d977aa27a263079690780dc91f93c0e30645a4e433f5406d694f5e3ba60bf0a73ce93fff616a21b0e4ac7e0b5d17cf8275711 SHA512 55841f248c796a870a81a508cec85962ed821c2abd40438eaee07135cb6dde0bd93e84a3ff709dfeaf26f7f79717197c80c3aad378d62b3e4b232f60c46b0151 +DIST soapyremote-0.5.3.tar.gz 74563 BLAKE2B b6580e4060b2409d73c86bccd35c0d7a23e77cefd3ca5a196285ba2446dcc4837098bc426de553deabdfe1b8fbac1832d47c98280b18be83d87213e2b4d6193e SHA512 33faa29a283284d31a0400cb2099567aec4ba8001f6ea6d105367b3a8440fb755524e2aa55a2742917181d6ad75526cdeda15a83ada27f7f5527f23a852335e6 diff --git a/net-wireless/soapyremote/soapyremote-0.5.3.ebuild b/net-wireless/soapyremote/soapyremote-0.5.3.ebuild new file mode 100644 index 000000000000..64a176dd971b --- /dev/null +++ b/net-wireless/soapyremote/soapyremote-0.5.3.ebuild @@ -0,0 +1,26 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake + +DESCRIPTION="Soapy SDR remote module" +HOMEPAGE="https://github.com/pothosware/SoapyRemote" + +if [ "${PV}" = "9999" ]; then + EGIT_REPO_URI="https://github.com/pothosware/SoapyRemote.git" + EGIT_CLONE_TYPE="shallow" + inherit git-r3 +else + KEYWORDS="~amd64 ~x86" + SRC_URI="https://github.com/pothosware/SoapyRemote/archive/soapy-remote-${PV}.tar.gz -> ${P}.tar.gz" + S="${WORKDIR}"/SoapyRemote-soapy-remote-"${PV}" +fi + +LICENSE="Boost-1.0" +SLOT="0" + +RDEPEND="net-wireless/soapysdr + net-dns/avahi" +DEPEND="${RDEPEND}" diff --git a/net-wireless/soapyrtlsdr/Manifest b/net-wireless/soapyrtlsdr/Manifest index 1b3fa3b3bd9c..ce38d9c92cba 100644 --- a/net-wireless/soapyrtlsdr/Manifest +++ b/net-wireless/soapyrtlsdr/Manifest @@ -1 +1,2 @@ DIST soapyrtlsdr-0.3.3.tar.gz 16760 BLAKE2B 63e4c707d65af13e9b3974c9afe1261909395c70589e07a5b8398fcd40c421dbb7754a39467a325d09f4312a410fb60e34f51fca1fdb26eac68ac495b0b2c9a8 SHA512 a43511c8644a8dbec8e0b7d8f114c4955ba9407727680680cef446a12a9ee27bfe892cab49aa77ce82d52e4272075f4e6f2ee38350675f5508601ff24ff82081 +DIST soapyrtlsdr-0.3.4.tar.gz 17190 BLAKE2B da4f18488cfff7c0aa3088bd3752fc7e13e9e403e5c96cdc2af8e451a4fee3881ae2dc44e4cc6596397fb6327b022128f5f31cff378770deb0a1edda95f9d3f9 SHA512 a5c5ec262eacf3f7f4d8d2efaa8aaf2421e291e2716b0ea9b375cde45bf99057b73b87eb3ca01fd2f0d7530c13c396981068fbb05d6d6a8f8e2388e7350c86cf diff --git a/net-wireless/soapyrtlsdr/soapyrtlsdr-0.3.4.ebuild b/net-wireless/soapyrtlsdr/soapyrtlsdr-0.3.4.ebuild new file mode 100644 index 000000000000..304813d53813 --- /dev/null +++ b/net-wireless/soapyrtlsdr/soapyrtlsdr-0.3.4.ebuild @@ -0,0 +1,25 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake + +DESCRIPTION="SoapySDR RTL-SDR Support Module" +HOMEPAGE="https://github.com/pothosware/SoapyRTLSDR" + +if [ "${PV}" = "9999" ]; then + EGIT_REPO_URI="https://github.com/pothosware/SoapyRTLSDR.git" + inherit git-r3 +else + KEYWORDS="~amd64 ~arm ~riscv ~x86" + SRC_URI="https://github.com/pothosware/SoapyRTLSDR/archive/soapy-rtlsdr-${PV}.tar.gz -> ${P}.tar.gz" + S="${WORKDIR}"/SoapyRTLSDR-soapy-rtlsdr-"${PV}" +fi + +LICENSE="MIT" +SLOT="0" + +RDEPEND="net-wireless/soapysdr:= + net-wireless/rtl-sdr:=" +DEPEND="${RDEPEND}" diff --git a/net-wireless/soapysdr/Manifest b/net-wireless/soapysdr/Manifest index 2de4d5a52684..1e06e5109f9e 100644 --- a/net-wireless/soapysdr/Manifest +++ b/net-wireless/soapysdr/Manifest @@ -1 +1,2 @@ DIST soapysdr-0.8.1.tar.gz 131252 BLAKE2B 4dc97b683063a1bc52717f15b12a2471a338cebc30dccfd58723792119a496f5170b67eeda1ff4dbcd34811948f70b9f0e5df11b5b1738b46839ca7377fe557b SHA512 50c8f1652bf9ca09215f9c4115175427ca7b5338add7591e31ca0e627093c94b73e7cf7f84fa71ff419cc010d3c1263931506c728bbaa00413a7915d56a87787 +DIST soapysdr-0.9.0.tar.gz 240603 BLAKE2B 71519da9aecdc738c41fba19a3b2422eaadc7e978c5c2d0003efb22d8aa527eba04534fdc8e9797783769485107d606913308274a1e3e0f26cfb03bbbfe7aea8 SHA512 f9069e9a823c4892b7f20af30e102bcacadc2ca75978833b52bd5b26d636af8c1cbbf84ac95828683fc5d768b2f0619b1ced137f1f84fa384d1df9a5b06608b8 diff --git a/net-wireless/soapysdr/soapysdr-0.9.0.ebuild b/net-wireless/soapysdr/soapysdr-0.9.0.ebuild new file mode 100644 index 000000000000..7d63e29aa2ce --- /dev/null +++ b/net-wireless/soapysdr/soapysdr-0.9.0.ebuild @@ -0,0 +1,54 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +PYTHON_COMPAT=( python3_{13..14} ) +inherit cmake python-single-r1 + +DESCRIPTION="vendor and platform neutral SDR support library" +HOMEPAGE="https://github.com/pothosware/SoapySDR" + +if [ "${PV}" = "9999" ]; then + EGIT_REPO_URI="https://github.com/pothosware/SoapySDR.git" + EGIT_CLONE_TYPE="shallow" + inherit git-r3 +else + KEYWORDS="~amd64 ~arm ~riscv ~x86" + SRC_URI="https://github.com/pothosware/SoapySDR/archive/soapy-sdr-${PV}.tar.gz -> ${P}.tar.gz" + S="${WORKDIR}"/SoapySDR-soapy-sdr-"${PV}" +fi + +LICENSE="Boost-1.0" +SLOT="0/${PV}" +IUSE="bladerf hackrf python rtlsdr plutosdr uhd" +REQUIRED_USE="python? ( ${PYTHON_REQUIRED_USE} )" + +RDEPEND="python? ( ${PYTHON_DEPS} )" +DEPEND="${RDEPEND}" +BDEPEND="python? ( dev-lang/swig:0 )" +PDEPEND=" + bladerf? ( net-wireless/soapybladerf ) + hackrf? ( net-wireless/soapyhackrf ) + rtlsdr? ( net-wireless/soapyrtlsdr ) + plutosdr? ( net-wireless/soapyplutosdr ) + uhd? ( net-wireless/soapyuhd ) +" + +pkg_setup() { + use python && python-single-r1_pkg_setup +} + +src_configure() { + local mycmakeargs=( + -DENABLE_PYTHON3=$(usex python) + -DENABLE_LUAJIT=OFF + ) + + cmake_src_configure +} + +src_install() { + cmake_src_install + use python && python_optimize +} diff --git a/net-wireless/soapysdr/soapysdr-9999.ebuild b/net-wireless/soapysdr/soapysdr-9999.ebuild index 886dd4316314..7d63e29aa2ce 100644 --- a/net-wireless/soapysdr/soapysdr-9999.ebuild +++ b/net-wireless/soapysdr/soapysdr-9999.ebuild @@ -35,21 +35,14 @@ PDEPEND=" uhd? ( net-wireless/soapyuhd ) " -PATCHES=( - "${FILESDIR}"/soapysdr-0.8.1-python3.12-distutils.patch - "${FILESDIR}"/soapysdr-0.8.1-cmake4.patch -) - pkg_setup() { use python && python-single-r1_pkg_setup } src_configure() { local mycmakeargs=( - -DENABLE_PYTHON=$(usex python) -DENABLE_PYTHON3=$(usex python) - -DBUILD_PYTHON3=$(usex python) - -DUSE_PYTHON_CONFIG=ON + -DENABLE_LUAJIT=OFF ) cmake_src_configure diff --git a/profiles/features/wd40/package.use.mask b/profiles/features/wd40/package.use.mask index a34a852d9f71..f8f928810acf 100644 --- a/profiles/features/wd40/package.use.mask +++ b/profiles/features/wd40/package.use.mask @@ -73,6 +73,7 @@ dev-python/srt test dev-python/sympy test dev-python/textdistance test dev-python/time-machine test +dev-python/tornado test dev-python/wcag-contrast-ratio test dev-python/xarray test dev-python/yarl test diff --git a/profiles/package.deprecated b/profiles/package.deprecated index f7dd6976faaf..1127b178d6fe 100644 --- a/profiles/package.deprecated +++ b/profiles/package.deprecated @@ -17,6 +17,12 @@ #--- END OF EXAMPLES --- +# Michał Górny <mgorny@gentoo.org> (2026-10-22) +# Since 5.0.0 release, the package is outright broken (unbound local). +# The "open source" support mailing list dropped my mail, and either way +# upstream seems to stopped paying any attention to it. +dev-python/reportlab + # Michał Górny <mgorny@gentoo.org> (2026-08-01) # Used only by old dev-python/feedparser versions. To be last-rited # once they're gone. diff --git a/profiles/package.mask/00-gentoo b/profiles/package.mask/00-gentoo index 19d5dfdad741..557f70a8b1c8 100644 --- a/profiles/package.mask/00-gentoo +++ b/profiles/package.mask/00-gentoo @@ -36,6 +36,28 @@ #--- END OF EXAMPLES --- +# Michał Górny <mgorny@gentoo.org> (2026-10-22) +# Not ported to py3.13. No revdeps. +# Removal on 2026-11-21. Bug #952389. +dev-python/pythondialog + +# Michał Górny <mgorny@gentoo.org> (2026-10-22) +# Not ported to py3.13. Ton of other bugs reported, including build +# failures and a version bump request. +# Removal on 2026-11-21. Bug #952686. +sci-mathematics/gsl-shell + +# Michał Górny <mgorny@gentoo.org> (2026-10-22) +# Not ported to py3.13. Indirectly depends on dev-python/sphinx that +# dropped py3.12 support. +# Removal on 2026-11-21. Bug #956588. +mail-client/alot + +# Sam James <sam@gentoo.org> (2026-10-22) +# Replaced by importlib, no reverse dependencies (bug #984696). Removal +# on 2026-11-22. +dev-python/entrypoints + # Sam James <sam@gentoo.org> (2026-10-20) # Breaks one of its major reverse dependencies, so mask for now as nothing # needs a newer version yet (bug #984580). diff --git a/profiles/package.mask/srcux-workarounds b/profiles/package.mask/srcux-workarounds index c68e33da21d8..705ba0bef4fd 100644 --- a/profiles/package.mask/srcux-workarounds +++ b/profiles/package.mask/srcux-workarounds @@ -25,14 +25,24 @@ # 2026-10-12: AIRouter C++ (replaces the Rust 0.1.2 line; runs on Alpha). Masked so # hosts with the Rust AIRouter are not upgraded by a world update; unmask per host # (bravo first, for the two-host qualification gate). +# 2026-10-22: _pre20261022_p1 added (shared work briefing; schema 16, so migrations 9-16 +# from _p6; the same mask applies). =net-misc/airouter-1.0.0_pre20261012 =net-misc/airouter-1.0.0_pre20261012_p6 +=net-misc/airouter-1.0.0_pre20261022_p1 =acct-user/airouter-1 =acct-group/airouter-1 # 2026-10-18: smith C++ 0.11.x, now 0.11.1 (replaces the Rust 0.2.0 line; runs on Alpha and the # desktop). Masked so hosts with Rust smith are not upgraded by a world update; unmask # per host. USE=server also needs a smith account, a PKI and a local AIRouter node. +# 2026-10-22: 0.13.0 added (`smith work` needs AIRouter >= _pre20261022, `shared_work = true` _p1). =app-misc/smith-0.11.1 +=app-misc/smith-0.13.0 =acct-user/smith-0 =acct-group/smith-0 + +# 2026-10-22: Workbench C++ 1.0.8 (replaces the Rust 0.1.0 line; runs on Alpha). Masked so +# hosts with the Rust Workbench are not upgraded by a world update (it also needs the masked +# C++ AIRouter); unmask per host. +=www-apps/workbench-1.0.8 diff --git a/sci-libs/lrslib/files/lrslib-073-gcc14.patch b/sci-libs/lrslib/files/lrslib-073-gcc14.patch new file mode 100644 index 000000000000..864ce92a96da --- /dev/null +++ b/sci-libs/lrslib/files/lrslib-073-gcc14.patch @@ -0,0 +1,42 @@ +--- a/lrslib.c 2026-10-22 20:22:09.935022216 +0200 ++++ b/lrslib.c 2026-10-22 20:23:46.903807455 +0200 +@@ -84,10 +84,10 @@ + /* signals handling */ + /*******************************/ + #ifndef SIGNALS +-static void checkpoint (); +-static void die_gracefully (); ++static void checkpoint (int); ++static void die_gracefully (int); + static void setup_signals (void); +-static void timecheck (); ++static void timecheck (int); + #endif + + /*******************************/ +@@ -6368,7 +6368,7 @@ + } + + static void +-timecheck () ++timecheck (int) + { + lrs_dump_state (); + errcheck ("signal", signal (SIGALRM, timecheck)); +@@ -6376,14 +6376,14 @@ + } + + static void +-checkpoint () ++checkpoint (int) + { + lrs_dump_state (); + errcheck ("signal", signal (SIGUSR1, checkpoint)); + } + + static void +-die_gracefully () ++die_gracefully (int) + { + lrs_dump_state (); + diff --git a/sci-libs/lrslib/lrslib-073.ebuild b/sci-libs/lrslib/lrslib-073.ebuild index 5bb3f05ee7c9..30ea09ae07a2 100644 --- a/sci-libs/lrslib/lrslib-073.ebuild +++ b/sci-libs/lrslib/lrslib-073.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2024 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -24,6 +24,7 @@ DEPEND="${RDEPEND}" PATCHES=( "${FILESDIR}/${P}-cflags-ldflags.patch" + "${FILESDIR}/${P}-gcc14.patch" ) src_prepare() { diff --git a/sci-mathematics/fricas/Manifest b/sci-mathematics/fricas/Manifest index 4fd431206bcf..815db725b86a 100644 --- a/sci-mathematics/fricas/Manifest +++ b/sci-mathematics/fricas/Manifest @@ -1,2 +1,2 @@ -DIST fricas-1.3.12-full.tar.bz2 10951028 BLAKE2B a88a9665ac003aaabf3aeb1a862a0402d781b22ffbb8a5fb95d5875ac459b86cd1f86c0160945898afa1bb88ca95f372ec73e9e58b73a3f7f75cb44f632571d7 SHA512 458505a41282e3aa874c70fe688a40296cb9a0d8053571d091a11df96c84ae5e8792524d83d156ccfecc915fa7c36b9dfa801cb68eaad36404aeccb673199358 DIST fricas-1.3.13-full.tar.bz2 10997314 BLAKE2B 57af1c51ca2ca754e58b754f237f34ea65ae3f0bdd8985e383a19a80a1438a04fa112e2067f5641a836bd3413d0298150b7261b16f7201cd5a8dd971b928d98b SHA512 8f2f8de62ccca7ee47a99ca19533ffd8384ee0acb888a5143836ed7587b78484bfe8da7bad13991166cf8b631243ceca44c45c879af7f38de5890f3c3db7ae6d +DIST fricas-1.4.0-full.tar.bz2 10651441 BLAKE2B b3eb80dd5994020719e2183a5e6107d17aa74451cd8894bc6f58e333ae102c2f02c1e8e63399aa5681bf09eb6449c896138f817d6f856cd8bc92e29996effea3 SHA512 409ac842085b9ed5ba441f1264c8b6b9695f8f0227a662de7a32c3dc91a42864c784fcd1d8ef1f0357a0628c837caf52ecca78e3bcb7e364b4def1dfb79f38c8 diff --git a/sci-mathematics/fricas/fricas-1.3.12.ebuild b/sci-mathematics/fricas/fricas-1.4.0.ebuild index 0e22b7c634cf..bc7eca47b168 100644 --- a/sci-mathematics/fricas/fricas-1.3.12.ebuild +++ b/sci-mathematics/fricas/fricas-1.4.0.ebuild @@ -1,7 +1,8 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 + VIRTUALX_REQUIRED="doc" inherit virtualx elisp-common @@ -71,7 +72,7 @@ src_test() { src_install() { emake -j1 DESTDIR="${D}" install - dodoc README.rst FAQ + dodoc README.rst if use emacs; then sed -e "s|(setq load-path (cons (quote \"/usr/$(get_libdir)/fricas/emacs\") load-path)) ||" \ diff --git a/sci-ml/ollama/Manifest b/sci-ml/ollama/Manifest index 95c44f01c351..5df84ca230c5 100644 --- a/sci-ml/ollama/Manifest +++ b/sci-ml/ollama/Manifest @@ -1,3 +1,3 @@ DIST llama.cpp-b11738.tar.gz 38354143 BLAKE2B 90f24b357ae8892bfb0a36c9937a7fd5d337c8f06f0da33270796e46d63ba48b11f4df07b5d1cbf0a240d504eb3a5c0568491c8bc67406ace5336f6783ae9b4e SHA512 947be844bace7c6904f6268b9b0348f0418c502802c6aa3622294db0d4d3742674e5048299ae0078aec1151db29291dfbbff393196d1a0c35adecf97d25b2e36 -DIST ollama-0.40.7-deps.tar.xz 77648092 BLAKE2B e8e87c098bb15aad0a1366538e2e6f3909069dd460f45b3f1e2a76db632436f24a2bbf1bc463168b94aa8b5b4994e797387ce560a622f4d18bcec0e9b5ebcb4f SHA512 e168c6e1e16482ebfe41ffa9f1794933c5881d49eb7f5ca348df46e909b85df327717979c835232a0782c3d40f2b1cfc6ec2b9eced820417a2d0d18251be7956 -DIST ollama-0.40.7.gh.tar.gz 26710831 BLAKE2B c2a3688f16e2fb8fff8c2f1c884b3ce5003c03b729aab9e856f65926c8b1fc2f00fb9e54542a0c6f744f795908eea27f307a606f072f74ddce1d54c04826e031 SHA512 45ff1aaa3d19282fc001a4fa0de3f886d538ac08bee9ea7ab1238fa22bc30bc0acced67cdf997e179eff4e33cbe281d62aaeeaeb85f2358fd2c77857ce6801a5 +DIST ollama-0.40.8-deps.tar.xz 77648092 BLAKE2B e8e87c098bb15aad0a1366538e2e6f3909069dd460f45b3f1e2a76db632436f24a2bbf1bc463168b94aa8b5b4994e797387ce560a622f4d18bcec0e9b5ebcb4f SHA512 e168c6e1e16482ebfe41ffa9f1794933c5881d49eb7f5ca348df46e909b85df327717979c835232a0782c3d40f2b1cfc6ec2b9eced820417a2d0d18251be7956 +DIST ollama-0.40.8.gh.tar.gz 26735721 BLAKE2B 339208fb60d773311239cb5d3a8e1544e6a03fb6a2280cde28715526389b02e41cf914e19008b53834aff88b9b3e7dc75e3b4c098fe7037f0a1fa09cf4ac4916 SHA512 f85ddd86c1ce104ff1f42e2f17a06acfcff0b1153c51b4ece052b3e1250dde5f2ac6821ec0a05fffd5e05166e8162c68fea65fe0fbeb584ec06dc88e5c4d2be4 diff --git a/sci-ml/ollama/ollama-0.40.7.ebuild b/sci-ml/ollama/ollama-0.40.8.ebuild index 13d7cf00ec19..13d7cf00ec19 100644 --- a/sci-ml/ollama/ollama-0.40.7.ebuild +++ b/sci-ml/ollama/ollama-0.40.8.ebuild diff --git a/sec-keys/openpgp-keys-apache-xmlgraphics/Manifest b/sec-keys/openpgp-keys-apache-xmlgraphics/Manifest new file mode 100644 index 000000000000..28a3667d9fb5 --- /dev/null +++ b/sec-keys/openpgp-keys-apache-xmlgraphics/Manifest @@ -0,0 +1,6 @@ +DIST openpgp-keys-apache-xmlgraphics-20260128 18941 BLAKE2B 40ac4dff3ea5de72f66b5d38fd495139bfcbe2c635aec1d72007a6ef1f75aef5f48df0a4d34ef0db57332ddd2f3b6512c301ed5db0375e7a36b4e741d9592fdc SHA512 0d35727716e670639d1dfc623bdd9f91518ce24ab32d4bf0e21629d5e719dedfffdd66bc8af6e09fe65ae9ccca3bb4808a8beb0b13101080e122cf5b69a5d8f7 +DIST openpgp-keys-xmlgraphics02-ubuntu-20260128.asc 7456 BLAKE2B 4b76b9bb9aadbeaed31f383dc085a810b6cdce09c9baca5df0ad586b10885e68d3a92e2fd03e97b9695b25d0a21b164846b36c2263a9ddc0d4ad42bab59628e0 SHA512 3ec9041af8c609e8469cbfb36d6abc5896aabab153309496d5ba391760142b5e3e50bd1f7a51ca47139f0923459bb47050ffcc912df1f9054b49200436f6b8f1 +DIST openpgp-keys-xmlgraphics03-ubuntu-20260128.asc 9167 BLAKE2B 637a16e42d3a3cb0b847ce8ca5f70f55fdededc07075a7696a988dc3ee8cb10cbe3cf00e80b4671bec2d12e04f77b60fce12b84fe85a77db687f7a34cdf5454e SHA512 e930fc2b53b83bc964cfb6c8f4d65fd16046d8522d34ca583ff786d4d3b82d87bc56b5df649cf1ffac49e115b57fac3cc8296a1c286ff0e4462863acd160aeb2 +DIST openpgp-keys-xmlgraphics04-ubuntu-20260128.asc 1659 BLAKE2B 6079833ec59c8fd3ca4d1960cd8b500ab50b20ea0ad4c83c3914905c97f4a4bf9065637342a2d88f98ba9367eb9e71a80f648a1e189ad03694d404b97106e199 SHA512 441cd6fba559dc7aaf82917a77a2fa3a00f57505671ee041f8872bf4f13cbd331f487a09db6844b3f14ad2661a1923d488c95d95e9bf8e159cdbdf6fb92a9316 +DIST openpgp-keys-xmlgraphics05-ubuntu-20260128.asc 32006 BLAKE2B aa5190c58738fa145eed4d7bb3434348b38fe646fbda5e34b699dee67a9bd43f86bb1dffea6132719a87d47337ce29b53352e8738654b22b2dae0cfc31a0bf57 SHA512 b50921a2dee669ebcdeef703d1520506ff04f5c31c00d5946a4f952478a3704c0483db8535536a8441cd732f12a48edb3600e59bd23c00975627d749889e7ce5 +DIST openpgp-keys-xmlgraphics06-ubuntu-20260128.asc 9622 BLAKE2B 1f9f254cfcacab1e6e8888e197f3ffa00ed038fe5484e374bbadecf232c9e78b63626871b780ebcc007eae446a3052cb25c6d8e936b8bda0d5975bae1abae408 SHA512 1c1943a4a50fbcb6f29832f0a4ff77b62dd9691f1e91adf8441c8ff57e4133f1ec8db884fe4d8053ce44615db01eafa145c3404adac1455920b25809097ec029 diff --git a/sec-keys/openpgp-keys-apache-xmlgraphics/metadata.xml b/sec-keys/openpgp-keys-apache-xmlgraphics/metadata.xml new file mode 100644 index 000000000000..0098fa341ee2 --- /dev/null +++ b/sec-keys/openpgp-keys-apache-xmlgraphics/metadata.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE pkgmetadata SYSTEM "https://srcux.com/dtd/metadata.dtd"> +<pkgmetadata> + <maintainer type="project"> + <email>java@gentoo.org</email> + <name>Java</name> + </maintainer> + <origin>srcux</origin> +</pkgmetadata> diff --git a/sec-keys/openpgp-keys-apache-xmlgraphics/openpgp-keys-apache-xmlgraphics-20260128.ebuild b/sec-keys/openpgp-keys-apache-xmlgraphics/openpgp-keys-apache-xmlgraphics-20260128.ebuild new file mode 100644 index 000000000000..1163b45e3752 --- /dev/null +++ b/sec-keys/openpgp-keys-apache-xmlgraphics/openpgp-keys-apache-xmlgraphics-20260128.ebuild @@ -0,0 +1,22 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +SEC_KEYS_VALIDPGPKEYS=( + '0B4235A1DE4EB7BB978E070CA072D4D04358C584:xmlgraphics02:manual,ubuntu' + '39DF200ECF6DCF076D968A2773CE2EFB8408F755:manual' + '5C9A30FF22B2C02F30261C305B93F1DF7CDB6DEA:xmlgraphics:manual' + '700A735AB4EC4B56F9112852F73095707C611584:xmlgraphics03:manual,ubuntu' + '7FFB2D270C4DE3113518D09DE3E4404FCC31AE97:xmlgraphics04:manual,ubuntu' + 'AB6638CE472A499B3959ADA2F989A2E5C93C5700:manual' + 'BE316D878F7CF48341EF13563E2A6FC25F298824:xmlgraphics05:manual,ubuntu' + 'D3544AC1BCADDF9D424FD0256654BABE0ED3D317:xmlgraphics06:manual,ubuntu' +) + +inherit sec-keys + +DESCRIPTION="OpenPGP keys used by xmlgraphics.apache.org/commons" +HOMEPAGE="https://xmlgraphics.apache.org/commons/download.html" +SRC_URI+=" https://downloads.apache.org/xmlgraphics/fop/KEYS -> ${P}" +KEYWORDS="amd64 arm64 ppc64" diff --git a/sys-apps/fakechroot/Manifest b/sys-apps/fakechroot/Manifest index a7019d114129..22c0a3f9c06b 100644 --- a/sys-apps/fakechroot/Manifest +++ b/sys-apps/fakechroot/Manifest @@ -1,2 +1,3 @@ +DIST fakechroot_2.20.1+ds-15.debian.tar.xz 21336 BLAKE2B 1fbf12bcf55f743f20efe2ed793e6df3f28668e62bd3181abb0a15b187db47f023d529ed1bb81b866d8aff2a259d50ca3622de4f7c31301f2ceaa9e9d7e6afdf SHA512 099a79589721355598c9aff788071810f638f9a222fdaa37c485e5ba5354f6b3752b1ecedb1e1703eefa414611a8776f8b9d81b08c3a0e1e34a7bef6164ea38b DIST fakechroot_2.20.1+ds-2.debian.tar.xz 16120 BLAKE2B f4fe6943ca8e0f2bc68cc9d97af1195c34b8700b37838ced773e1452352d46806ebd31dbd6439121fa625ce74148d749e5064fe347ea5b1fa2e41c4e1b1a9867 SHA512 060448526eb7ac89b53984c582bcfffb641abdf4e7696e0033610343039012e6d459fb6682b4c1caa12bfcb91d515dfc4123ff3f1be3468d9db1a58ab9a8b415 DIST fakechroot_2.20.1+ds.orig.tar.xz 88680 BLAKE2B 5e119eb6d7acee1f45bfc267e15b228624bbc5e29ebb19bcb61c4684719df4ce751c315423c27bceb0e2f97bcd491daa16d76f6b964842dcaddb90761dca092e SHA512 c61960b4fb03e65d7678511ae01a6f977d7f5584e81ee15a283f98da102dfc85d923265936443c7e9e208b62bb2147e8ae21a0ac62acf5bd8432775a4f6e723e diff --git a/sys-apps/fakechroot/fakechroot-2.20.1_p15.ebuild b/sys-apps/fakechroot/fakechroot-2.20.1_p15.ebuild new file mode 100644 index 000000000000..bcd3b0c8fcf6 --- /dev/null +++ b/sys-apps/fakechroot/fakechroot-2.20.1_p15.ebuild @@ -0,0 +1,45 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +# Upstream seem to be kind of dead, so using Debian's patches. +DESCRIPTION="Provide a faked chroot environment without requiring root privileges" +HOMEPAGE="https://github.com/dex4er/fakechroot" +if [[ ${PV} == *_p* ]] ; then + inherit autotools + + SRC_URI="mirror://debian/pool/main/f/${PN}/${PN}_$(ver_cut 1-3)+ds.orig.tar.xz" + SRC_URI+=" mirror://debian/pool/main/f/${PN}/${PN}_$(ver_cut 1-3)+ds-$(ver_cut 5).debian.tar.xz" + S="${WORKDIR}"/${PN}-$(ver_cut 1-3) +else + SRC_URI="https://github.com/dex4er/${PN}/releases/download/${PV}/${P}.tar.gz" +fi + +LICENSE="LGPL-2.1" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" + +RESTRICT="test" + +PATCHES=( + "${FILESDIR}"/${PN}-2.20.1-fix-build-for-clang.patch +) + +src_prepare() { + default + + if [[ ${PV} == *_p* ]] ; then + if [[ -d "${WORKDIR}"/debian/patches ]] ; then + eapply $(sed -e 's:^:../debian/patches/:' ../debian/patches/series || die) + fi + + eautoreconf + fi +} + +src_install() { + default + + find "${ED}" -name '*.la' -delete || die +} diff --git a/sys-apps/fakechroot/fakechroot-2.20.1_p2.ebuild b/sys-apps/fakechroot/fakechroot-2.20.1_p2.ebuild index 4a6d027535ae..8aacd61c8c26 100644 --- a/sys-apps/fakechroot/fakechroot-2.20.1_p2.ebuild +++ b/sys-apps/fakechroot/fakechroot-2.20.1_p2.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2022 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -22,6 +22,10 @@ KEYWORDS="~alpha amd64 ~arm arm64 ~hppa ~loong ~mips ppc ppc64 ~riscv ~s390 ~spa RESTRICT="test" +PATCHES=( + "${FILESDIR}"/${PN}-2.20.1-fix-build-for-clang.patch +) + src_prepare() { default diff --git a/sys-apps/fakechroot/files/fakechroot-2.20.1-fix-build-for-clang.patch b/sys-apps/fakechroot/files/fakechroot-2.20.1-fix-build-for-clang.patch new file mode 100644 index 000000000000..8c0795b7800b --- /dev/null +++ b/sys-apps/fakechroot/files/fakechroot-2.20.1-fix-build-for-clang.patch @@ -0,0 +1,121 @@ +https://github.com/dex4er/fakechroot/pull/126 + +From 7dec1d6d94cffab2d1968a1c0e5b665439e33c32 Mon Sep 17 00:00:00 2001 +From: "Z. Liu" <zhixu.liu@gmail.com> +Date: Tue, 20 Oct 2026 15:09:18 +0800 +Subject: [PATCH 1/2] drop unused fakechroot_wrapper::func member + +The func member of struct fakechroot_wrapper is never used: lazy +resolution only needs nextfunc and name (see fakechroot_loadfunc). +Initializing it from the wrapped function name makes the +wrapper_alias() wrappers take the address of functions that glibc +fortify headers turn into overloaded (pass_object_size / builtin) +versions, which fails with clang, e.g.: + + open64.c:32:15: error: address of overloaded function 'open64' is ambiguous + +Since the member is dead weight, simply remove it. + +Signed-off-by: Z. Liu <zhixu.liu@gmail.com> + +diff --git a/src/libfakechroot.h b/src/libfakechroot.h +index 4cf199f..56c7a6d 100644 +--- a/src/libfakechroot.h ++++ b/src/libfakechroot.h +@@ -143,7 +143,6 @@ + + #define wrapper_decl(function) \ + LOCAL struct fakechroot_wrapper fakechroot_##function##_wrapper_decl SECTION_DATA_FAKECHROOT = { \ +- (fakechroot_wrapperfn_t) function, \ + NULL, \ + #function \ + } +@@ -204,7 +203,6 @@ + typedef void (*fakechroot_wrapperfn_t)(void); + + struct fakechroot_wrapper { +- fakechroot_wrapperfn_t func; + fakechroot_wrapperfn_t nextfunc; + const char *name; + }; +-- +2.49.1 + + +From 75a47531758019f3d77ad53b46807b07a5237133 Mon Sep 17 00:00:00 2001 +From: "Z. Liu" <zhixu.liu@gmail.com> +Date: Tue, 20 Oct 2026 15:10:34 +0800 +Subject: [PATCH 2/2] use ANSI prototypes for the ftw()/nftw() entry points + +The entry points of ftw.c are still defined with old style (K&R) +parameter lists. C23 removed that language feature, so the build +fails when the configured compiler defaults to C23 (modern autoconf +probes for -std=gnu23 via AC_PROG_CC), e.g.: + + ftw.c:869:11: error: unknown type name 'path' + 869 | FTW_NAME (path, func, descriptors) + +Convert these definitions to ANSI prototypes, no functional change. + +Signed-off-by: Z. Liu <zhixu.liu@gmail.com> + +diff --git a/src/ftw.c b/src/ftw.c +index 92fc126..0e6130f 100644 +--- a/src/ftw.c ++++ b/src/ftw.c +@@ -866,10 +866,7 @@ ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, + /* Entry points. */ + + int +-FTW_NAME (path, func, descriptors) +- const char *path; +- FTW_FUNC_T func; +- int descriptors; ++FTW_NAME (const char *path, FTW_FUNC_T func, int descriptors) + { + debug(FTW_NAME_STRING "(\"%s\", &func, %d)", path, descriptors); + return ftw_startup (path, 0, func, descriptors, 0); +@@ -878,11 +875,7 @@ FTW_NAME (path, func, descriptors) + #if ((!defined(__FTW64_C) && HAVE_NFTW) || (defined(__FTW64_C) && HAVE_NFTW64)) + #ifndef _LIBC + int +-NFTW_NAME (path, func, descriptors, flags) +- const char *path; +- NFTW_FUNC_T func; +- int descriptors; +- int flags; ++NFTW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags) + { + debug(NFTW_NAME_STRING "(\"%s\", &func, %d, %d)", path, descriptors, flags); + return ftw_startup (path, 1, func, descriptors, flags); +@@ -894,11 +887,8 @@ NFTW_NAME (path, func, descriptors, flags) + int NFTW_NEW_NAME (const char *, NFTW_FUNC_T, int, int); + + int +-NFTW_NEW_NAME (path, func, descriptors, flags) +- const char *path; +- NFTW_FUNC_T func; +- int descriptors; +- int flags; ++NFTW_NEW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, ++ int flags) + { + debug(NFTW_NEW_NAME_STRING "(\"%s\", &func, %d, %d)", path, descriptors, flags); + if (flags +@@ -920,11 +910,8 @@ int NFTW_OLD_NAME (const char *, NFTW_FUNC_T, int, int); + + int + attribute_compat_text_section +-NFTW_OLD_NAME (path, func, descriptors, flags) +- const char *path; +- NFTW_FUNC_T func; +- int descriptors; +- int flags; ++NFTW_OLD_NAME (const char *path, NFTW_FUNC_T func, int descriptors, ++ int flags) + { + debug(NFTW_OLD_NAME_STRING "(\"%s\", &func, %d, %d)", path, descriptors, flags); + flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH); +-- +2.49.1 + diff --git a/sys-apps/opal-utils/opal-utils-7.0.ebuild b/sys-apps/opal-utils/opal-utils-7.0.ebuild index 402e8d60fdbb..f55a68a4fd9a 100644 --- a/sys-apps/opal-utils/opal-utils-7.0.ebuild +++ b/sys-apps/opal-utils/opal-utils-7.0.ebuild @@ -1,4 +1,4 @@ -# Copyright 2019-2025 Gentoo Authors +# Copyright 2019-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/sys-apps/systemd-utils/systemd-utils-260.1-r1.ebuild b/sys-apps/systemd-utils/systemd-utils-260.1-r1.ebuild index 303df18425df..f288f98d5c49 100644 --- a/sys-apps/systemd-utils/systemd-utils-260.1-r1.ebuild +++ b/sys-apps/systemd-utils/systemd-utils-260.1-r1.ebuild @@ -2,6 +2,7 @@ # Distributed under the terms of the GNU General Public License v2 EAPI=8 + PYTHON_COMPAT=( python3_{13..14} ) QA_PKGCONFIG_VERSION=$(ver_cut 1) @@ -86,7 +87,6 @@ BDEPEND=" app-text/docbook-xsl-stylesheets dev-libs/libxslt dev-util/gperf - dev-util/patchelf >=sys-apps/coreutils-8.16 sys-devel/gettext virtual/pkgconfig diff --git a/sys-apps/systemd-utils/systemd-utils-262.ebuild b/sys-apps/systemd-utils/systemd-utils-262.ebuild index ea07951c5838..da36baede602 100644 --- a/sys-apps/systemd-utils/systemd-utils-262.ebuild +++ b/sys-apps/systemd-utils/systemd-utils-262.ebuild @@ -90,7 +90,6 @@ BDEPEND=" app-text/docbook-xsl-stylesheets dev-libs/libxslt dev-util/gperf - dev-util/patchelf >=sys-apps/coreutils-8.16 sys-devel/gettext virtual/pkgconfig diff --git a/sys-boot/aboot/Manifest b/sys-boot/aboot/Manifest index 12e7547628bc..e03ed9f0f1f1 100644 --- a/sys-boot/aboot/Manifest +++ b/sys-boot/aboot/Manifest @@ -1,5 +1,6 @@ DIST aboot-1.0_pre20040408.tar.bz2 115062 BLAKE2B 49f1b45abcd4726fd036e24709d10bbfea42cee03293e31b2deb3aec383c1c438602aa97d3909516ec9864986dfcf82612fe7644b47e3e2760bdf377313446a0 SHA512 b1fff7ce806336f89efe8239108a8547053f22727cba93cea742995432c4f35133f8dc619588390ba6e0d114e2fd62b80b730443e6c18900aa2a8832ddd4ebfc DIST aboot-2.0.tar.gz 192381 BLAKE2B d9fc871d31df7378e35765875d1fab98608c0856300e818f8f71e2bac424fe0172412693add03537f6df5cbadc8f215cd961ffc98965d9cadf8477112824ee0f SHA512 ecc0221302cc5edaf3da6ead7c3a2b5682fe12ab2225c28aec35fe78aa8b2ccb9ecc2a339e0f09aad95339d0f47142d704ccbb47c391ea57f33da292fd4dc4e2 DIST aboot-2.0_pre20210801.tar.gz 173477 BLAKE2B 54c38a98b9e3e956f08b4fcca6a4f3351364d7774a915916aae8af67237982635a2686c1df0628fc28dcc2443da8ad57be822c8530e1580261c60dde73f00519 SHA512 57181897147bba65e8b216af262bf2d2e57cb76bd809d8207439dbfbe84ff550cf9a43c0f10f9925e54ca21660cfe9441c7607743ff3f7613c1e22e219708081 +DIST aboot-2.1.tar.gz 192429 BLAKE2B 7372ccaf1a71d599f201fcf7c67995511eb51f405311f313cb013942ee730603173a77e9d3e7cab6b4e6b498a974cc3e5e8faf54640943b0dbcb04c90a1f179f SHA512 54ce9fdc611c2b6b753cfc9d2185547421d0e03c1116a77d0e652e5608085a5f014e8234fcc3628bbc6b2f936c3d47ce77e8f0038d7c1144de5119472ad82902 DIST aboot_gentoo.diff.bz2 9527 BLAKE2B 2492b456f7411630b6ecfb19db775969b69a35c0ef31b2146fd353aac78cf4d3c9ef2162cf54ab4ac025c65607219b3a33a74d2105be196e6dda54dd2509c1f8 SHA512 868a73fee56e9bf6d986175a6a4dc321ed12fb78d74dfb8f1a3e35274577ddc9ea0820768b44836403587b3976ad8dacdcf6562a9ce5c45981c752670901155f DIST gentoo-headers-base-2.6.22.tar.bz2 4700338 BLAKE2B a264d7999ed7735e42ea21f8cf96b60ca1bdbfcb76dbdfcba14a849043e1f6a9109cd72bf5b91b9ab877dc3025ce6a9270ea914cf277b8f77cecff703646a7ec SHA512 99849928eede0c4b101ddc9179c0974cac6b4f18803dc98b72dcbe80cea88d3062f709beaa1b890edbed9c147be0dd516065d4cee945164b5d9f19f7a3cf249d diff --git a/sys-boot/aboot/aboot-2.1.ebuild b/sys-boot/aboot/aboot-2.1.ebuild new file mode 100644 index 000000000000..11275a334d95 --- /dev/null +++ b/sys-boot/aboot/aboot-2.1.ebuild @@ -0,0 +1,68 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=9 + +inherit toolchain-funcs + +DESCRIPTION="Alpha Linux boot loader for SRM" +HOMEPAGE="https://github.com/alphalinux/aboot" + +if [[ ${PV} == 9999 ]] ; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/alphalinux/${PN}.git" +else + SRC_URI="https://github.com/alphalinux/${PN}/archive/refs/tags/${P}.tar.gz" + S="${WORKDIR}/${PN}-${P}" + KEYWORDS="-* ~alpha" +fi + +LICENSE="GPL-2" +SLOT="0" + +BDEPEND="app-text/docbook-sgml-utils" + +src_compile() { + emake AR="$(tc-getAR)" CC="$(tc-getCC)" LD="$(tc-getLD)" \ + HOSTCC="$(tc-getBUILD_CC)" HOSTCFLAGS="${BUILD_CFLAGS}" \ + all netabootwrap + + einfo "Building man pages" + emake -C doc/man +} + +src_install() { + dodir /boot /sbin /usr/share/man/man{1,5,8} + emake root="${D}" install + + insinto /boot + doins net_aboot.nh + dobin netabootwrap + dodoc INSTALL README.md aboot.conf + + insinto /etc + newins "${FILESDIR}"/aboot.conf aboot.conf.example +} + +pkg_postinst() { + einfo "To make aboot install a new bootloader on your harddisk follow" + einfo "these steps:" + einfo "" + einfo " - edit the file /etc/aboot.conf" + einfo " - cd /boot" + einfo " - swriteboot -c2 /dev/sda bootlx" + einfo " This will install a new bootsector on /dev/sda and aboot will" + einfo " use the second partition on this device to lookup kernel and " + einfo " initrd (as described in the aboot.conf file)" + einfo "" + einfo "IMPORTANT :" + einfo "" + einfo "The partition table of your boot device has to contain " + einfo "a BSD-DISKLABEL and the first 12 megabytes of your boot device" + einfo "must not be part of a partition as aboot will write its bootloader" + einfo "in there and not as with most x86 bootloaders into the " + einfo "master boot sector. If your partition table does not reflect this" + einfo "you are going to destroy your installation !" + einfo "Also note that aboot currently only supports ext2/3 partitions" + einfo "to boot from." +} diff --git a/sys-boot/aboot/aboot-9999.ebuild b/sys-boot/aboot/aboot-9999.ebuild index f416b9104048..11275a334d95 100644 --- a/sys-boot/aboot/aboot-9999.ebuild +++ b/sys-boot/aboot/aboot-9999.ebuild @@ -1,18 +1,18 @@ # Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 -EAPI=8 +EAPI=9 inherit toolchain-funcs DESCRIPTION="Alpha Linux boot loader for SRM" -HOMEPAGE="https://github.com/alphalinux-org/aboot" +HOMEPAGE="https://github.com/alphalinux/aboot" if [[ ${PV} == 9999 ]] ; then inherit git-r3 - EGIT_REPO_URI="https://github.com/alphalinux-org/${PN}.git" + EGIT_REPO_URI="https://github.com/alphalinux/${PN}.git" else - SRC_URI="https://github.com/alphalinux-org/${PN}/archive/refs/tags/${P}.tar.gz" + SRC_URI="https://github.com/alphalinux/${PN}/archive/refs/tags/${P}.tar.gz" S="${WORKDIR}/${PN}-${P}" KEYWORDS="-* ~alpha" fi diff --git a/sys-devel/nvptx-tools/Manifest b/sys-devel/nvptx-tools/Manifest index 818fa1f04421..66e5b53cef78 100644 --- a/sys-devel/nvptx-tools/Manifest +++ b/sys-devel/nvptx-tools/Manifest @@ -1,2 +1,3 @@ DIST nvptx-tools-0_pre20240809.tar.gz 994915 BLAKE2B eef94a55d4ec48fb3031e20d5722e4f0564e7ed5ddc217a0b88d15236d9c15bbd60d14ab537e3535c40e800d75578c21833c6f414bdf11f0fed6f7eb3721478a SHA512 b98ee6f045ec820d056ce7cf4af4d3dcabdd0e77d15f02a88dd3e7f60de6ec32ecb454787f1ef626e358cde7744c5635f8d2e8ff78b46a714f627a37ac256ddb DIST nvptx-tools-0_pre20260402.tar.gz 1005862 BLAKE2B f2b458f2546b5a5faeafce33207d2f4d72a0dedc153f1f5fa0796d594fc054cec1e861af3fdb988d9eaa9f5c58ceb9de50c485bf619c021f72c15a46ed03175e SHA512 6f475200cab4d9858457b334f07f5e525e5a84eb02421eea6fa714efe5c58fd3f1ad7d7d0b8ad4797a3ab84ff4ed5ca08ea1c149ad63e38aec7648e78f91c170 +DIST nvptx-tools-0_pre20261017.tar.gz 1029414 BLAKE2B efaa431f8f442d1c20cf296265f725d0927f8a3c2939e066f8fab4c9df4a01795d6d089472713ce2235cd6423a589ba28e044cf91fec4b352af07052178fe583 SHA512 9be43f019ebed85272df8042136738bb2fa731847f871ae7978f51dd36c56e6451665f9e06cb139a8b7fac4483129ae74087fc8b7c7a54f66b8729ec2d0a3e85 diff --git a/sys-devel/nvptx-tools/nvptx-tools-0_pre20261017.ebuild b/sys-devel/nvptx-tools/nvptx-tools-0_pre20261017.ebuild new file mode 100644 index 000000000000..cb3f98a51014 --- /dev/null +++ b/sys-devel/nvptx-tools/nvptx-tools-0_pre20261017.ebuild @@ -0,0 +1,52 @@ +# Copyright 2023-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DESCRIPTION="Collection of tools for use with nvptx-none GCC toolchains" +HOMEPAGE="https://github.com/SourceryTools/nvptx-tools https://gcc.gnu.org/wiki/nvptx https://gcc.gnu.org/wiki/Offloading" + +if [[ ${PV} == 9999 ]] ; then + EGIT_REPO_URI="https://github.com/SourceryTools/nvptx-tools" + inherit git-r3 +else + MY_COMMIT="58ccfa5783d59315d49c60fd9b7f076611b4e6ae" + SRC_URI="https://github.com/SourceryTools/nvptx-tools/archive/${MY_COMMIT}.tar.gz -> ${P}.tar.gz" + S="${WORKDIR}"/${PN}-${MY_COMMIT} + + KEYWORDS="~amd64" +fi + +LICENSE="GPL-3" +SLOT="0" +IUSE="test" +RESTRICT="!test? ( test )" + +export CBUILD=${CBUILD:-${CHOST}} +export CTARGET=${CTARGET:-${CHOST}} +if [[ ${CTARGET} == ${CHOST} ]] ; then + if [[ ${CATEGORY} == cross-* ]] ; then + export CTARGET=${CATEGORY#cross-} + fi +fi + +BDEPEND=" + test? ( + dev-python/lit + dev-util/dejagnu + ) +" + +is_crosscompile() { + [[ ${CHOST} != ${CTARGET} ]] +} + +pkg_setup() { + # Reject newlib-on-glibc type installs + if [[ ${CTARGET} == ${CHOST} ]] ; then + case ${CHOST} in + *-newlib|nvptx-*) ;; + *) die "Use sys-devel/crossdev to build a nvptx(-none) toolchain" ;; + esac + fi +} diff --git a/sys-devel/nvptx-tools/nvptx-tools-9999.ebuild b/sys-devel/nvptx-tools/nvptx-tools-9999.ebuild index b6308a59af98..cb3f98a51014 100644 --- a/sys-devel/nvptx-tools/nvptx-tools-9999.ebuild +++ b/sys-devel/nvptx-tools/nvptx-tools-9999.ebuild @@ -1,4 +1,4 @@ -# Copyright 2023-2024 Gentoo Authors +# Copyright 2023-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -10,7 +10,7 @@ if [[ ${PV} == 9999 ]] ; then EGIT_REPO_URI="https://github.com/SourceryTools/nvptx-tools" inherit git-r3 else - MY_COMMIT="a0c1fff6534a4df9fb17937c3c4a4b1071212029" + MY_COMMIT="58ccfa5783d59315d49c60fd9b7f076611b4e6ae" SRC_URI="https://github.com/SourceryTools/nvptx-tools/archive/${MY_COMMIT}.tar.gz -> ${P}.tar.gz" S="${WORKDIR}"/${PN}-${MY_COMMIT} diff --git a/sys-libs/musl/files/musl-1.2.6-CVE-2026-107891.patch b/sys-libs/musl/files/musl-1.2.6-CVE-2026-107891.patch new file mode 100644 index 000000000000..3a72d563c217 --- /dev/null +++ b/sys-libs/musl/files/musl-1.2.6-CVE-2026-107891.patch @@ -0,0 +1,166 @@ +>From 7813e9119eb5fe681b6750b8a033a0490bca0941 Mon Sep 17 00:00:00 2001 +From: Szabolcs Nagy <nsz@port70.net> +Date: Thu, 23 Apr 2026 21:22:18 +0000 +Subject: [PATCH] regex: avoid int overflow in regcomp + +regcomp has many int arith ops that may overflow. before tre_expand_ast +int values are limited by 2x the input length n, e.g.: + + input ERE int field max value + aaaaaaaaaaaa... position n + ()()()()()()... submatch_id n/2+1 + ()||||||||||... num_tags 2n-3 + +so overflow before expand requires large n (and a lot of memory). later +some values can become big due to repetitions: + + input ERE int field that overflows + (((a{255}){255}){255}){255} position + ((a|b*){255,}){255,} num_transitions + +num_transitions and memory use can be big without {k,} repetitions too: + + input ERE what max value + (a|a|a|a...|a)* num_transitions n^2/4 + (()|a|a|...|a)* sum tnfa tags > n^3/16 - n^2 + +so memory can run out quickly as n grows (with 31bit va space 1700 long +input is enough even without {k,}) about half is tnfa and half is ast +firstpos/lastpos data. but overflow can happen before allocations fail. + +fix early overflow by limiting n. the stack usage is in [n,11n] without +repetitions, usually 7n, so the stack limit got adjusted too. the limit +is such that almost all inputs that worked before still work. + +overflows during and after expand are fixed by explicit checks via a +check_add helper and xcalloc instead of xmalloc. +--- + src/regex/regcomp.c | 41 +++++++++++++++++++++++++++++++++++------ + 1 file changed, 35 insertions(+), 6 deletions(-) + +diff --git a/src/regex/regcomp.c b/src/regex/regcomp.c +index b4b81968..a7115b12 100644 +--- a/src/regex/regcomp.c ++++ b/src/regex/regcomp.c +@@ -1680,6 +1680,12 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree, + AST to TNFA compilation routines. + */ + ++/* check if a+b overflows for a,b in [0,INT_MAX] */ ++static int check_add(unsigned a, unsigned b) ++{ ++ return a + b > INT_MAX; ++} ++ + typedef enum { + COPY_RECURSE, + COPY_SET_RESULT_PTR +@@ -1731,6 +1737,8 @@ tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, + /* XXX - e.g. [ab] has only one position but two + nodes, so we are creating holes in the state space + here. Not fatal, just wastes memory. */ ++ if (check_add(pos, *pos_add)) ++ return REG_ESPACE; + pos += *pos_add; + num_copied++; + } +@@ -1826,6 +1834,8 @@ tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, + break; + } + } ++ if (check_add(*pos_add, num_copied)) ++ return REG_ESPACE; + *pos_add += num_copied; + return status; + } +@@ -1870,6 +1880,8 @@ tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, + tre_literal_t *lit= node->obj; + if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) + { ++ if (check_add(lit->position, pos_add)) ++ return REG_ESPACE; + lit->position += pos_add; + if (lit->position > max_pos) + max_pos = lit->position; +@@ -1998,6 +2010,8 @@ tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, + } + + iter_depth--; ++ if (check_add(pos_add_total, pos_add - pos_add_last)) ++ return REG_ESPACE; + pos_add_total += pos_add - pos_add_last; + if (iter_depth == 0) + pos_add = pos_add_total; +@@ -2010,6 +2024,8 @@ tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, + } + } + ++ if (check_add(*position, pos_add_total)) ++ return REG_ESPACE; + *position += pos_add_total; + + /* `max_pos' should never be larger than `*position' if the above +@@ -2711,9 +2727,19 @@ regcomp(regex_t *restrict preg, const char *restrict regex, int cflags) + /* Parse context. */ + tre_parse_ctx_t parse_ctx; + ++ /* Limits: before tre_expand_ast, int values like position, subid or ++ * num_tags are bounded by 2x input length, used stack entries are ++ * bounded by 16x input length. input length is checked upfront to ++ * avoid overflows before expand. later, values that can grow big are ++ * bounds checked via check_add and xcalloc. */ ++ const int input_limit = 1024000; ++ const int stack_limit = 8*input_limit; ++ if (strnlen(regex, input_limit) >= input_limit) ++ return REG_ESPACE; ++ + /* Allocate a stack used throughout the compilation process for various + purposes. */ +- stack = tre_stack_new(512, 1024000, 128); ++ stack = tre_stack_new(512, stack_limit, 128); + if (!stack) + return REG_ESPACE; + /* Allocate a fast memory allocator. */ +@@ -2801,9 +2827,12 @@ regcomp(regex_t *restrict preg, const char *restrict regex, int cflags) + for example "a*" or "ab*". Figure out a simple way to detect + this possibility. */ + tmp_ast_l = tree; +- tmp_ast_r = tre_ast_new_literal(mem, 0, 0, parse_ctx.position++); ++ tmp_ast_r = tre_ast_new_literal(mem, 0, 0, parse_ctx.position); + if (tmp_ast_r == NULL) + ERROR_EXIT(REG_ESPACE); ++ if (check_add(parse_ctx.position, 1)) ++ ERROR_EXIT(REG_ESPACE); ++ parse_ctx.position++; + + tree = tre_ast_new_catenation(mem, tmp_ast_l, tmp_ast_r); + if (tree == NULL) +@@ -2813,22 +2842,22 @@ regcomp(regex_t *restrict preg, const char *restrict regex, int cflags) + if (errcode != REG_OK) + ERROR_EXIT(errcode); + +- counts = xmalloc(sizeof(int) * parse_ctx.position); ++ counts = xcalloc(sizeof(int), parse_ctx.position); + if (counts == NULL) + ERROR_EXIT(REG_ESPACE); + +- offs = xmalloc(sizeof(int) * parse_ctx.position); ++ offs = xcalloc(sizeof(int), parse_ctx.position); + if (offs == NULL) + ERROR_EXIT(REG_ESPACE); + +- for (i = 0; i < parse_ctx.position; i++) +- counts[i] = 0; + tre_ast_to_tnfa(tree, NULL, counts, NULL); + + add = 0; + for (i = 0; i < parse_ctx.position; i++) + { + offs[i] = add; ++ if (check_add(add, counts[i] + 1)) ++ ERROR_EXIT(REG_ESPACE); + add += counts[i] + 1; + counts[i] = 0; + } +-- +2.21.0 diff --git a/sys-libs/musl/musl-1.2.6-r3.ebuild b/sys-libs/musl/musl-1.2.6-r3.ebuild new file mode 100644 index 000000000000..97f4a81fef74 --- /dev/null +++ b/sys-libs/musl/musl-1.2.6-r3.ebuild @@ -0,0 +1,264 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit crossdev flag-o-matic toolchain-funcs prefix + +DESCRIPTION="Light, fast and, simple C library focused on standards-conformance and safety" +HOMEPAGE="https://musl.libc.org" + +if [[ ${PV} == 9999 ]] ; then + EGIT_REPO_URI="https://git.musl-libc.org/git/musl" + inherit git-r3 +else + VERIFY_SIG_OPENPGP_KEY_PATH=/usr/share/openpgp-keys/musl.asc + inherit verify-sig + + SRC_URI=" + https://musl.libc.org/releases/${P}.tar.gz + https://distfiles.gentoo.org/pub/proj/musl/${P}-patches.tar.xz + verify-sig? ( https://musl.libc.org/releases/${P}.tar.gz.asc ) + " + KEYWORDS="-* ~amd64 ~arm ~arm64 ~m68k ~mips ~ppc ~ppc64 ~riscv ~x86" + + BDEPEND="verify-sig? ( sec-keys/openpgp-keys-musl )" +fi + +GETENT_COMMIT="93a08815f8598db442d8b766b463d0150ed8e2ab" +GETENT_FILE="musl-getent-${GETENT_COMMIT}.c" +SRC_URI+=" + https://dev.gentoo.org/~blueness/musl-misc/getconf.c + https://gitlab.alpinelinux.org/alpine/aports/-/raw/${GETENT_COMMIT}/main/musl/getent.c -> ${GETENT_FILE} + https://dev.gentoo.org/~blueness/musl-misc/iconv.c +" + +LICENSE="MIT LGPL-2 GPL-2" +SLOT="0" +IUSE="crypt headers-only split-usr" + +QA_SONAME="usr/lib/libc.so" +QA_DT_NEEDED="usr/lib/libc.so" +# bug #830213 +QA_PRESTRIPPED="usr/lib/crtn.o" + +# We want crypt on by default for this as sys-libs/libxcrypt isn't (yet?) +# built as part as crossdev. Also, elide the blockers when in cross-*, +# as it doesn't make sense to block the normal CBUILD libxcrypt at all +# there when we're installing into /usr/${CHOST} anyway. +if is_crosspkg ; then + IUSE="${IUSE/crypt/+crypt}" +else + RDEPEND="crypt? ( !sys-libs/libxcrypt[system] )" + PDEPEND="!crypt? ( sys-libs/libxcrypt[system] )" +fi + +PATCHES=( + "${FILESDIR}"/${PN}-getifaddrs-qemu-workaround.patch + "${WORKDIR}"/${P}-patches + "${FILESDIR}"/${P}-riscv32-syscalls.patch + "${FILESDIR}"/${P}-riscv32-syscalls-2.patch + "${FILESDIR}"/${PN}-1.2.6-CVE-2026-107891.patch +) + +just_headers() { + use headers-only && target_is_not_host +} + +pkg_setup() { + if [[ ${CTARGET} == ${CHOST} ]] ; then + case ${CHOST} in + *-musl*) ;; + *) die "Use sys-devel/crossdev to build a musl toolchain" ;; + esac + fi + + # Fix for bug #667126, copied from glibc ebuild: + # make sure host make.conf doesn't pollute us + if target_is_not_host || tc-is-cross-compiler ; then + CHOST=${CTARGET} strip-unsupported-flags + fi +} + +src_unpack() { + if [[ ${PV} == 9999 ]] ; then + git-r3_src_unpack + elif use verify-sig ; then + # We only verify the release; not the additional (fixed, safe) files + # we download. + # (Seem to get IPC error on verifying in cross?) + ! target_is_not_host && verify-sig_verify_detached "${DISTDIR}"/${P}.tar.gz{,.asc} + fi + + default +} + +src_prepare() { + default + + mkdir "${WORKDIR}"/misc || die + cp "${DISTDIR}"/getconf.c "${WORKDIR}"/misc/getconf.c || die + cp "${DISTDIR}/${GETENT_FILE}" "${WORKDIR}"/misc/getent.c || die + cp "${DISTDIR}"/iconv.c "${WORKDIR}"/misc/iconv.c || die +} + +src_configure() { + strip-flags && filter-lto # Prevent issues caused by aggressive optimizations & bug #877343 + tc-getCC ${CTARGET} + + just_headers && export CC=true + + local sysroot + target_is_not_host && sysroot=/usr/${CTARGET} + ./configure \ + --target=${CTARGET} \ + --prefix="${EPREFIX}${sysroot}/usr" \ + --syslibdir="${EPREFIX}${sysroot}/lib" \ + --disable-gcc-wrapper || die +} + +src_compile() { + emake obj/include/bits/alltypes.h + just_headers && return 0 + + emake + if ! is_crosspkg ; then + emake -C "${T}" getconf getent iconv \ + CC="$(tc-getCC)" \ + CFLAGS="${CFLAGS}" \ + CPPFLAGS="${CPPFLAGS}" \ + LDFLAGS="${LDFLAGS}" \ + VPATH="${WORKDIR}/misc" + fi + + $(tc-getCC) ${CPPFLAGS} ${CFLAGS} -c -o libssp_nonshared.o "${FILESDIR}"/stack_chk_fail_local.c || die + $(tc-getAR) -rcs libssp_nonshared.a libssp_nonshared.o || die +} + +src_install() { + local target="install" + just_headers && target="install-headers" + emake DESTDIR="${D}" ${target} + just_headers && return 0 + + # musl provides ldd via a sym link to its ld.so + local sysroot= + target_is_not_host && sysroot=/usr/${CTARGET} + local ldso=$(basename "${ED}${sysroot}"/lib/ld-musl-*) + dosym -r "${sysroot}/lib/${ldso}" "${sysroot}/usr/bin/ldd" + + if ! use crypt ; then + # Allow sys-libs/libxcrypt[system] to provide it instead + rm "${ED}${sysroot}/usr/include/crypt.h" || die + rm "${ED}${sysroot}"/usr/*/libcrypt.a || die + fi + + if ! is_crosspkg ; then + # Fish out of config: + # ARCH = ... + # SUBARCH = ... + # and print $(ARCH)$(SUBARCH). + local arch=$(awk '{ k[$1] = $3 } END { printf("%s%s", k["ARCH"], k["SUBARCH"]); }' config.mak) + + # The musl build system seems to create a symlink: + # ${D}/lib/ld-musl-${arch}.so.1 -> /usr/lib/libc.so.1 (absolute) + # During cross or within prefix, there's no guarantee that the host is + # using musl so that file may not exist. Use a relative symlink within + # ${D} instead. + rm "${ED}"/lib/ld-musl-${arch}.so.1 || die + if use split-usr; then + dosym ../usr/lib/libc.so /lib/ld-musl-${arch}.so.1 + # If it's still a dead symlink, OK, we really do need to abort. + [[ -e "${ED}"/lib/ld-musl-${arch}.so.1 ]] || die + else + dosym libc.so /usr/lib/ld-musl-${arch}.so.1 + [[ -e "${ED}"/usr/lib/ld-musl-${arch}.so.1 ]] || die + fi + + cp "${FILESDIR}"/ldconfig.in-r3 "${T}"/ldconfig.in || die + sed -e "s|@@ARCH@@|${arch}|" "${T}"/ldconfig.in > "${T}"/ldconfig || die + eprefixify "${T}"/ldconfig + into / + dosbin "${T}"/ldconfig + into /usr + dobin "${T}"/getconf + dobin "${T}"/getent + dobin "${T}"/iconv + newenvd - "00musl" <<-EOF + # 00musl autogenerated by sys-libs/musl ebuild; DO NOT EDIT. + LDPATH="include ld.so.conf.d/*.conf" + EOF + fi + + if target_is_not_host ; then + into /usr/${CTARGET} + dolib.a libssp_nonshared.a + else + dolib.a libssp_nonshared.a + fi +} + +# Simple test to make sure our new musl isn't completely broken. +# Make sure we don't test with statically built binaries since +# they will fail. Also, skip if this musl is a cross compiler. +# +# If coreutils is built with USE=multicall, some of these files +# will just be wrapper scripts, not actual ELFs we can test. +musl_sanity_check() { + cd / #228809 + + # We enter ${ED} so to avoid trouble if the path contains + # special characters; for instance if the path contains the + # colon character (:), then the linker will try to split it + # and look for the libraries in an unexpected place. This can + # lead to unsafe code execution if the generated prefix is + # within a world-writable directory. + # (e.g. /var/tmp/portage:${HOSTNAME}) + pushd "${ED}"/usr/$(get_libdir) >/dev/null + + # first let's find the actual dynamic linker here + # symlinks may point to the wrong abi + local newldso=$(find . -maxdepth 1 -name 'libc.so' -type f -print -quit) + + einfo Last-minute run tests with ${newldso} in /usr/$(get_libdir) ... + + local x striptest + for x in cal date env free ls true uname uptime ; do + x=$(type -p ${x}) + [[ -z ${x} || ${x} != ${EPREFIX}/* ]] && continue + striptest=$(LC_ALL="C" file -L ${x} 2>/dev/null) || continue + case ${striptest} in + *"statically linked"*) continue;; + *"static-pie linked"*) continue;; + *"ASCII text"*) continue;; + esac + # We need to clear the locale settings as the upgrade might want + # incompatible locale data. This test is not for verifying that. + LC_ALL=C \ + ${newldso} --library-path . ${x} > /dev/null \ + || die "simple run test (${x}) failed" + done + + popd >/dev/null +} + +pkg_preinst() { + # Nothing to do if just installing headers + just_headers && return + + # Prepare /etc/ld.so.conf.d/ for files + mkdir -p "${EROOT}"/etc/ld.so.conf.d + + [[ -n ${ROOT} ]] && return 0 + [[ -d ${ED}/usr/$(get_libdir) ]] || return 0 + target_is_not_host && return 0 + musl_sanity_check +} + +pkg_postinst() { + target_is_not_host && return 0 + + [[ -n "${ROOT}" ]] && return 0 + + ldconfig || die +} diff --git a/sys-power/autosuspend/autosuspend-7.2.0.ebuild b/sys-power/autosuspend/autosuspend-7.2.0.ebuild index c18514dfa870..ea93594ffd88 100644 --- a/sys-power/autosuspend/autosuspend-7.2.0.ebuild +++ b/sys-power/autosuspend/autosuspend-7.2.0.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/virtual/jdk/jdk-28.ebuild b/virtual/jdk/jdk-28.ebuild index 4d76385c595a..677b2da26a97 100644 --- a/virtual/jdk/jdk-28.ebuild +++ b/virtual/jdk/jdk-28.ebuild @@ -5,7 +5,7 @@ EAPI=9 DESCRIPTION="Virtual for Java Development Kit (JDK)" SLOT="${PV}" -# KEYWORDS="" # Not an LTS candidate +KEYWORDS="~amd64" # Not an LTS candidate IUSE="headless-awt" RDEPEND="|| ( diff --git a/virtual/jre/jre-28.ebuild b/virtual/jre/jre-28.ebuild index 56a7f1b8a243..648cf38fb6c3 100644 --- a/virtual/jre/jre-28.ebuild +++ b/virtual/jre/jre-28.ebuild @@ -5,6 +5,6 @@ EAPI=9 DESCRIPTION="Virtual for Java Runtime Environment (JRE)" SLOT="${PV}" -# KEYWORDS="~amd64 ~arm64 ~ppc64" +KEYWORDS="~amd64" RDEPEND="virtual/jdk:${SLOT}" diff --git a/www-apps/rutorrent/Manifest b/www-apps/rutorrent/Manifest index 3dac05671c39..690df41ac6e8 100644 --- a/www-apps/rutorrent/Manifest +++ b/www-apps/rutorrent/Manifest @@ -1,2 +1,2 @@ -DIST rutorrent-5.3.15.tar.gz 3836159 BLAKE2B 23ee9e5263241dadec104c5a178ed31fa06263e39adff832fb6aed1e10a79e6358e5ff300a9b495674c5818ef67ca655ab25f1bd3e2520a7f465c13956e668b3 SHA512 240f5ba1c029166e87973f640192e30a91d1f4de7ad55ad70b2f8c4300d8c5ca1c2a7cd9a07e2ae22d2c58a9f291906eac717b621a88f88414815fdb7c2e63e4 DIST rutorrent-5.3.16.tar.gz 3874792 BLAKE2B 6c3decbff1bef1826d117c6e6c0c531f3cb8c0dfd19b4bfa0fae17270aa1119a0672d20fcdef6740bfc1388234ea0828c386a175330094f235514d929dbe4653 SHA512 e5559f371ce37a6a20ea026e3a226d80dfa12c614895e807eb664e9685f440f46aebcf835c47a80e38bcf25f2806df87114b7811cc81ee609d02c3d94cca4c74 +DIST rutorrent-5.3.18.tar.gz 3977839 BLAKE2B 1419f4825122512d1773c748093b595c3381c5872acd39954db26526a126095234104cb493aa9341e3d8adaddcbfd24f6cd809df4ae0bc421d9d57f83649e234 SHA512 d06f96ce09df94a299462e35d950df8062905102ef4c83b428c3c91e49756b77554da8ea755fd372fb45ecbf685315653ea0093f8c548f83bc3dff50362e1773 diff --git a/www-apps/rutorrent/rutorrent-5.3.15.ebuild b/www-apps/rutorrent/rutorrent-5.3.18.ebuild index 6810ce31b6c8..6810ce31b6c8 100644 --- a/www-apps/rutorrent/rutorrent-5.3.15.ebuild +++ b/www-apps/rutorrent/rutorrent-5.3.18.ebuild diff --git a/www-apps/workbench/Manifest b/www-apps/workbench/Manifest index 4ea2bcee2714..e2960094b65e 100644 --- a/www-apps/workbench/Manifest +++ b/www-apps/workbench/Manifest @@ -1,3 +1,5 @@ DIST workbench-0.1.0.tar.zst 42553058 BLAKE2B c0e1787476d9afd60eea27ce0a74bc25ab07c11a18ebf8bcff635f771fb8f86854ccc3b8cc7c9248576a88dfffed6f2c88382eaf94f236b252917cba9eea3c8a SHA512 1300dfd982ec81ab8b460152d31e3a5fa1950f478dec7454d8ebd0be4f6b917d5e02763e90aa0ce2166a89bd7482443ee6dd7f328a3bfd10215a501e3ddb1bf8 DIST workbench-0.1.0-r1.tar.zst 43089066 BLAKE2B 58494876b6d94cdc1b29326f7ad2c95e7eb890f20626bcc6a13fa5a42197c8624166a7aa42b25c8bd32d4341b896f6aba707bd497204ee2540cd625e3f973f4b SHA512 c7d0e436d11882027540b9fea2eea5c3931d3a797cf030ce24f1bc77f9298b63577c05b349fa6d9988272ceb6b8a17fc410f22a020e5a0e38bc011159de8d226 DIST workbench-0.1.0-r2.tar.zst 43088702 BLAKE2B 3e724164f5c36682e8f474b3807a6e7c964e02dffeb1c27f716c1d5e6dddb5a3b1154c1199771e4b9f0d5a5f9265afa851d9021872ee99678a513d8b48f910a3 SHA512 5e495ceb33082471db8b33818432f06c2b69d1690036abb73df4e96d46a88a120986b61f340576056603f73a5d40437f2ed40540d0753404775bb88abd649f0c +DIST airouter-1.0.0_pre20261022.tar.gz 1161236 BLAKE2B de9048a7b92a09831562dfb28fc55b8544e8ea0cd5435120aa607703acc6546a09d2a33588a0a130d595b54c2acd833601a302c2e2c1c0463f815d215fa16a03 SHA512 fe5b08a7c1c01fbf841eacecbaa041bc1d133742f24301a1e6e69a78f9b0d072c59560512b084178fe205323c7097123604a2fbe3b9631de9a68547c041f87b8 +DIST workbench-1.0.8.tar.gz 449768 BLAKE2B 660ac12ff294cc1556e47c16e66c36f7889fdfa5d919b9227aa9531dcb100c3165bab47680f4cbcc96b4f446fe9b06ae270a3262935437c741be2a50fedeb5ab SHA512 524d7b65eb208a6e0695e75e5201abd9b66874dd7410b6381931f6b1c3e40b2b9c138c3d71ab5681f5f0ada79ad99456d73a05b8f89b76c7661718f295513f6f diff --git a/www-apps/workbench/workbench-1.0.8.ebuild b/www-apps/workbench/workbench-1.0.8.ebuild new file mode 100644 index 000000000000..7eed086fd827 --- /dev/null +++ b/www-apps/workbench/workbench-1.0.8.ebuild @@ -0,0 +1,78 @@ +EAPI=8 + +inherit cmake + +DESCRIPTION="Workbench: projects, conversations and agent runs through AIRouter (C++)" +HOMEPAGE="https://trunkmasters.com/workbench" +# The shared memory export compiles AIRouter's legacy-memory component from AIRouter's +# own source archive (the same version as the AIRouter package it pairs with). +AIROUTER_PV="1.0.0_pre20261022" +SRC_URI="https://www.srcux.com/distfiles/${P}.tar.gz + https://www.srcux.com/distfiles/airouter-${AIROUTER_PV}.tar.gz" + +# The application carries no grant of an open-source license. +LICENSE="all-rights-reserved" +SLOT="0" +KEYWORDS="~amd64" +RESTRICT="mirror bindist" + +DEPEND=" + =dev-libs/boost-1.91.0* + =dev-libs/openssl-3.6.5* + =dev-db/postgresql-18.6* + =dev-cpp/nlohmann_json-3.12.0* + =dev-cpp/tomlplusplus-3.4.0* + =dev-cpp/jsoncons-1.6.0* +" +# Runtime: the AIRouter API for turns, Citadel sign-in, the Codegraph engine (Node) +# in bubblewrap, ffmpeg and the local whisper-server for voice input, sudo for the +# admin tools and the root release executor, curl/runuser/prlimit for the verifiers. +RDEPEND=" + ${DEPEND} + acct-user/workbench + acct-group/workbench + app-admin/sudo + >=net-misc/airouter-${AIROUTER_PV} + sys-auth/citadel + dev-util/codegraph + net-libs/nodejs + media-video/ffmpeg + app-accessibility/whisper-cpp + dev-vcs/git + net-misc/curl + sys-apps/acl + sys-apps/bubblewrap + sys-apps/util-linux +" +BDEPEND=" + =dev-cpp/catch-3.15.3* + >=dev-lang/python-3.11 +" + +src_configure() { + # The project refuses in-source builds and requires an absolute build root. + export WORKBENCH_CPP_BUILD_ROOT="${WORKDIR}/build" + local mycmakeargs=( + -DWB_SANITIZER=none + -DWB_AIROUTER_MEMORY_SOURCE="${WORKDIR}/airouter-${AIROUTER_PV}" + ) + cmake_src_configure +} + +src_test() { + # The end-to-end tests need PostgreSQL and sockets; they run outside the sandbox. + cmake_src_test -R '^unit$' +} + +src_install() { + cmake_src_install + newinitd "${ED}"/usr/share/workbench/openrc/workbenchd.initd workbenchd + newconfd "${ED}"/usr/share/workbench/openrc/workbenchd.confd workbenchd + # /var/lib/workbench (the workspaces, with their ACLs) is not the package's to set. +} + +pkg_postinst() { + elog "workbenchd serves /run/workbench/http.socket (symlinked from /run/workbench.sock)" + elog "for Apache, AIRouter's wb-mcp and task gateways. It needs WB_SESSION_SECRET in" + elog "/etc/workbench/secrets.env and the workbench database. Then: rc-service workbenchd start." +} diff --git a/www-client/opera/opera-138.0.6044.24.ebuild b/www-client/opera/opera-138.0.6044.24-r1.ebuild index 9e07d5e923a0..461a9267a64d 100644 --- a/www-client/opera/opera-138.0.6044.24.ebuild +++ b/www-client/opera/opera-138.0.6044.24-r1.ebuild @@ -31,7 +31,7 @@ fi # Commit ref from `strings libffmpeg.so | grep -F "FFmpeg version"` matches this Chromium version # or use Chromicler to handle bumps. # Does not _need_ to be updated for every new version of Opera, only when it breaks. -CHROMIUM_VERSION="153" +CHROMIUM_VERSION="154" SRC_URI=" amd64? ( ${SRC_URI_BASE[*]/%//${PV}/linux/${MY_PN}_${PV}_amd64.deb} ) arm64? ( https://deb.opera.com/opera-stable/pool/non-free/o/${MY_PN}/${MY_PN}_${PV}_arm64.deb ) diff --git a/www-client/qutebrowser/qutebrowser-3.6.3.ebuild b/www-client/qutebrowser/qutebrowser-3.6.3.ebuild index f7a55636135a..c0116ebc090d 100644 --- a/www-client/qutebrowser/qutebrowser-3.6.3.ebuild +++ b/www-client/qutebrowser/qutebrowser-3.6.3.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/www-client/qutebrowser/qutebrowser-9999.ebuild b/www-client/qutebrowser/qutebrowser-9999.ebuild index 0df0d3bbc696..589ef7593326 100644 --- a/www-client/qutebrowser/qutebrowser-9999.ebuild +++ b/www-client/qutebrowser/qutebrowser-9999.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 diff --git a/www-servers/apache/apache-2.4.69.ebuild b/www-servers/apache/apache-2.4.69.ebuild index 17dcf0af3c01..f1a1ef255833 100644 --- a/www-servers/apache/apache-2.4.69.ebuild +++ b/www-servers/apache/apache-2.4.69.ebuild @@ -147,7 +147,7 @@ HOMEPAGE="https://httpd.apache.org/" # some helper scripts are Apache-1.1, thus both are here LICENSE="Apache-2.0 Apache-1.1" SLOT="2" -KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-macos ~x64-solaris" +KEYWORDS="~alpha amd64 arm arm64 ~hppa ~loong ~mips ppc ppc64 ~riscv ~s390 ~sparc x86 ~x64-macos ~x64-solaris" DEPEND="${RDEPEND}" diff --git a/www-servers/nginx/nginx-1.31.7-r1.ebuild b/www-servers/nginx/nginx-1.31.7-r1.ebuild new file mode 100644 index 000000000000..17a2764b6216 --- /dev/null +++ b/www-servers/nginx/nginx-1.31.7-r1.ebuild @@ -0,0 +1,43 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +NGINX_SUBSYSTEMS=(+http stream mail) +NGINX_MODULES=( + +http_{charset,gzip,ssi,userid,access,auth_basic,mirror,autoindex,geo,map} + +http_{split_clients,referer,rewrite,proxy,fastcgi,uwsgi,scgi,grpc} + +http_{memcached,limit_conn,limit_req,empty_gif,browser,upstream_hash} + +http_{upstream_ip_hash,upstream_least_conn,upstream_random} + +http_{upstream_keepalive,upstream_zone} + http_{ssl,v2,v3,realip,addition,xslt,image_filter,geoip,sub,dav,flv,mp4} + http_{gunzip,gzip_static,auth_request,random_index,secure_link,degradation} + http_{slice,stub_status,perl} + +mail_{pop3,imap,smtp} + mail_ssl + +stream_{limit_conn,access,geo,map,split_clients,return,pass,set} + +stream_{upstream_hash,upstream_least_conn,upstream_random,upstream_zone} + stream_{ssl,realip,geoip,ssl_preread} +) +NGINX_TESTS_COMMIT=ff61b0ffdb77c4279be2aab1529a5ad06bfcc119 +NGINX_MISC_FILES=( + nginx-{r2.logrotate,r2.service,r5.conf,r6.initd,r1.confd,r1.tmpfiles} +) +NGINX_SUPPORT_MODULE_STUBS=1 + +inherit nginx + +KEYWORDS="amd64 arm arm64 ~loong ~ppc ~ppc64 ~riscv x86" + +PATCHES=( + "${FILESDIR}/${PN}-httpoxy-mitigation-r1.patch" +) + +src_prepare() { + nginx_src_prepare + + if use test ; then + # Fails with network-sandbox (bug #976129) + rm "${NGX_TESTS_S}"/tunnel_next_upstream.t || die + fi +} diff --git a/x11-drivers/nvidia-drivers/Manifest b/x11-drivers/nvidia-drivers/Manifest index d5d503f5ba99..688fcf7bb7c1 100644 --- a/x11-drivers/nvidia-drivers/Manifest +++ b/x11-drivers/nvidia-drivers/Manifest @@ -2,6 +2,7 @@ DIST NVIDIA-Linux-aarch64-535.309.01.run 270033799 BLAKE2B 3f5ce5f024d0bb3570703 DIST NVIDIA-Linux-aarch64-580.178.04.run 314757575 BLAKE2B 2dbe1f8ccd0df8ef882be9745c94732f7da691b960054b0ebaba8932486d30504251e263b2614c732c1ad15dfd9ac22962a6d0b3e00d811407a0fd41adef70cf SHA512 3753150ab1fc319137431d24d38aabab9d4dcb11d988cfe7f80813f946926a181415640ac874bf28104274b305ec2d29c7806fbea945224ea97fa5b4afb2475a DIST NVIDIA-Linux-aarch64-580.196.07.run 314791776 BLAKE2B 6128906d54956b9348eacef092500dc94826dc908d869f7294e6b12443661520be3685fc53c1c76482673c20e2cabe0260245caa5da86a8b6ca68a715075e38d SHA512 d5197bebdcfec6cd4c03e0987fd40cbaa4e75e663393e1925d7388859883f809b8d9c7b5836368d561581e3ad7dcaa9b393dc7840c7c8965769f687af1baefea DIST NVIDIA-Linux-aarch64-595.104.02.run 317793631 BLAKE2B dacfb4a822b7e6a8fa57826f9bcac5a444bd77ed61a9e87cb89d4dee1ef22716527ea39a0119f74795e131a1543aa46e2c9396af0259fe5e8ccae04a0a50d142 SHA512 8bb367b77a44427e70c23b8362be74fb7d6d28357e90349bb9ea293a6b99d9e05f05b7002f8b4cd0b1b35ebf431bed4b5dbf5a64ba9698ed69e0da3fdde5d6a7 +DIST NVIDIA-Linux-aarch64-595.112.07.run 317615390 BLAKE2B 6455da567641b6d54047be3db5678fca9b8c3743c5edea56fe3216d2d05a4c21e15e14ec16f1a58bf26ef43fb9f2a09c8c5cbb27782a66cdcba8523d9ed7bdf2 SHA512 30ee1855286b9a53e9bbb21d7365513b860ab24d0b42807886726128b9ce24a22d0a668c85c1e2d9f00519eb4b224faf3f5c9d05b87a7e3cd65e0348a8c489ee DIST NVIDIA-Linux-aarch64-615.78.08.run 394587485 BLAKE2B 8ac847f2277f5761aa11965c41ec7f64d9ca4baa91979c64f2254b529e95760e83521f3f6fc85aba9d75636b44e39e0a2c03af1541e2c2d2c3f2ffb908e98464 SHA512 6844c78be6e2780ad38461a6b87cfefa702d403c1d20e888003a999dffa609733d0933daa24f3bc077005b95638cc9d76f76c465bfbe9e46f7dbe832cbb90a5d DIST NVIDIA-Linux-x86-390.157.run 49417041 BLAKE2B 435b5e0e60468cc74b41cc5d9b5bb0105d29c11a2d408457835212a96519c9334ecf19278b36749d3ff7f498f5b0ae45a2efae2d428703dac21245c62f1efebe SHA512 dae9ac2c12f9ad90f7441e00e01a984147c577892421911d7f67f6a31e616ac1cb7d434bc9c7f58fbd9b8ae909521b5bece607a63c79588c0bb9d99c6bad5fb0 DIST NVIDIA-Linux-x86_64-390.157.run 85835541 BLAKE2B 44b855cd11f3b2f231f9fb90492ae2e67a67ea3ea83c413e7c90956d38c9730a8bd0321281ae03c6afce633d102f5b499aed25622b9bfd31bdd2c98f0717e95b SHA512 4780503a4cff0180b1b1f37d20a6ba3438dd78b51fa7f69b98d35521835e5d61099129746929d51d63afa2d47b672b3e145e1e1897584dc3799e7f822c5b3a0a @@ -10,12 +11,14 @@ DIST NVIDIA-Linux-x86_64-535.309.01.run 341945818 BLAKE2B dc7bca19eee348648ed6de DIST NVIDIA-Linux-x86_64-580.178.04.run 397214534 BLAKE2B 53ea96e4d1342003238aaa37495df8939b0e8813b8088347ec406a4c066bdfcf9947abc71f05da9433bfbee7b04b67f8a2bfb9729423ec776c0b1e31d5ce1b77 SHA512 b463dec07dab14370e3bd89d559e54e5f81d5b01be9de2bac6c700334c10a8bce2f9049b978a01a97d515d5ebb16ec4ecf02ce35979a8833a619bc6bd97084de DIST NVIDIA-Linux-x86_64-580.196.07.run 397209845 BLAKE2B 69315e16040d32ffa922dfbc0a9dc96625ccc42306e1f724cadeb0f597d434a13e7a391e54ea88949c095c4e29a7f9af220b6722711efb4316c0220fab8fc63f SHA512 dabd0b33fae2c4f05501202a4bcac3b8dd8dc1e5121b6e0991b8ba47eec36beb33af5b3fde2994567e0133a55910a2dab80facca682e14ac8c09990fcf9166e4 DIST NVIDIA-Linux-x86_64-595.104.02.run 423378754 BLAKE2B 02cf64ce916433e00afbb82c6e7bbc4e7e6723ebaa84a90b431641ed918a21a527545e844666067df29bb1bf097b064842ebe4737336a54074fed55e5f5bceaf SHA512 8c6bbc638a0192c43595dda9db70c86da0dc1f7d768838b452af7070449c4967d7e1abfc969ec4d2505e460fb8bbf9e261c4c918f1fc3efe6d56be33f8a14b16 +DIST NVIDIA-Linux-x86_64-595.112.07.run 423351953 BLAKE2B d25c45818c41bebf1e1e4cc6eb7effb06144ab7ea5d7ebff4e623bfc698354852f2600b91af56040c344ab7319a7f1f5430791b5766ab5f48e0d4a8bd808805b SHA512 f23912837701d11fb1dedd1d99ef466f24e450706ed2df68bc85617c91864b3f6902d36d6e4a0aee2cf02217fd5e9abbbbac54ed4536b5219580a3f4d0eb8eb8 DIST NVIDIA-Linux-x86_64-595.44.15.run 423974279 BLAKE2B 9a13bfbe7612a95041c43a14d7b579675504b28953449c037900b53488ab9f3391d357ef4bc9e642da54c9dee8f3d34a22655daaf9a1fb7de816e5ac9957a788 SHA512 b36af6233c795a6538bc0d1af678546b35330545b82bb7d2f70dcea59f9fd596861acb0cdca68404321716c1657589ba22d37c8cd6c513b320a80c36fb6a4130 DIST NVIDIA-Linux-x86_64-615.78.08.run 529834675 BLAKE2B 0d866b034ce9ca496fc60fff606dad264b35dd6895fb13314ba5071e265de45dd7376950720c2b1033e065cbe34ca09bc8565e04eab83c7d08d06f68037bbe92 SHA512 fb0c16c9812e60ac1f5ca663f03dd84f386ae4ad55beab4655c65fe8d094c98246c5acbcf9e385e93e38dbfb340472712fc80593a8bf0be47f34ff871bf4d83d DIST NVIDIA-kernel-module-source-535.309.01.tar.xz 12537052 BLAKE2B 2bff6a129c7c407de95d140fefefd50c266c7c8dcf938a71690545cff65b3502872645e79d47f9091deac54b7e140bd93a321b18af0b9cb24ca958f033461c53 SHA512 5bd08038823f3f9c39307b441cadc6231d71195adf1835f33e5af794df24be8abed83d3b246edbf5cbaae2379b52ecdd5c975c722001e19fc96054528bada773 DIST NVIDIA-kernel-module-source-580.178.04.tar.xz 22267204 BLAKE2B 7566451a6b13c24bb80476c68bbcd93c0d8dbd0bc4eae2ca5fcda06690a2d386b6a0782aa1836e07061e84bf923fab479746294f229acd4c53e7a6da4a309b9f SHA512 38b5ad1895946287073e5951a88733898a805d02325eb7f23169e61663d43b73bb4db65b0d9648daef11edcc50646320a4e18af6e7584d63cec960fcd3b75de5 DIST NVIDIA-kernel-module-source-580.196.07.tar.xz 22303836 BLAKE2B badfe37049b6e06b5a38cb0ac834262029eccdc22e0749d4faff2fd889a3d52c070d74acb0ec0449fa63c7709de829589c45a693ddcb52a3b77f8b4bff984ad2 SHA512 a520cb76dbdbafca9a9a11fc73abf9711596309b226b9b5cc8f9d69cdb0d7b7db89ac71c92275d470b7a3dd6b945a466742d2990c10a3c39526b43acf21b1440 DIST NVIDIA-kernel-module-source-595.104.02.tar.xz 23810384 BLAKE2B 1466b186c17156280a1e97dab7e72da6b112ed877be281915a33c6d9fa8c664db90059432913d787f31ea605e12ba2f5eaff51eec8949f473e87eb1db32917d0 SHA512 83875be617c3d8a40abe8584603017de0048aa7f4649ee1262cfe5bb6da00ac60de5b0ab45509736254d320a15da2206fd3c6935705f97b490aaf67e11c72a91 +DIST NVIDIA-kernel-module-source-595.112.07.tar.xz 23814324 BLAKE2B 26894e716c6ef859e5ac9b8ac87346b26072aa392fb7a783fca5fba561aa0dd7e156b50cbcb06993b82142c24c35bf66223415484238d05db8be0cee00158506 SHA512 a879974d3b309f73e22475b061bbf3447194ca705d766ddb8e156856765991adf06d151fe3214b020f4a035d00c931c5b8d09188d9711953093aa706698f2dd7 DIST NVIDIA-kernel-module-source-615.78.08.tar.xz 25908104 BLAKE2B 7a73f396965056bf7ca8506e37cb7719bfcc2a79e8c44a7477a038cf39ca7630c6de96b42d8d56e23640d40b80f6fa8dde20bdb843832913de3d0f93f650ce68 SHA512 5ced2c7e3e9df21f8494ea439ac2103307fcbcb295a3f11e5fdf4f1dbeda3f0b13800a06f8147ebd064f98897c24e92f5de82023a8ca43a582694d951c9620c9 DIST nvidia-installer-390.157.tar.bz2 150323 BLAKE2B 8058ca87a6dea956d564af9f7eab9b1fc82b1f2382bd5cb2f6d97cbe2e19292533522d5f8f2eacbeb16520372715fcb72f8f9b0998962af5d4e75522c8d74524 SHA512 93a7ece648602157496c8e27a88864ede341a100f5328a4c1a25faf8f0b94d252060e5e2f71d0c302fd0566c10773c30b76b3b5f431d8039b71c90a7969f7d7e DIST nvidia-installer-470.256.02.tar.bz2 146269 BLAKE2B 4402e725c8fd0157eb84d4ec140a33f97e873014577487ef1e32fb8921cabe79c60ff46532e5bfaabb2b6ac894f7c80086dbab19e61906e2e27346fb85f98829 SHA512 c962dc17d9696256d1ebedcaeb3010994f2ce3895aedc47987599ebfec132a9b9a249d2ece6502b15bd4a93f9b0106aa13487a7da5f64199c274ce11eba27cfd @@ -23,6 +26,7 @@ DIST nvidia-installer-535.309.01.tar.bz2 149086 BLAKE2B 14ed4eed4d3f998bf3c93d7c DIST nvidia-installer-580.178.04.tar.bz2 169996 BLAKE2B 231762680ac9dfb88ff553ad655dc9498b81f1c505d30ecd620609f2e912670c2846b16d05dc85273a4f5ec74bbf717d02f9b976adfa951dbd621a172e8a7174 SHA512 6995905e3c06ed1c284ec1302149f590ebca2278604f0206d543cc3202891044fe2badedf0608e31f522857c431dcd7d35f6c281667b2556fa0b290a9eccb62c DIST nvidia-installer-580.196.07.tar.bz2 169967 BLAKE2B 66832b2e873fb548878904a7861b1970817fc06a39d9e5fecbc10e8db101163f5441f1c37b11954a1d52bec3744df2b61098367afc489c7f2e0f35418df61d62 SHA512 89c6dd0e0999d09049cb34a2609c3f28226ba5d95d7d728d9bf2f72a837752e0f51c144443b763d7f4ee4f6728121f80feb8c66b65c6b9e028f2063cef0fb4c2 DIST nvidia-installer-595.104.02.tar.bz2 172685 BLAKE2B a15646a5b40c51a55417df1570c257b40d591a20fe4bf5c1658c64e597de448cc2b0807ce47b4ef5c654161ebb5ce82184631966e7fb6885c8bfcba5d7eb24b6 SHA512 90adf5b1d6af339d46ee82217b7c1187316be8268577b776e5313907119fd9b8a796e6da33ed687c2b9baa2124dff624f584dcc5ec4a29dcc733e1b27975088f +DIST nvidia-installer-595.112.07.tar.bz2 172363 BLAKE2B 93d1b5d6f6fbf8043903ccccf648000dce23290f348095447e3205a614f67b8304b132fb165077010a41dcc713d91c5b03ca0b774d1ff2f53b514ac79ac898fe SHA512 fc219aed406d9751691b7525f5edb4b3ef29cdf2d6c36c6eddd5ac968a387dbec0fcc5dfc6884f01da14a1539a195060bcfe47c9ec18ed09d04af202b8fd079f DIST nvidia-installer-595.91.07.tar.bz2 172429 BLAKE2B b4128b9075a5b302f825ff70af8ad210030292b920c5f9cb88930c6853e9fa915c9033b80d7ba1ac729a8a8c6c8787f61444419ae0aae47ceeefdefb4984fc19 SHA512 316d278f3f3500f33a92c993accb62333cbd6d719a8e730e28a387d5c80987f4bc7d6b05f0d2897f766d51cb067736e254057a0984d72207c67accb8510233e4 DIST nvidia-installer-615.78.08.tar.bz2 172150 BLAKE2B 72cbbc1faf2406bf1d5b36c5b01e91475b785182389ddeff77e76070a67aa20b519a12fbc409389817453f39173c0253a5dc926adbbc71d80c7ec1e248764bed SHA512 03e2ad38bb07dca362ba3308e2d5a55557b30fa8b7923b7d0ec8a0147e8c59b5d0fff2cf9393ef2b1d66690ac16cb826de308c006c5b4c644e9be0055c190108 DIST nvidia-modprobe-390.157.tar.bz2 35306 BLAKE2B 80d202b39c2f95ec0f909712c2e101483af50124092c32efb33bd98ff58ddfb97b737ff07f1ddc941f688c1b4c26a15f392566d522e18506771f10422fcd571c SHA512 109bbe24a3758b568ea65fe1e9d78f69fae6108ec0497b796c885a3912825fe04ffd7389a3e22987f1a10a2926bf6eaa384faabc59478aa3ee244dd3ac91c6d3 @@ -31,6 +35,7 @@ DIST nvidia-modprobe-535.309.01.tar.bz2 40756 BLAKE2B 4c8d41c3f8d811155aad952e71 DIST nvidia-modprobe-580.178.04.tar.bz2 41668 BLAKE2B 9d7f239a3189db7528a5a9c0f2565f6dc785d48cabb5a2d12857d8fc69cc4ef00a1a6c4f1b75e8a0af989b48764878c3cb80b21756a908a1bb2140ab56376f95 SHA512 80ee138546022b0bca3824a563fe5df26f14337e9ee3df7b28917455525f9ef4606d12d5394f74e53089d7ba3955fa077b1dd3702beb5688c4c6b1d6e796fa68 DIST nvidia-modprobe-580.196.07.tar.bz2 41669 BLAKE2B 490ca02f5027b4a73cdb610ef49c29e20bc2f3b2e11694eb2725891476d3040f5f9fb95692ccab7d9a844aa430955d9faf63b2270ac407206fc0abfea32f03ef SHA512 b975ea7d9d7d6afed625402930999f09085735fb169a735d445f7c7e61ab451d955d40bea6f7785d05296af9998f02c2bab22373056da30ed4e1639446e6012e DIST nvidia-modprobe-595.104.02.tar.bz2 41729 BLAKE2B ac72388d9c628cf601f4f298ec0d7a571e4ddfe3c19a9289c5c4e531c62e82f197d7b1d27f77d4d2f71fe9940f127271929a1c0dfea398fe5eb55d5c31ab740c SHA512 c4e6f843030406eb4cd3f8fff44f574b4cafaa422b7789381f83b991ea32806dde6e0a7c6edd17fab34e5de634669b83a781b16b653d0d3ed599abc4a879aac3 +DIST nvidia-modprobe-595.112.07.tar.bz2 41716 BLAKE2B 1f95b41e195ddde8bcfcc82688f0d0db8f525ad652fb2abfa0730bfd3434e55e399acaf3b0dec2647aeec2f088467f968e116e2dac462570836f18a35e4374e7 SHA512 8e287999c45cab710165ecbd46fb983f8f6daa1b2b0a6578dd2dc1f6833ba5e238401d0f1c9e72e0c52598d978d2d81896566ea5d5b7d694cee4782af34d8dfe DIST nvidia-modprobe-595.91.07.tar.bz2 41725 BLAKE2B 0ceec46af414ea6232014b25c384b01390fd85ca81c39f994f5bafcdc2e3bbf670ed6416fc7d8b8d0ae56bc689106356f956b5f9c4b7e69ff66b5fe3d05729c9 SHA512 9ad2780218119eaa55d2da0c1007ffa73c3262b0e692cae25260f18d993d531e4cacfe83356b4f4f923e62cc80903915073d0225810656798a9462c75a769a98 DIST nvidia-modprobe-615.78.08.tar.bz2 41943 BLAKE2B 7ee4f83738020491488b12e861ba4bfcd8c1106583360caa6cb766051e3d75cc42f3aa804dc0e2962ac66fb5f93458ad1948c739a0942a1d0c6ca1a59c9f934e SHA512 eb5bb4535dcc5298d345b16a8d4a9772d2b18f342b1790bbf455774049c1dc1f8aacd2b40d9285dc56952b3a04281cb620b0ec83f11ef72f616f51e506f38ebb DIST nvidia-persistenced-390.157.tar.bz2 48654 BLAKE2B 2cf6c92da90acff55e8c180a06b0c6a29d48be9fd8ca3c541202fd14f0697220fba6b66452942aa7ae7cc8821f5c5eb9fa8f7a31d2248624e5f9141d83a176b8 SHA512 70f0707ed6f2b877e69ef90cf782f66cbc9d2071db53ca09ceb7b89427b0fe176708517340621fa251539b7d481b238adeeb60261674eed74de1f62db6dbb72e @@ -39,6 +44,7 @@ DIST nvidia-persistenced-535.309.01.tar.bz2 48569 BLAKE2B 5756b1335d6a1932529bab DIST nvidia-persistenced-580.178.04.tar.bz2 62230 BLAKE2B 387f778b3ec3e48112e35bd3cd3e161527005ff5dc52774755acbcee5e18a1d02f7dd88c7a58d2632863262ff0f4a7b1e091c60caf8f8823d49fe38e3791a0f9 SHA512 2b9e26833e63a81401d3c83c0379f2d86bb0f8f44e1fcbe3ff235739f284a0adf46d76520d802274d8e19df0de656d9a2e9b52dac539ced6ca7bb7b1f4be6629 DIST nvidia-persistenced-580.196.07.tar.bz2 62228 BLAKE2B 5743acd5bfb20a55b86312460a71bda0d73e6422390457839f809862f0a7cbdf87bc80255e9da11a08317ee109c44f6c5b830be5520648ea0ef1da82d9456b93 SHA512 4876712dcefd691dcee018fa71c09bfbbfced420c5e8661de96fddf3f52384a88d853a4a61fe861478d3f6603f4b339530f0d2439b62a505b36721c9d58aeec4 DIST nvidia-persistenced-595.104.02.tar.bz2 62545 BLAKE2B 7212c5fe14af130825f60290b324afd711d4ec664a3f5b3c37adc40f02887cf639a555ad8c087117b36e0aff212c5312e97afc0d87d494e8fde88a0059c72e6d SHA512 ac88efaa447106c3205468fdeb2513ef87048579ebd21ac9b19e006606523f653ac58147597ed1a26a69df7885868316ca355686b1418a0e2479451070762282 +DIST nvidia-persistenced-595.112.07.tar.bz2 62504 BLAKE2B a3748640935050a2f4eef05d1f2ff7a502c57bc0f99489a30bf156c9a2b37dceb9f0a328346de6e376a5ad01670471d04ee0d51dbb6b85aac87166161f5d2bdd SHA512 23572177113c5de96125fa9b266f1ce93b08e5653ee6527292f3444f056acdb26570a83a15b8fc28d31bb253f4b01d3facd16e0bafea00137c2a4c491450834f DIST nvidia-persistenced-595.91.07.tar.bz2 62519 BLAKE2B 9da9fbacebc6c3c1b56621d7676ec78cf61081d824dbcabf8599168ce673444adf736b6cd62129b0f87d619f79c6d5f0bbce311f5ecba346cda8881bb7bce6f8 SHA512 b9f9c69fb85ce22676789f5d88c460dee1961c42e1bf4ad3e677ac800715f888ded41590fe287b2c5972e3b89982e7b4d6290ccdf7ff2fa870f3a5ef3a7ae9ae DIST nvidia-persistenced-615.78.08.tar.bz2 64040 BLAKE2B 21c49bc244d8ec6e1734a366c74b525807bbea71b719ebee9c9c4f7af4937fdedbf0e814566855848fbedf7165a375f69626691358941e58ae51ea2c1e5f372b SHA512 f0b8b02cd16875404974e55411aa6750872962b8b518ac522b10c6a67778a404a721d19b80beca80ac91d0b0363b9bd92591f1ea14e99278e4478d1d29103ebe DIST nvidia-settings-390.157.tar.bz2 1108938 BLAKE2B d9ec1ab5d7f157d74627272adf752a671dd17e2ede02069437b6180b927fe315b15cc6416d584ab2f038b76fe01e5b64514a834f24d88ac16676e55205fdecdd SHA512 b16699009bf56a1ff3b623a528c58061c4f5a2c5c4a373756a07bcd667743a1df7c733a572d2d0baa835a904ac2fda8a5c85890dcc00c3a0f223e0467aea902d @@ -47,6 +53,7 @@ DIST nvidia-settings-535.309.01.tar.bz2 1094768 BLAKE2B 6a951e7e8e1ccd968a0727d2 DIST nvidia-settings-580.178.04.tar.bz2 1132866 BLAKE2B d17b698a73be1c1346eb2ba10f50432250b4167cffdc8cde40610f2dc733b9ff6e1ba5214ee0c0509c59e82f6984c1ab686d39379f7a00edba94e45166c6fa97 SHA512 2d336afd1ebbe6272404bd43f8756ac35932a3d7650c6d524fae0d77ee33cffc71cba94f6fe038c001964d5de20d7814d1e6092c062fb9cea946e8b60766a140 DIST nvidia-settings-580.196.07.tar.bz2 1132924 BLAKE2B 4c045ec4b743528d2004fe8dc38058805493512a4d3ae1c02174f7c06314f98b6a1c9d5ccbfa65c0ed34500a40b180ba87cb7aba30aa4f3820adb0bc36c12264 SHA512 4a9ad25d84efc099055e031c846b32bb277f1d012c5b688e51eea552d6ede9feafe3089f73f3b3bec6704d43b54c39def7f2bbf35bf26eca1115f65ba2b00e07 DIST nvidia-settings-595.104.02.tar.bz2 1136465 BLAKE2B 78735592dee4b0131a646880b8c0ab8acea65153941e3a49ac095ce7fe4523c3adf45deb5eb24363a14f0e76d22bceb6e95d1904232f61153fbaa82d08383d4d SHA512 0fd8a2488dff64e682ccbb2c596a1304041467e3d87f8b093b7dec81ccdadde1dd582a1329c768b0520ca850f2925eb07e370ed0f5e9e3e779b99d151bf561c3 +DIST nvidia-settings-595.112.07.tar.bz2 1136323 BLAKE2B c9a5113f052f03717f2b1fd81cae69cac645a8c380c1ce4bb758fdca90ae4043f2fa2b4d7205cc021593435d4a681a3c09c83f35b10012ed06d8b9279a38b08c SHA512 8d04f9b0558c41db2e5cc00b1759af26c12c1d871e22e075a589cd3050819e40b0ecefdd516d7ef2c2c14bc359b1fa0ae2b9df56ec35c167d18f805870b084a7 DIST nvidia-settings-595.91.07.tar.bz2 1136346 BLAKE2B 56e4ed3d51b3210935f49918e9975a8ef7ae0a199ff45f9512ed5058a379fb8e7402e599965da071481e3501fa8081b0ef034e36981ab8816636e7b6b49e8ee7 SHA512 0a412f7923c4b8227b154106280165f9930b41c137beddf5f69e49e70b348ede664f859a51159b7d3e5eb6c0c271ba9a1c1a5930c7f652dbc861cdea9c59265d DIST nvidia-settings-615.78.08.tar.bz2 1148466 BLAKE2B 62e698e984847b941ccdf08904c98fd0811653b66a3731b8990635c4ebdfa9a5a20f6e1298188f36cfa6170f2ee895c6fc812bbf79e6f8f86e82b09caf5ef760 SHA512 fa01f1f2a1b3931d7ab16f7625cc6bf472053246dbbd41301b8ff697f004d854c69551586bc4d2b3e9511c981c738d4e24a0979efcb1b855962393f475fa4bfd DIST nvidia-xconfig-390.157.tar.bz2 107399 BLAKE2B 0cbb3b1ba508ddc3ba4a161c290b31bf7a67f67880bdbd01171a962c5fa97ca945b1ef17932403f8589309380743f1da632c728d8accb6d4e56dbd895e7486bb SHA512 50b390811c9bf091b14add41f886925b17496b41006b7d3981d2398caa65cf5db8a579c8cebed78278384bf3acc629befb07b54339c61abcc75852c0dcb8b8ca @@ -55,6 +62,7 @@ DIST nvidia-xconfig-535.309.01.tar.bz2 111085 BLAKE2B 7a2f55989f93bbfecef0ae0c76 DIST nvidia-xconfig-580.178.04.tar.bz2 111557 BLAKE2B 0ad4c36edafa3086ccd0189ad0b1768cdddd7159b8bc2153830ec95ec4b06b5d41215dd1c2408bb5ea2a529ea54996d9be952ad1effd437ce7eb82926e56dd74 SHA512 9cb3060e62c56686dab3d6ba75ac42998a5a16ac34b2785d47c15f50df67a85eefb475507fd0a605f4e60203e0cdecbe38a7d8d9e579687cb529d72806874d01 DIST nvidia-xconfig-580.196.07.tar.bz2 111524 BLAKE2B b27d13e5ed5f9aa198b4437bf8ccdeaafe513c4331b93e77d4c0d475c7ebc2516e658fe1bfe5d54a196da55c5d3e683438e396da577af44173a67b074f747388 SHA512 f60e76aa615675084ce2d8fe8586f05df604d848b636b75769eb4f9da24a3b92c8a3c7eb174e7e5aa747092fbb580d8b46884a87d2470c7845fe3730094e5e17 DIST nvidia-xconfig-595.104.02.tar.bz2 111065 BLAKE2B 962277dd2e663a60e935d41bdfd32f9a6fdf0cd67a8c28b2b816c0034fa81ce9b62e696357a3f30863d997d5bb69b28595d1fd6e10cf22f4a06fa48461dbf523 SHA512 27cfcdbb7f4a2b5adc6c7de17a9d8e9fe47ba048dac2d665cfb03b039bc5b7903ee5f9988b9ee8566086ec9424eabf5b2ff4e5a1dcc8139e068328fd6fc4a429 +DIST nvidia-xconfig-595.112.07.tar.bz2 111095 BLAKE2B 83695b7eb9067419877244d75545935c61c986c57c06998f887d5b57680637d0ad0f59721b2b59f88ed5e0282e19821c41151ea9f6d0a65ddd28519d67c13cc2 SHA512 80706fe0790bb88353fde50a628805e49a15f50a516f79ebc77ca4aa4e994d8313768d7368a6b623211bab265b2de8120c0efc3be15c0de87d074b99f4f4cd35 DIST nvidia-xconfig-595.91.07.tar.bz2 111007 BLAKE2B 30f556a5b8c41cedc637f52064c402e39590d5df0381a8b2b74cd5bfa3c94a42bb6b95ff68898e240eef36736205a592cacf1595e8ea054b5290314bce8bcdea SHA512 6675a2f56fb2574f55b7342c1c3da221f5b1dc55329018075f71903706cbbf8b9e2ba91190d0c021e66a03e758c7cad1f5a1f1b266653849fe85773092ebc6e1 DIST nvidia-xconfig-615.78.08.tar.bz2 111311 BLAKE2B 0d1d934f9f27fad53a7a4f50882edf33932f4ada19483c299f8c5521a0d16dc0a998f91ecd59915e61672f3653139e1ffe71566d46c2d3838e4de1b13ceb3bf7 SHA512 bd779fa9ef0edea8985bd7d7bcc742cd1c648411715a7fa3e5bca7db780dc84b2d48ee62323120eeba1ceca9f7454515409cfc51453ad1821c19757956bf950d DIST open-gpu-kernel-modules-595.44.15.tar.gz 24814978 BLAKE2B 896ff15e29469cf358765aa378d7c8fcbaf0dbaba78cf1815b0b2c49b0e67a9dacd6756356007eff9c1d899a832a3d703bae3e8bf527330db4a2b80fdc0d2173 SHA512 d3893088fdeecc6e128fed548df87b18d87c38d11274333e4f21b5ecf1a912a6a058f76fdcda4d969b670789f3fdc3eba54283a506caf043696a778a98388611 diff --git a/x11-drivers/nvidia-drivers/nvidia-drivers-595.112.07.ebuild b/x11-drivers/nvidia-drivers/nvidia-drivers-595.112.07.ebuild new file mode 100644 index 000000000000..e534e1bceeca --- /dev/null +++ b/x11-drivers/nvidia-drivers/nvidia-drivers-595.112.07.ebuild @@ -0,0 +1,555 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +MODULES_OPTIONAL_IUSE=+modules +inherit desktop dot-a eapi9-pipestatus eapi9-ver flag-o-matic linux-mod-r1 +inherit readme.gentoo-r1 systemd toolchain-funcs unpacker user-info + +MODULES_KERNEL_MAX=7.3 +NV_URI="https://download.nvidia.com/XFree86/" + +DESCRIPTION="NVIDIA Accelerated Graphics Driver" +HOMEPAGE="https://www.nvidia.com/" +SRC_URI=" + amd64? ( ${NV_URI}Linux-x86_64/${PV}/NVIDIA-Linux-x86_64-${PV}.run ) + arm64? ( ${NV_URI}Linux-aarch64/${PV}/NVIDIA-Linux-aarch64-${PV}.run ) + $(printf "${NV_URI}%s/%s-${PV}.tar.bz2 " \ + nvidia-{installer,modprobe,persistenced,settings,xconfig}{,}) + ${NV_URI}NVIDIA-kernel-module-source/NVIDIA-kernel-module-source-${PV}.tar.xz +" +# nvidia-installer is unused but here for GPL-2's "distribute sources" +S=${WORKDIR} + +LICENSE=" + NVIDIA-2025 Apache-2.0 Boost-1.0 BSD BSD-2 GPL-2 MIT ZLIB + curl openssl public-domain +" +SLOT="0/${PV%%.*}" +KEYWORDS="-* ~amd64 ~arm64" +IUSE="+X abi_x86_32 abi_x86_64 persistenced powerd +static-libs +tools wayland" + +COMMON_DEPEND=" + acct-group/video + X? ( x11-libs/libpciaccess ) + persistenced? ( + acct-user/nvpd + net-libs/libtirpc:= + ) + tools? ( + >=app-accessibility/at-spi2-core-2.46:2 + dev-libs/glib:2 + dev-libs/jansson:= + media-libs/harfbuzz:= + x11-libs/cairo + x11-libs/gdk-pixbuf:2 + x11-libs/gtk+:3[X] + x11-libs/libX11 + x11-libs/libXext + x11-libs/libXxf86vm + x11-libs/pango + ) +" +# egl-wayland2: nvidia currently ships both versions so, to ensure +# everything works properly, depend on both at same time for now +# (may use one or the other depending on setup) +RDEPEND=" + ${COMMON_DEPEND} + || ( + dev-libs/openssl-compat:3 + dev-libs/openssl:0/3 + ) + sys-libs/glibc + X? ( + media-libs/libglvnd[X,abi_x86_32(-)?] + x11-libs/libX11[abi_x86_32(-)?] + x11-libs/libXext[abi_x86_32(-)?] + ) + powerd? ( sys-apps/dbus[abi_x86_32(-)?] ) + wayland? ( + >=gui-libs/egl-gbm-1.1.1-r2[abi_x86_32(-)?] + >=gui-libs/egl-wayland-1.1.13.1[abi_x86_32(-)?] + gui-libs/egl-wayland2[abi_x86_32(-)?] + X? ( gui-libs/egl-x11[abi_x86_32(-)?] ) + ) +" +DEPEND=" + ${COMMON_DEPEND} + static-libs? ( + x11-base/xorg-proto + x11-libs/libX11 + x11-libs/libXext + ) + tools? ( + dev-util/vulkan-headers + media-libs/libglvnd + sys-apps/dbus + x11-base/xorg-proto + x11-libs/libXrandr + x11-libs/libXv + x11-libs/libvdpau + ) +" +BDEPEND=" + sys-devel/m4 + virtual/pkgconfig +" + +# there is some non-prebuilt exceptions but rather not maintain a list +QA_PREBUILT="lib/firmware/* usr/bin/* usr/lib*" + +PATCHES=( + "${FILESDIR}"/nvidia-modprobe-390.141-uvm-perms.patch + "${FILESDIR}"/nvidia-settings-530.30.02-desktop.patch +) + +pkg_setup() { + use modules && [[ ${MERGE_TYPE} != binary ]] || return + + # do early before linux-mod-r1 so can use chkconfig to setup CONFIG_CHECK + get_version + require_configured_kernel + + local CONFIG_CHECK=" + PROC_FS + ~DRM_KMS_HELPER + ~SYSVIPC + ~!LOCKDEP + ~!PREEMPT_RT + ~!SLUB_DEBUG_ON + !DEBUG_MUTEXES + $(usev amd64 'X86_PAT') + $(usev powerd '~CPU_FREQ') + " + + kernel_is -ge 6 11 && linux_chkconfig_present DRM_FBDEV_EMULATION && + CONFIG_CHECK+=" DRM_TTM_HELPER" + + linux_chkconfig_present DRM_KMS_HELPER && + CONFIG_CHECK+=" DRM_FBDEV_EMULATION" + + local drm_helper_msg="Cannot be directly selected in the kernel's config menus, and may need + selection of a DRM device even if unused, e.g. CONFIG_DRM_QXL=m or + DRM_AMDGPU=m (among others, consult the kernel config's help), can + also use DRM_NOUVEAU=m as long as built as module *not* built-in." + local ERROR_DRM_KMS_HELPER="CONFIG_DRM_KMS_HELPER: is not set but is needed for wayland support, + tty takeover (e.g. for simpledrm), and config-less Xorg auto-detection. + ${drm_helper_msg}" + local ERROR_DRM_TTM_HELPER="CONFIG_DRM_TTM_HELPER: is not set but is needed to compile when using + kernel version 6.11.x or newer while DRM_FBDEV_EMULATION is set. + ${drm_helper_msg}" + local ERROR_PREEMPT_RT="CONFIG_PREEMPT_RT: is set but is unsupported by NVIDIA upstream and + will fail to build unless the env var IGNORE_PREEMPT_RT_PRESENCE=1 is + set. Please do not report issues if run into e.g. kernel panics while + ignoring this." + + linux-mod-r1_pkg_setup +} + +src_prepare() { + # make patches usable across versions + rm nvidia-modprobe && mv nvidia-modprobe{-${PV},} || die + rm nvidia-persistenced && mv nvidia-persistenced{-${PV},} || die + rm nvidia-settings && mv nvidia-settings{-${PV},} || die + rm nvidia-xconfig && mv nvidia-xconfig{-${PV},} || die + mv NVIDIA-kernel-module-source-${PV} kernel-module-source || die + + default + + sed 's/__USER__/nvpd/' \ + nvidia-persistenced/init/systemd/nvidia-persistenced.service.template \ + > "${T}"/nvidia-persistenced.service || die + + # use alternative vulkan icd option if USE=-X (bug #909181) + use X || sed -i 's/"libGLX/"libEGL/' nvidia_{layers,icd}.json || die + + # makefile attempts to install wayland library even if not built + use wayland || sed -i 's/ WAYLAND_LIB_install$//' \ + nvidia-settings/src/Makefile || die +} + +src_compile() { + tc-export AR CC CXX LD OBJCOPY OBJDUMP PKG_CONFIG + + # extra flags for the libXNVCtrl.a static library + local xnvflags=-fPIC #840389 + tc-is-lto && xnvflags+=" $(test-flags-CC -ffat-lto-objects)" + + # same as uname -m + local target_arch + case ${ARCH} in + amd64) target_arch=x86_64;; + arm64) target_arch=aarch64;; + *) die "Unrecognised architecture: ${ARCH}";; + esac + + NV_ARGS=( + PREFIX="${EPREFIX}"/usr + HOST_CC="$(tc-getBUILD_CC)" + HOST_LD="$(tc-getBUILD_LD)" + BUILD_GTK2LIB= + NV_USE_BUNDLED_LIBJANSSON=0 + NV_VERBOSE=1 DO_STRIP= MANPAGE_GZIP= OUTPUTDIR=out + TARGET_ARCH="${target_arch}" + WAYLAND_AVAILABLE=$(usex wayland 1 0) + XNVCTRL_CFLAGS="${xnvflags}" + ) + + if use modules; then + local o_cflags=${CFLAGS} o_cxxflags=${CXXFLAGS} o_ldflags=${LDFLAGS} + + # environment flags are normally unused for modules, but nvidia uses + # them for building the formerly closed "blob" and it is a bit fragile + filter-flags -fno-plt #912949 + filter-lto + CC=${KERNEL_CC} CXX=${KERNEL_CXX} strip-unsupported-flags + LDFLAGS=$(raw-ldflags) + + # the "blob" uses C++ which is an issue if there is debug symbols + # when running pahole, there is a pahole.sh wrapper that tries to + # exclude C++ but it did not seem to be enough last time tried + linux_chkconfig_present DEBUG_INFO_BTF_MODULES && append-flags -g0 + + : video:kernel-module-source:kernel-module-source/kernel-open + local modlist=( nvidia{,-drm,-modeset,-peermem,-uvm}=${_} ) + local modargs=( + IGNORE_CC_MISMATCH=yes NV_VERBOSE=1 + SYSOUT="${KV_OUT_DIR}" SYSSRC="${KV_DIR}" + TARGET_ARCH="${target_arch}" + + # kernel takes "x86" and "x86_64" as meaning the same, but nvidia + # makes the distinction (since 550.135) and is not happy with "x86" + # TODO?: it should be ok/better for tc-arch-kernel to do x86_64 + $(usev amd64 ARCH=x86_64) + ) + + # temporary workaround for bug #914468 + addpredict "${KV_OUT_DIR}" + + linux-mod-r1_src_compile + CFLAGS=${o_cflags} CXXFLAGS=${o_cxxflags} LDFLAGS=${o_ldflags} + fi + + emake "${NV_ARGS[@]}" -C nvidia-modprobe + use persistenced && emake "${NV_ARGS[@]}" -C nvidia-persistenced + use X && emake "${NV_ARGS[@]}" -C nvidia-xconfig + + if use tools; then + # avoid noisy *very* noisy logs with deprecation warnings + CFLAGS="-Wno-deprecated-declarations ${CFLAGS}" \ + emake "${NV_ARGS[@]}" -C nvidia-settings + elif use static-libs; then + # pretend GTK+3 is available, not actually used (bug #880879) + emake "${NV_ARGS[@]}" BUILD_GTK3LIB=1 \ + -C nvidia-settings/src out/libXNVCtrl.a + fi +} + +src_install() { + local libdir=$(get_libdir) libdir32=$(ABI=x86 get_libdir) + + NV_ARGS+=( DESTDIR="${D}" LIBDIR="${ED}"/usr/${libdir} ) + + local -A paths=( + [APPLICATION_PROFILE]=/usr/share/nvidia + [CUDA_ICD]=/etc/OpenCL/vendors + [EGL_EXTERNAL_PLATFORM_JSON]=/usr/share/egl/egl_external_platform.d + [FIRMWARE]=/lib/firmware/nvidia/${PV} + [GBM_BACKEND_LIB_SYMLINK]=/usr/${libdir}/gbm + [GLVND_EGL_ICD_JSON]=/usr/share/glvnd/egl_vendor.d + [OPENGL_DATA]=/usr/share/nvidia + [VULKANSC_ICD_JSON]=/usr/share/vulkansc + [VULKAN_ICD_JSON]=/usr/share/vulkan + [WINE_LIB]=/usr/${libdir}/nvidia/wine + [XORG_OUTPUTCLASS_CONFIG]=/usr/share/X11/xorg.conf.d + + [GLX_MODULE_SHARED_LIB]=/usr/${libdir}/xorg/modules/extensions + [GLX_MODULE_SYMLINK]=/usr/${libdir}/xorg/modules + [XMODULE_SHARED_LIB]=/usr/${libdir}/xorg/modules + ) + + local skip_files=( + $(usev !X "libGLX_nvidia libglxserver_nvidia") + libGLX_indirect # non-glvnd unused fallback + libnvidia-{gtk,wayland-client} nvidia-{settings,xconfig} # from source + libnvidia-egl-gbm 15_nvidia_gbm # gui-libs/egl-gbm + libnvidia-egl-wayland 10_nvidia_wayland # gui-libs/egl-wayland + libnvidia-egl-wayland2 09_nvidia_wayland2 # gui-libs/egl-wayland2 + libnvidia-egl-xcb 20_nvidia_xcb.json # gui-libs/egl-x11 + libnvidia-egl-xlib 20_nvidia_xlib.json # gui-libs/egl-x11 + libnvidia-pkcs11.so # using the openssl3 version instead + ) + local skip_modules=( + $(usev !X "nvfbc vdpau xdriver") + $(usev !modules gsp) + $(usev !powerd nvtopps) + installer nvpd # handled separately / built from source + ) + local skip_types=( + GLVND_LIB GLVND_SYMLINK EGL_CLIENT.\* GLX_CLIENT.\* # media-libs/libglvnd + OPENCL_WRAPPER.\* # virtual/opencl + DOCUMENTATION DOT_DESKTOP .\*_SRC DKMS_CONF SYSTEMD_UNIT # handled separately / unused + ) + + local DOCS=( + README.txt NVIDIA_Changelog supported-gpus/supported-gpus.json + nvidia-settings/doc/{FRAMELOCK,NV-CONTROL-API}.txt + ) + local HTML_DOCS=( html/. ) + einstalldocs + + local DISABLE_AUTOFORMATTING=yes + local DOC_CONTENTS="\ +Trusted users should be in the 'video' group to use NVIDIA devices. +You can add yourself by using: gpasswd -a my-user video\ +$(usev modules " + +Like all out-of-tree kernel modules, it is necessary to rebuild +${PN} after upgrading or rebuilding the Linux kernel +by for example running \`emerge @module-rebuild\`. Alternatively, +if using a distribution kernel (sys-kernel/gentoo-kernel{,-bin}), +this can be automated by setting USE=dist-kernel globally. + +Loaded kernel modules also must not mismatch with the installed +${PN} version (excluding -r revision), meaning should +ensure \`eselect kernel list\` points to the kernel that will be +booted before building and preferably reboot after upgrading +${PN} (the ebuild will emit a warning if mismatching). + +See '${EPREFIX}/etc/modprobe.d/nvidia.conf' for modules options.")\ +$(use amd64 && usev !abi_x86_32 " + +Note that without USE=abi_x86_32 on ${PN}, 32bit applications +(typically using wine / steam) will not be able to use GPU acceleration.") + +For additional information or for troubleshooting issues, please see +https://wiki.gentoo.org/wiki/NVIDIA/nvidia-drivers and NVIDIA's own +documentation that is installed alongside this README." + readme.gentoo_create_doc + + if use modules; then + linux-mod-r1_src_install + + insinto /etc/modprobe.d + newins "${FILESDIR}"/nvidia-595.conf nvidia.conf + + # used for gpu verification with binpkgs (not kept, see pkg_preinst) + insinto /usr/share/nvidia + doins supported-gpus/supported-gpus.json + fi + + emake "${NV_ARGS[@]}" -C nvidia-modprobe install + fowners :video /usr/bin/nvidia-modprobe #505092 + fperms 4710 /usr/bin/nvidia-modprobe + + if use persistenced; then + emake "${NV_ARGS[@]}" -C nvidia-persistenced install + newconfd "${FILESDIR}"/nvidia-persistenced.confd nvidia-persistenced + newinitd "${FILESDIR}"/nvidia-persistenced.initd nvidia-persistenced + systemd_dounit "${T}"/nvidia-persistenced.service + fi + + if use tools; then + emake "${NV_ARGS[@]}" -C nvidia-settings install + + doicon nvidia-settings/doc/nvidia-settings.png + domenu nvidia-settings/doc/nvidia-settings.desktop + + exeinto /etc/X11/xinit/xinitrc.d + newexe "${FILESDIR}"/95-nvidia-settings-r1 95-nvidia-settings + fi + + if use static-libs; then + dolib.a nvidia-settings/src/out/libXNVCtrl.a + strip-lto-bytecode + + insinto /usr/include/NVCtrl + doins nvidia-settings/src/libXNVCtrl/NVCtrl{Lib,}.h + fi + + use X && emake "${NV_ARGS[@]}" -C nvidia-xconfig install + + # mimic nvidia-installer by reading .manifest to install files + # 0:file 1:perms 2:type 3+:subtype/arguments -:module + local m into + while IFS=' ' read -ra m; do + ! [[ ${#m[@]} -ge 2 && ${m[-1]} =~ MODULE: ]] || + [[ " ${m[0]##*/}" =~ ^(\ ${skip_files[*]/%/.*|\\} )$ ]] || + [[ " ${m[2]}" =~ ^(\ ${skip_types[*]/%/|\\} )$ ]] || + has ${m[-1]#MODULE:} "${skip_modules[@]}" && continue + + case ${m[2]} in + MANPAGE) + gzip -dc ${m[0]} | newman - ${m[0]%.gz} + pipestatus || die + continue + ;; + GBM_BACKEND_LIB_SYMLINK) m[4]=../${m[4]};; # missing ../ + VDPAU_SYMLINK) m[4]=vdpau/; m[5]=${m[5]#vdpau/};; # .so to vdpau/ + esac + + if [[ -v 'paths[${m[2]}]' ]]; then + into=${paths[${m[2]}]} + elif [[ ${m[2]} == EXPLICIT_PATH ]]; then + into=${m[3]} + elif [[ ${m[2]} == *_BINARY ]]; then + into=/usr/bin + elif [[ ${m[3]} == COMPAT32 ]]; then + use abi_x86_32 || continue + into=/usr/${libdir32} + elif [[ ${m[2]} == *_@(LIB|SYMLINK) ]]; then + into=/usr/${libdir} + else + die "No known installation path for ${m[0]}" + fi + [[ ${m[3]: -2} == ?/ ]] && into+=/${m[3]%/} + [[ ${m[4]: -2} == ?/ ]] && into+=/${m[4]%/} + + if [[ ${m[2]} =~ _SYMLINK$ ]]; then + [[ ${m[4]: -1} == / ]] && m[4]=${m[5]} + dosym ${m[4]} ${into}/${m[0]} + continue + fi + # avoid portage warning due to missing soname links in manifest + [[ ${m[0]} =~ ^libnvidia-ngx.so ]] && + dosym ${m[0]} ${into}/${m[0]%.so*}.so.1 + + printf -v m[1] %o $((m[1] | 0200)) # 444->644 + insopts -m${m[1]} + insinto ${into} + doins ${m[0]} + done < .manifest || die + insopts -m0644 # reset + + # MODULE:installer non-skipped extras + # (sleep services skipped, obsoleted by NVreg_UseKernelSuspendNotifiers=1) + dobin nvidia-bug-report.sh + + insinto /usr/share/nvidia/files.d + doins sandboxutils-filelist.json + + # needed with >=systemd-256 or may fail to resume with some setups + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1072722 + : "$(systemd_get_systemunitdir)" + local unitdir=${_#"${EPREFIX}"} + insinto "${unitdir}"/systemd-homed.service.d + newins - 10-nvidia.conf <<-EOF + [Service] + Environment=SYSTEMD_HOME_LOCK_FREEZE_SESSION=false + EOF + insinto "${unitdir}"/systemd-suspend.service.d + newins systemd/system/systemd-suspend.service.d/nvidia-suspend-nofreeze.conf 10-nvidia.conf + dosym -r "${unitdir}"/systemd-{suspend,hibernate}.service.d/10-nvidia.conf + dosym -r "${unitdir}"/systemd-{suspend,hybrid-sleep}.service.d/10-nvidia.conf + dosym -r "${unitdir}"/systemd-{suspend,suspend-then-hibernate}.service.d/10-nvidia.conf + + # MODULE:powerd extras + if use powerd; then + newinitd "${FILESDIR}"/nvidia-powerd.initd nvidia-powerd #923117 + systemd_dounit systemd/system/nvidia-powerd.service + + insinto /usr/share/dbus-1/system.d + doins nvidia-dbus.conf + fi + + # symlink non-versioned so nvidia-settings can use it even if misdetected + dosym nvidia-application-profiles-${PV}-key-documentation \ + ${paths[APPLICATION_PROFILE]}/nvidia-application-profiles-key-documentation + + # don't attempt to strip firmware files (silences errors) + dostrip -x ${paths[FIRMWARE]} + + # sandbox issues with /dev/nvidiactl and others (bug #904292,#921578) + # are widespread and sometime affect revdeps of packages built with + # USE=opencl/cuda making it hard to manage in ebuilds (minimal set, + # ebuilds should handle manually if need others or addwrite) + insinto /etc/sandbox.d + newins - 20nvidia <<<'SANDBOX_PREDICT="/dev/nvidiactl:/dev/nvidia-caps:/dev/char"' + + # dracut does not use /etc/modprobe.d if hostonly=no, but want to make sure + # our settings are used for bug 932781#c8 and nouveau blacklist if either + # modules are included (however, just best-effort without initramfs regen) + if use modules; then + echo "install_items+=\" ${EPREFIX}/etc/modprobe.d/nvidia.conf \"" >> \ + "${ED}"/usr/lib/dracut/dracut.conf.d/10-${PN}.conf || die + fi +} + +pkg_preinst() { + use modules || return + + # set video group id based on live system (bug #491414) + local g=$(egetent group video | cut -d: -f3) + [[ ${g} =~ ^[0-9]+$ ]] || die "Failed to determine video group id (got '${g}')" + sed -i "s/@VIDEOGID@/${g}/" "${ED}"/etc/modprobe.d/nvidia.conf || die + + # try to find driver mismatches using temporary supported-gpus.json + for g in $(grep -l 0x10de /sys/bus/pci/devices/*/vendor 2>/dev/null); do + g=$(grep -io "\"devid\":\"$(<${g%vendor}device)\"[^}]*branch\":\"[0-9]*" \ + "${ED}"/usr/share/nvidia/supported-gpus.json 2>/dev/null) + if [[ ${g} ]]; then + g=$((${g##*\"}+1)) + if ver_test -ge ${g}; then + NV_LEGACY_MASK=">=${CATEGORY}/${PN}-${g}" + break + fi + fi + done + rm "${ED}"/usr/share/nvidia/supported-gpus.json || die +} + +pkg_postinst() { + linux-mod-r1_pkg_postinst + + readme.gentoo_print_elog + + if [[ -r /proc/driver/nvidia/version && + $(</proc/driver/nvidia/version) != *" ${PV} "* ]]; then + ewarn "\nCurrently loaded NVIDIA modules do not match the newly installed" + ewarn "libraries and may prevent launching GPU-accelerated applications." + if use modules; then + ewarn "Easiest way to fix this is normally to reboot. If still run into issues" + ewarn "(e.g. API mismatch messages in the \`dmesg\` output), please verify" + ewarn "that the running kernel is ${KV_FULL} and that (if used) the" + ewarn "initramfs does not include NVIDIA modules (or at least, not old ones)." + fi + fi + + if [[ $(</proc/cmdline) == *slub_debug=[!-]* ]]; then + ewarn "\nDetected that the current kernel command line is using 'slub_debug='," + ewarn "this may lead to system instability/freezes with this version of" + ewarn "${PN}. Bug: https://bugs.gentoo.org/796329" + fi + + if [[ -v NV_LEGACY_MASK ]]; then + ewarn "\n***WARNING***" + ewarn "\nYou are installing a version of ${PN} known not to work" + ewarn "with a GPU of the current system. If unwanted, add the mask:" + if [[ -d ${EROOT}/etc/portage/package.mask ]]; then + ewarn "\n echo '${NV_LEGACY_MASK}' > ${EROOT}/etc/portage/package.mask/${PN}" + else + ewarn "\n echo '${NV_LEGACY_MASK}' >> ${EROOT}/etc/portage/package.mask" + fi + ewarn "\n...then downgrade to a legacy[1] branch if possible (not all old versions" + ewarn "are available or fully functional, may need to consider nouveau[2])." + ewarn "\n[1] https://www.nvidia.com/object/IO_32667.html" + ewarn "[2] https://wiki.gentoo.org/wiki/Nouveau" + fi + + if ver_replacing -lt 595; then + elog "\n>=${PN}-595 has changes that may or may not need attention:" + elog "1. USE=kernel-open was removed and is now always enabled. If for some" + elog " reason you really need the closed variant (e.g. Runtime D3 on Turing" + elog " GPUs), please use LTS 580.xx instead as Gentoo has no intention to" + elog " support both variants anymore going forward" + elog "2. systemd/elogind sleep services (nvidia-sleep.sh) were tentatively" + elog " removed and replaced by setting NVreg_UseKernelSuspendNotifiers=1 in" + elog " ${EROOT}/etc/modprobe.d/nvidia.conf. If using a non-default custom" + elog " nvidia.conf, please ensure the option is set. Also, systemd users" + elog " may want to ensure that they do not have old sleep/suspend/resume" + elog " *nvidia* files in ${EROOT}/etc/systemd to avoid potential issues." + elog "3. nvidia-drm.modeset=1 was removed from nvidia.conf because it is now" + elog " default enabled regardless (new NVIDIA default)" + fi +} diff --git a/x11-libs/libX11/Manifest b/x11-libs/libX11/Manifest index f2b625b50fc7..93cdd43734c1 100644 --- a/x11-libs/libX11/Manifest +++ b/x11-libs/libX11/Manifest @@ -1 +1,2 @@ DIST libX11-1.8.13.tar.xz 1803464 BLAKE2B 1ccb826184ed67d13e254d5cf20a40d2994837ffab6c05c647865927589e4aaa8933b57cc88226a6fb6ac836be88a57087e8f4d447b37b5327993c9f8834e8ea SHA512 4c4a098eaff09a51309f3f322bc435ccd022c8f753974eb2650b60e42b737077ca0fde0df82b53f4ba8ed2388bbc8cb59ba66cc9946ae2b5907d7d1a9580e03d +DIST libX11-1.8.14.tar.xz 1897544 BLAKE2B 0bb56f393ebaf12f62d145f543bc192055879e9d6a7d50d2d141c446dabb8756e3347d260dedb26014d7604fa22b196d9321dbd3f5bf4888a295057e8ef0624d SHA512 4d61596d06235791d6afab823d0457dc42d2769717eb80d4c7e5bc825bb08db6fae46a75561547c679b5f892f24ebd0e1129b60bd4a574f7d1edb3cbfc91df0c diff --git a/x11-libs/libX11/libX11-1.8.14.ebuild b/x11-libs/libX11/libX11-1.8.14.ebuild new file mode 100644 index 000000000000..cc9561ad3220 --- /dev/null +++ b/x11-libs/libX11/libX11-1.8.14.ebuild @@ -0,0 +1,47 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +XORG_DOC=doc +XORG_MULTILIB=yes +inherit toolchain-funcs xorg-3 + +# Note: please bump this with x11-misc/compose-tables +DESCRIPTION="X.Org X11 library" + +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-macos" +IUSE="test" +RESTRICT="!test? ( test )" + +# HACK: libX11 produces .pc files that depend on xproto.pc. When libX11 +# is installed as a binpkg, DEPEND packages are not pulled in, +# but to build source packages against libX11, xorg-proto is +# needed. Until a "build-against-depend" option is available in +# ebuilds, we RDEPEND on xproto. See bug #903707 and others. +RDEPEND=" + >=x11-libs/libxcb-1.11.1[${MULTILIB_USEDEP}] + x11-misc/compose-tables + x11-base/xorg-proto +" +DEPEND="${RDEPEND} + x11-libs/xtrans +" +BDEPEND="test? ( dev-lang/perl )" + +src_configure() { + local XORG_CONFIGURE_OPTIONS=( + $(use_with doc xmlto) + $(use_enable doc specs) + --enable-ipv6 + --without-fop + --with-keysymdefdir="${ESYSROOT}/usr/include/X11" + CPP="$(tc-getPROG CPP cpp)" + ) + xorg-3_src_configure +} + +src_install() { + xorg-3_src_install + rm -rf "${ED}"/usr/share/X11/locale || die +} diff --git a/x11-libs/libXi/Manifest b/x11-libs/libXi/Manifest index 41cce763ad23..c80009e74f8f 100644 --- a/x11-libs/libXi/Manifest +++ b/x11-libs/libXi/Manifest @@ -1 +1,2 @@ DIST libXi-1.8.3.tar.xz 411684 BLAKE2B 45ffc7c3a3407b669fcad80f8ba809455745e3ff87d8bcc861c851c16981cfa2ace2e1075cd778f58257f924a4b7b61bc17c4d7e6628a1cc961f99b4d575550d SHA512 5fb8273424467c102d3bab01cb273169038ff6fae739f6873ca357be8890c4fd30ba2952bca2759249458796df53ad130e2c9c3674b385602afd13c718faf79a +DIST libXi-1.8.4.tar.xz 422212 BLAKE2B f556b55157e587fa351e254e8221bed06568c98129cfd789ffcdc69d1a43e18110a382c14b58d427ed3768ad7df851ca66020ea84d35027a8b415edec6e8b50c SHA512 7cfff5989b4fa375372ac5e28e6c6a9249ff14c1dc6e0c4feeafa7f356e157882c5df30c17b757aabe47a34b8a6da7ab796be5809fe82b6f25bd45cceb1991d1 diff --git a/x11-libs/libXi/libXi-1.8.4.ebuild b/x11-libs/libXi/libXi-1.8.4.ebuild new file mode 100644 index 000000000000..173490539879 --- /dev/null +++ b/x11-libs/libXi/libXi-1.8.4.ebuild @@ -0,0 +1,28 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +XORG_DOC=doc +XORG_MULTILIB=yes +inherit xorg-3 + +DESCRIPTION="X.Org Xi library" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-macos ~x64-solaris" + +RDEPEND=" + >=x11-libs/libX11-1.6.2[${MULTILIB_USEDEP}] + >=x11-libs/libXext-1.3.2[${MULTILIB_USEDEP}] + >=x11-libs/libXfixes-5.0.1[${MULTILIB_USEDEP}]" +DEPEND="${RDEPEND} + >=x11-base/xorg-proto-2021.4.99.2" + +src_configure() { + local XORG_CONFIGURE_OPTIONS=( + $(use_enable doc specs) + $(use_with doc xmlto) + $(use_with doc asciidoc) + --without-fop + ) + xorg-3_src_configure +} diff --git a/x11-libs/libXpm/Manifest b/x11-libs/libXpm/Manifest index 62020c407dff..9443017e926f 100644 --- a/x11-libs/libXpm/Manifest +++ b/x11-libs/libXpm/Manifest @@ -1 +1,2 @@ DIST libXpm-3.5.19.tar.xz 468388 BLAKE2B ef7adfd98fd7c4671cf2767062d73c135a01c4e76e6d8990eb4f9b69f8e21478c4e0aabd8246eec03b964a036d427496914a978568bca16c37ea86578870b4c3 SHA512 dd95a70af0ac2fb0f5876e69ad19b38128c5add94b00238ee41a22634aa4dc56b22c1b47c4baa6a1ab549308d11b302c250b405322da8a64ea24441a065fbada +DIST libXpm-3.5.20.tar.xz 473428 BLAKE2B 51696b74007855df96a638a771ebba5a0cb2c0bd26b3c21857be2b49a2d360c88ecb3cdfbe1d58d5cee176aa2bbb6d9b7641187c35ff5bbde5ba2ed35ce14fa0 SHA512 389f1f3a228d9953587f707b6be455dceebc28e7ff181f01fc9089ea2b6fe67d09f0aaef7d18eaf292bab1c7a7baf10f25928006a272c5d8f3b021d0a82f4225 diff --git a/x11-libs/libXpm/libXpm-3.5.20.ebuild b/x11-libs/libXpm/libXpm-3.5.20.ebuild new file mode 100644 index 000000000000..f626f679d075 --- /dev/null +++ b/x11-libs/libXpm/libXpm-3.5.20.ebuild @@ -0,0 +1,40 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +XORG_DOC=doc +XORG_MULTILIB=yes +inherit xorg-3 + +DESCRIPTION="X.Org Xpm library" +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-macos ~x64-solaris" +IUSE="test" +RESTRICT="!test? ( test )" + +RDEPEND=" + >=x11-libs/libX11-1.6.2[${MULTILIB_USEDEP}] + >=x11-libs/libXext-1.3.2[${MULTILIB_USEDEP}] + >=x11-libs/libXt-1.1.4[${MULTILIB_USEDEP}] + + app-alternatives/gzip + app-arch/ncompress +" +DEPEND="${RDEPEND} + x11-base/xorg-proto + test? ( >=dev-libs/glib-2.46:2 ) +" +BDEPEND=" + sys-devel/gettext + test? ( + app-arch/gzip + app-arch/ncompress + ) +" + +src_configure() { + local XORG_CONFIGURE_OPTIONS=( + $(use_enable test unit-tests) + ) + xorg-3_src_configure +} diff --git a/x11-libs/libXrender/Manifest b/x11-libs/libXrender/Manifest index ce37259b1df5..4d1c997a43b1 100644 --- a/x11-libs/libXrender/Manifest +++ b/x11-libs/libXrender/Manifest @@ -1 +1,2 @@ DIST libXrender-0.9.12.tar.xz 310432 BLAKE2B 703f32aabc074097796ecf97933fd9eb74aca4794496ae5d62fac3cef8646d41fffa8904ac3e76735473f918fa65e9c3da16e54ac512c45f2b00592e8e46dff1 SHA512 3d24a6877b500608e3e2a393532a99d4fd54fc343375d8fb51dfbb1b50cedf002c7722f771cf7776f93cb6e0421ca5966ce45435cb402d5f12a398f9ea743474 +DIST libXrender-0.9.13.tar.xz 316404 BLAKE2B 29ec5e3401cc73d030afcec1786f0d4448079746ccc631ab974a5e3ca3c63b5bd0761985b4022fab48072873b80f8f5e7b34249cc2a56eb158bde6fd455c665d SHA512 c7428584a4bf5258ee95b8b1dafa14eb9e735ad6d3f16f356c0b5023e2d1023f61698b12f055df0bde761b2798b7bf42121007f3fe935ea6ac56cd1a6d5ece44 diff --git a/x11-libs/libXrender/libXrender-0.9.13.ebuild b/x11-libs/libXrender/libXrender-0.9.13.ebuild new file mode 100644 index 000000000000..df14291e2f46 --- /dev/null +++ b/x11-libs/libXrender/libXrender-0.9.13.ebuild @@ -0,0 +1,16 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +XORG_MULTILIB=yes +inherit xorg-3 + +DESCRIPTION="X.Org Xrender library" + +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-macos ~x64-solaris" + +RDEPEND=" + >=x11-libs/libX11-1.6.2[${MULTILIB_USEDEP}]" +DEPEND="${RDEPEND} + x11-base/xorg-proto" diff --git a/x11-libs/libXtst/Manifest b/x11-libs/libXtst/Manifest index 488592022c52..a390e095703f 100644 --- a/x11-libs/libXtst/Manifest +++ b/x11-libs/libXtst/Manifest @@ -1 +1,2 @@ DIST libXtst-1.2.5.tar.xz 291748 BLAKE2B daf9d3c5412623a06c8579b341c1ab6e720719c4f3151acf5c735ea08c79c33800993d55cd9f9e8660be85e290fac641e5e9266deb4d317d9957ced847b1ebb1 SHA512 848fa580d7abccd48c9ca3440f92e299839ada0912ed60d38d4d4f5bf37431cd02d7059265ab4e524c3e2cb9c368b9b90b863d1ed97d74979ef8811fc5e635a9 +DIST libXtst-1.2.6.tar.xz 299372 BLAKE2B 5dee7d2d38b12e5863559d0a1a2309270c39fd83fcd00557be1453b114048f2f6132ce887da217bdadf4a678fd98026f687060fbcd44d19b8ffea8669f291320 SHA512 c1ce90e1274d91c0c7e18b9f004a583bc4104d461cb85642cc01a104cab854f14fc2960a578af56c9ae8d307a20d5efe2a40062cc0c4352765ea8db540d5f34e diff --git a/x11-libs/libXtst/libXtst-1.2.6.ebuild b/x11-libs/libXtst/libXtst-1.2.6.ebuild new file mode 100644 index 000000000000..708748c44909 --- /dev/null +++ b/x11-libs/libXtst/libXtst-1.2.6.ebuild @@ -0,0 +1,18 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +XORG_DOC=doc +XORG_MULTILIB=yes +inherit xorg-3 + +DESCRIPTION="X.Org Xlib-based client API for the XTEST & RECORD extensions library" + +KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86 ~x64-solaris" + +RDEPEND=">=x11-libs/libX11-1.6.2[${MULTILIB_USEDEP}] + >=x11-libs/libXext-1.3.2[${MULTILIB_USEDEP}] + >=x11-libs/libXi-1.7.2[${MULTILIB_USEDEP}]" +DEPEND="${RDEPEND} + x11-base/xorg-proto" diff --git a/x11-misc/xkeyboard-config/Manifest b/x11-misc/xkeyboard-config/Manifest index 1ac89db63221..54f3a05216de 100644 --- a/x11-misc/xkeyboard-config/Manifest +++ b/x11-misc/xkeyboard-config/Manifest @@ -1 +1,2 @@ DIST xkeyboard-config-2.48.tar.xz 953316 BLAKE2B 3200801ed54cc9ec28790053fa3311429e9fbce4607cda67f1f2991b4f0764df0dc35c4f880541833644ba9a4aba075792ce3dd26c54a2539312d8c6bbb75a54 SHA512 2c24f9cca97b8863ff2e71fc3780e9c3e22e4486c80d45022e2208d559bdb40824bdb8a4037e0d5690900b6e080ddef4eacfc8bff843e04ba5ab86940f1fb6ea +DIST xkeyboard-config-2.49.tar.bz2 1172238 BLAKE2B 497459e94d15a23ba5cb46d46b8add04bf4daa6f657f27bb14f5d98a8c500051206663b70888e6e314f2e7379ea78650d50154af42f2d84bc307cf0e79852cbf SHA512 e7d84c228dc6e6850cd9c3eeb58daa7a409911954380c6d491661770b90904a5f5935e99dabbfbbfdf272d21a81390f3302dd0584d5a1ee0e47db98c6205ad04 diff --git a/x11-misc/xkeyboard-config/xkeyboard-config-2.49.ebuild b/x11-misc/xkeyboard-config/xkeyboard-config-2.49.ebuild new file mode 100644 index 000000000000..09a439befabb --- /dev/null +++ b/x11-misc/xkeyboard-config/xkeyboard-config-2.49.ebuild @@ -0,0 +1,81 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +PYTHON_COMPAT=( python3_{13..14} ) +inherit meson python-any-r1 + +DESCRIPTION="X keyboard configuration database" +HOMEPAGE="https://www.freedesktop.org/wiki/Software/XKeyboardConfig/ https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config" + +if [[ ${PV} == 9999 ]]; then + EGIT_REPO_URI="https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config.git" + inherit git-r3 +else + SRC_URI="https://gitlab.freedesktop.org/${PN}/${PN}/-/archive/${P}/${PN}-${P}.tar.bz2 -> ${P}.tar.bz2" + S="${WORKDIR}/${PN}-${P}" + KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" +fi + +LICENSE="MIT" +SLOT="0" +IUSE="test" +RESTRICT="!test? ( test )" + +BDEPEND=" + dev-lang/perl + dev-libs/libxslt + sys-devel/gettext + ${PYTHON_DEPS} + test? ( + x11-apps/xkbcomp + x11-libs/libxkbcommon + $(python_gen_any_dep ' + dev-python/pycountry[${PYTHON_USEDEP}] + dev-python/pytest-xdist[${PYTHON_USEDEP}] + dev-python/pytest[${PYTHON_USEDEP}] + ') + ) +" + +python_check_deps() { + use test || return 0 + python_has_version \ + "dev-python/pycountry[${PYTHON_USEDEP}]" \ + "dev-python/pytest-xdist[${PYTHON_USEDEP}]" \ + "dev-python/pytest[${PYTHON_USEDEP}]" +} + +pkg_setup() { + python-any-r1_pkg_setup +} + +src_prepare() { + default + + # Remove pytest timeout + sed -i -e "/test('pytest'/,/)$/ { s/timeout: [0-9]*/timeout: 0/ }" meson.build || die +} + +src_configure() { + local emesonargs=( + -Dcompat-rules=true + ) + meson_src_configure +} + +src_install() { + meson_src_install + + # Workaround for portage's collision checks, see pkg_preinst (bug #957712) + mv "${ED}"/usr/share/X11/xkb{,.workaround} || die +} + +pkg_preinst() { + # Avoid touching EROOT if not needed, and use -f just-in-case anyway + if [[ -d ${EROOT}/usr/share/X11/xkb && ! -L ${EROOT}/usr/share/X11/xkb ]]; then + rm -rf "${EROOT}"/usr/share/X11/xkb || die + fi + mv "${ED}"/usr/share/X11/xkb{.workaround,} || die +} diff --git a/x11-misc/xkeyboard-config/xkeyboard-config-9999.ebuild b/x11-misc/xkeyboard-config/xkeyboard-config-9999.ebuild index 4fa4cc6986e7..09a439befabb 100644 --- a/x11-misc/xkeyboard-config/xkeyboard-config-9999.ebuild +++ b/x11-misc/xkeyboard-config/xkeyboard-config-9999.ebuild @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 @@ -13,7 +13,8 @@ if [[ ${PV} == 9999 ]]; then EGIT_REPO_URI="https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config.git" inherit git-r3 else - SRC_URI="https://www.x.org/releases/individual/data/${PN}/${P}.tar.xz" + SRC_URI="https://gitlab.freedesktop.org/${PN}/${PN}/-/archive/${P}/${PN}-${P}.tar.bz2 -> ${P}.tar.bz2" + S="${WORKDIR}/${PN}-${P}" KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86" fi @@ -51,7 +52,7 @@ pkg_setup() { } src_prepare() { - eapply_user + default # Remove pytest timeout sed -i -e "/test('pytest'/,/)$/ { s/timeout: [0-9]*/timeout: 0/ }" meson.build || die |
