diff --git a/.gitignore b/.gitignore index 08bf5da..5ab8d18 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /.vscode /_build /target +/*.Dockerfile diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e3e8c0b..c8ca8b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,18 +1,30 @@ stages: - build + - test - lint + - deploy + +workflow: + rules: + - if: $CI_MERGE_REQUEST_IID + - if: $CI_COMMIT_TAG + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH .flatpak: - image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-45' + image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-50' variables: FLATPAK_BUILD_DIR: _build MANIFEST_PATH: build-aux/org.pipewire.Helvum.json APP_FLATPAK_MODULE: Helvum before_script: - flatpak --version + - flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo + - flatpak install -y --user flathub org.gnome.Platform//50 org.gnome.Sdk//50 + - flatpak install -y --user flathub org.freedesktop.Sdk.Extension.llvm22//25.08 + - flatpak install -y --user flathub org.freedesktop.Sdk.Extension.rust-stable//25.08 - flatpak info org.gnome.Platform - flatpak info org.gnome.Sdk - - flatpak info org.freedesktop.Sdk.Extension.llvm16 + - flatpak info org.freedesktop.Sdk.Extension.llvm22 - flatpak info org.freedesktop.Sdk.Extension.rust-stable - flatpak-builder --version @@ -21,33 +33,56 @@ build: extends: .flatpak script: - flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse ${FLATPAK_BUILD_DIR} ${MANIFEST_PATH} + artifacts: + when: always + paths: + - ${FLATPAK_BUILD_DIR} + - .flatpak-builder/build/${APP_FLATPAK_MODULE}-1/_flatpak_build + expire_in: 1 day -# TODO: Run meson test +meson-test: + stage: test + extends: .flatpak + script: + - >- + flatpak-builder --run ${FLATPAK_BUILD_DIR} ${MANIFEST_PATH} + bash -c "meson test -C ${CI_PROJECT_DIR}/.flatpak-builder/build/${APP_FLATPAK_MODULE}-1/_flatpak_build --no-rebuild --print-errorlogs" + needs: ["build"] clippy: stage: lint extends: .flatpak script: - - flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse --stop-at=${APP_FLATPAK_MODULE} ${FLATPAK_BUILD_DIR} ${MANIFEST_PATH} - >- flatpak-builder --run ${FLATPAK_BUILD_DIR} ${MANIFEST_PATH} cargo clippy --color=always --all-targets -- -D warnings + needs: ["build"] rustfmt: stage: lint - image: "rust:slim" # TODO: Check image + image: "rust:1.94-slim" script: - rustup component add rustfmt - - rustc -Vv && cargo -Vv - - cargo fmt --version - cargo fmt --all -- --color=always --check rustdoc: stage: lint extends: .flatpak script: - - flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse --stop-at=${APP_FLATPAK_MODULE} ${FLATPAK_BUILD_DIR} ${MANIFEST_PATH} - >- flatpak-builder --run ${FLATPAK_BUILD_DIR} ${MANIFEST_PATH} env RUSTDOCFLAGS=-Dwarnings cargo doc --no-deps + needs: ["build"] +release: + stage: deploy + extends: .flatpak + script: + - flatpak-builder --repo=repo --force-clean ${FLATPAK_BUILD_DIR} ${MANIFEST_PATH} + - flatpak build-bundle repo helvum.flatpak org.pipewire.Helvum + artifacts: + paths: + - helvum.flatpak + expire_in: never + rules: + - if: $CI_COMMIT_TAG diff --git a/Cargo.lock b/Cargo.lock index 4876527..ac0e5fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,79 +1,98 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] -name = "anyhow" -version = "1.0.75" +name = "annotate-snippets" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" +dependencies = [ + "anstyle", + "unicode-width", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bindgen" -version = "0.66.1" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.4.0", + "annotate-snippets", + "bitflags", "cexpr", "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", + "itertools", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.37", + "syn", ] [[package]] name = "bitflags" -version = "1.3.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "cairo-rs" -version = "0.18.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c0466dfa8c0ee78deef390c274ad756801e0a6dbb86c5ef0924a298c5761c4d" +checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95" dependencies = [ - "bitflags 2.4.0", + "bitflags", "cairo-sys-rs", "glib", "libc", - "once_cell", - "thiserror", ] [[package]] name = "cairo-sys-rs" -version = "0.18.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +checksum = "f8b4985713047f5faee02b8db6a6ef32bbb50269ff53c1aee716d1d195b76d54" dependencies = [ "glib-sys", "libc", @@ -82,11 +101,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ - "libc", + "find-msvc-tools", + "shlex", ] [[package]] @@ -95,14 +115,14 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] name = "cfg-expr" -version = "0.15.5" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" +checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" dependencies = [ "smallvec", "target-lexicon", @@ -110,15 +130,21 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -126,25 +152,67 @@ dependencies = [ ] [[package]] -name = "convert_case" -version = "0.6.0" +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] [[package]] name = "cookie-factory" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] [[package]] name = "field-offset" @@ -152,30 +220,36 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.9.0", + "memoffset", "rustc_version", ] [[package]] -name = "futures-channel" -version = "0.3.28" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -184,59 +258,57 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn", ] [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", - "pin-utils", "slab", ] [[package]] name = "gdk-pixbuf" -version = "0.18.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc9c2ed73a81d556b65d08879ba4ee58808a6b1927ce915262185d6d547c6f3" +checksum = "25f420376dbee041b2db374ce4573892a36222bb3f6c0c43e24f0d67eae9b646" dependencies = [ "gdk-pixbuf-sys", "gio", "glib", "libc", - "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.18.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +checksum = "48f31b37b1fc4b48b54f6b91b7ef04c18e00b4585d98359dd7b998774bbd91fb" dependencies = [ "gio-sys", "glib-sys", @@ -247,9 +319,9 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.7.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edb019ad581f8ecf8ea8e4baa6df7c483a95b5a59be3140be6a9c3b0c632af6" +checksum = "fd42fdbbf48612c6e8f47c65fb92d2e8f39c25aecd6af047e83897c1a22d2a4e" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -262,9 +334,9 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.7.2" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbab43f332a3cf1df9974da690b5bb0e26720ed09a228178ce52175372dcfef0" +checksum = "9d974ac4f15e67472c3a9728daf612590b4a5762a4b33f0edd298df0b80d043c" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -279,9 +351,9 @@ dependencies = [ [[package]] name = "gio" -version = "0.18.2" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57052f84e8e5999b258e8adf8f5f2af0ac69033864936b8b6838321db2f759b1" +checksum = "e3848bcba3a35cc0a71df8ba8ecfd799d6bfb862342a53a4a915fb62213aa4e6" dependencies = [ "futures-channel", "futures-core", @@ -290,32 +362,30 @@ dependencies = [ "gio-sys", "glib", "libc", - "once_cell", "pin-project-lite", "smallvec", - "thiserror", ] [[package]] name = "gio-sys" -version = "0.18.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +checksum = "64729ba2772c080448f9f966dba8f4456beeb100d8c28a865ef8a0f2ef4987e1" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "winapi", + "windows-sys", ] [[package]] name = "glib" -version = "0.18.2" +version = "0.22.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c316afb01ce8067c5eaab1fc4f2cd47dc21ce7b6296358605e2ffab23ccbd19" +checksum = "c207e04e51605dcf7b2924c41591b3a10e1438eaac5bcf448fb91f325381104a" dependencies = [ - "bitflags 2.4.0", + "bitflags", "futures-channel", "futures-core", "futures-executor", @@ -328,30 +398,26 @@ dependencies = [ "libc", "log", "memchr", - "once_cell", "smallvec", - "thiserror", ] [[package]] name = "glib-macros" -version = "0.18.2" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8da903822b136d42360518653fcf154455defc437d3e7a81475bf9a95ff1e47" +checksum = "506d23499707c7142898429757e8d9a3871d965239a2cb66dfa05052be6d6f19" dependencies = [ "heck", - "proc-macro-crate", - "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.37", + "syn", ] [[package]] name = "glib-sys" -version = "0.18.1" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +checksum = "5f7fbac234ed5bc2a28359b7bde8e1b9cdf1441cc2d7f068e4824672d7db9445" dependencies = [ "libc", "system-deps", @@ -359,15 +425,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gobject-sys" -version = "0.18.0" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +checksum = "22a861859b887a79cf461359c192c97a57d8fb0229dd291232e57aa11f6fa72c" dependencies = [ "glib-sys", "libc", @@ -376,9 +442,9 @@ dependencies = [ [[package]] name = "graphene-rs" -version = "0.18.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2228cda1505613a7a956cca69076892cfbda84fc2b7a62b94a41a272c0c401" +checksum = "c7d1b7881f96869f49808b6adfe906a93a57a34204952253444d68c3208d71f1" dependencies = [ "glib", "graphene-sys", @@ -387,9 +453,9 @@ dependencies = [ [[package]] name = "graphene-sys" -version = "0.18.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4144cee8fc8788f2a9b73dc5f1d4e1189d1f95305c4cb7bd9c1af1cfa31f59" +checksum = "517f062f3fd6b7fd3e57a3f038a74b3c23ca32f51199ff028aa704609943f79c" dependencies = [ "glib-sys", "libc", @@ -399,9 +465,9 @@ dependencies = [ [[package]] name = "gsk4" -version = "0.7.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d958e351d2f210309b32d081c832d7de0aca0b077aa10d88336c6379bd01f7e" +checksum = "53c912dfcbd28acace5fc99c40bb9f25e1dcb73efb1f2608327f66a99acdcb62" dependencies = [ "cairo-rs", "gdk4", @@ -414,9 +480,9 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.7.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bd9e3effea989f020e8f1ff3fa3b8c63ba93d43b899c11a118868853a56d55" +checksum = "d7d54bbc7a9d8b6ffe4f0c95eede15ccfb365c8bf521275abe6bcfb57b18fb8a" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -430,9 +496,9 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.7.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb51aa3e9728575a053e1f43543cd9992ac2477e1b186ad824fd4adfb70842" +checksum = "7181b837f04cbe93f79441475f7a00560a92cba7a72e38cc1a68b6f8b78eaae2" dependencies = [ "cairo-rs", "field-offset", @@ -451,23 +517,21 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.7.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d57ec49cf9b657f69a05bca8027cff0a8dfd0c49e812be026fc7311f2163832f" +checksum = "3581b242ba62fdff122ebb626ea641582ec326031622bd19d60f85029c804a87" dependencies = [ - "anyhow", "proc-macro-crate", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "gtk4-sys" -version = "0.7.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54d8c4aa23638ce9faa2caf7e2a27d4a1295af2155c8e8d28c4d4eeca7a65eb8" +checksum = "20ba8e695e2640455561274e65e45f0a151619e450746007667f4b23ceae4e1b" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -484,57 +548,56 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "helvum" version = "0.5.1" dependencies = [ + "async-channel", + "futures-util", "glib", + "gtk4", "libadwaita", "libc", + "libspa", "log", - "once_cell", "pipewire", ] [[package]] name = "indexmap" -version = "2.0.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] [[package]] name = "libadwaita" -version = "0.5.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe7e70c06507ed10a16cda707f358fbe60fe0dc237498f78c686ade92fd979c" +checksum = "bc0da4e27b20d3e71f830e5b0f0188d22c257986bf421c02cfde777fe07932a4" dependencies = [ - "gdk-pixbuf", "gdk4", "gio", "glib", @@ -546,9 +609,9 @@ dependencies = [ [[package]] name = "libadwaita-sys" -version = "0.5.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e10aaa38de1d53374f90deeb4535209adc40cc5dba37f9704724169bceec69a" +checksum = "aaee067051c5d3c058d050d167688b80b67de1950cfca77730549aa761fc5d7d" dependencies = [ "gdk4-sys", "gio-sys", @@ -562,42 +625,42 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.148" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "winapi", + "windows-link", ] [[package]] name = "libspa" -version = "0.7.2" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0434617020ddca18b86067912970c55410ca654cdafd775480322f50b857a8c4" +checksum = "b6b8cfa2a7656627b4c92c6b9ef929433acd673d5ab3708cda1b18478ac00df4" dependencies = [ - "bitflags 2.4.0", + "bitflags", "cc", "convert_case", "cookie-factory", "libc", "libspa-sys", "nix", - "nom", + "nom 8.0.0", "system-deps", ] [[package]] name = "libspa-sys" -version = "0.7.2" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e70ca3f3e70f858ef363046d06178c427b4e0b63d210c95fd87d752679d345" +checksum = "901049455d2eb6decf9058235d745237952f4804bc584c5fcb41412e6adcc6e0" dependencies = [ "bindgen", "cc", @@ -606,30 +669,21 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.6.3" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" -version = "0.7.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -642,15 +696,14 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nix" -version = "0.26.4" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cfg-if", + "cfg_aliases", "libc", - "memoffset 0.7.1", - "pin-utils", ] [[package]] @@ -664,29 +717,37 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.18.0" +name = "nom" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "pango" -version = "0.18.0" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a9e54b831d033206160096b825f2070cf5fda7e35167b1c01e9e774f9202d1" +checksum = "251bdc6e6487b811be0e406a21e301e07e45c0aa8fa39e00c0c8e12a91752438" dependencies = [ "gio", "glib", "libc", - "once_cell", "pango-sys", ] [[package]] name = "pango-sys" -version = "0.18.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +checksum = "bbd111a20ca90fedf03e09c59783c679c00900f1d8491cca5399f5e33609d5d6" dependencies = [ "glib-sys", "gobject-sys", @@ -695,31 +756,25 @@ dependencies = [ ] [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "parking" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pipewire" -version = "0.7.2" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d009c8dd65e890b515a71950f7e4c801523b8894ff33863a40830bf762e9e9" +checksum = "9688b89abf11d756499f7c6190711d6dbe5a3acdb30c8fbf001d6596d06a8d44" dependencies = [ "anyhow", - "bitflags 2.4.0", + "bitflags", "libc", "libspa", "libspa-sys", @@ -731,9 +786,9 @@ dependencies = [ [[package]] name = "pipewire-sys" -version = "0.7.2" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "890c084e7b737246cb4799c86b71a0e4da536031ff7473dd639eba9f95039f64" +checksum = "cb028afee0d6ca17020b090e3b8fa2d7de23305aef975c7e5192a5050246ea36" dependencies = [ "bindgen", "libspa-sys", @@ -742,67 +797,42 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "once_cell", "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.9.5" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -812,9 +842,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -823,97 +853,83 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rustc-hash" -version = "1.1.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "semver" -version = "1.0.19" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] -name = "serde" -version = "1.0.188" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn", ] [[package]] name = "serde_spanned" -version = "0.6.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.11.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -922,9 +938,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.1.1" +version = "7.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" dependencies = [ "cfg-expr", "heck", @@ -935,115 +951,125 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "thiserror" -version = "1.0.49" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn", ] [[package]] name = "toml" -version = "0.7.8" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", - "serde", + "serde_core", "serde_spanned", "toml_datetime", + "toml_parser", + "toml_writer", "winnow", ] [[package]] -name = "unicode-ident" -version = "1.0.12" +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "version-compare" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" [[package]] -name = "version_check" -version = "0.9.4" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "winapi" -version = "0.3.9" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-link", ] -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "winnow" -version = "0.5.15" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index e72f4fd..91ea683 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "helvum" version = "0.5.1" -authors = ["Tom Wagner "] +authors = ["Tom Wagner ", "Naveen Prashanth "] edition = "2021" -rust-version = "1.70" +rust-version = "1.94" license = "GPL-3.0-only" description = "A GTK patchbay for pipewire" repository = "https://gitlab.freedesktop.org/pipewire/helvum" @@ -14,12 +14,14 @@ categories = ["gui", "multimedia"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -pipewire = "0.7.1" -adw = { version = "0.5", package = "libadwaita", features = ["v1_4"] } -glib = { version = "0.18", features = ["log"] } +pipewire = "0.9.2" +libspa = "0.9.2" +async-channel = "2.5.0" +adw = { version = "0.9.1", package = "libadwaita", features = ["v1_9", "gtk_v4_22"] } +glib = { version = "0.22.7", features = ["log", "v2_88"] } +gtk4 = "0.11.3" -log = "0.4.11" +log = "0.4.29" -once_cell = "1.7.2" - -libc = "0.2" +libc = "0.2.186" +futures-util = "0.3.32" diff --git a/README.md b/README.md index 7ed5ec0..4af0a0d 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ Helvum is a GTK-based patchbay for pipewire, inspired by the JACK tool [catia](h ![Screenshot](docs/screenshot.png) - +```` - +```` # Features planned @@ -20,33 +20,40 @@ More suggestions are welcome! # Building ## Via flatpak + If you don't have the flathub repo in your remote-list for flatpak you will need to add that first: + ```shell $ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo ``` Then install the required flatpak platform and SDK, if you dont have them already: + ```shell -$ flatpak install org.gnome.{Platform,Sdk}//45 org.freedesktop.Sdk.Extension.rust-stable//23.08 org.freedesktop.Sdk.Extension.llvm16//23.08 +$ flatpak install org.gnome.{Platform,Sdk}//50 org.freedesktop.Sdk.Extension.rust-stable//25.08 org.freedesktop.Sdk.Extension.llvm22//25.08 ``` To compile and install as a flatpak, clone the project, change to the project directory, and run: + ```shell $ flatpak-builder --install flatpak-build/ build-aux/org.pipewire.Helvum.json ``` You can then run the app via + ```shell $ flatpak run org.pipewire.Helvum ``` ## Manually + For compilation, you will need: -- Meson -- An up-to-date rust toolchain -- `libclang-3.7` or higher -- `libadwaita-1` and `libpipewire-0.3` development packages and their dependencies +- **Meson** (>= 1.9.0) +- **Rust Toolchain** (>= 1.94.0) +- **GTK4** (>= 4.22.0) development packages +- **libadwaita-1** (>= 1.9.0) development packages +- **libpipewire-0.3** (>= 1.6.0) development packages and their dependencies To compile and install, run @@ -60,6 +67,7 @@ in the repository root. This will install the compiled project files into `/usr/local`. # License and Credits + Helvum is distributed under the terms of the GPL3 license. See LICENSE for more information. diff --git a/build-aux/org.pipewire.Helvum.json b/build-aux/org.pipewire.Helvum.json index 8716d17..e0f9df3 100644 --- a/build-aux/org.pipewire.Helvum.json +++ b/build-aux/org.pipewire.Helvum.json @@ -1,11 +1,11 @@ { "id": "org.pipewire.Helvum", "runtime": "org.gnome.Platform", - "runtime-version": "45", + "runtime-version": "50", "sdk": "org.gnome.Sdk", "sdk-extensions": [ "org.freedesktop.Sdk.Extension.rust-stable", - "org.freedesktop.Sdk.Extension.llvm16" + "org.freedesktop.Sdk.Extension.llvm22" ], "command": "helvum", "finish-args": [ @@ -13,11 +13,12 @@ "--socket=wayland", "--device=dri", "--share=ipc", - "--filesystem=xdg-run/pipewire-0" + "--filesystem=xdg-run/pipewire-0", + "--filesystem=host" ], "build-options": { - "append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm16/bin", - "prepend-ld-library-path": "/usr/lib/sdk/llvm16/lib", + "append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm22/bin", + "prepend-ld-library-path": "/usr/lib/sdk/llvm22/lib", "build-args": [ "--share=network" ] diff --git a/data/meson.build b/data/meson.build index 305c8f4..7c35ed5 100644 --- a/data/meson.build +++ b/data/meson.build @@ -11,10 +11,11 @@ desktop_file = configure_file( if desktop_file_validate.found() test( 'validate-desktop', - desktop_file_validate, + sh, args: [ - desktop_file - ], + '-c', + 'desktop-file-validate data/' + base_id + '.desktop' + ] ) endif @@ -36,10 +37,11 @@ appdata_file = configure_file( if appstream_util.found() test( 'validate-appdata', - appstream_util, + sh, args: [ - 'validate', '--nonet', appdata_file - ], + '-c', + 'appstream-util validate --nonet data/' + base_id + '.metainfo.xml' + ] ) endif diff --git a/meson.build b/meson.build index 34bf0fa..83ab731 100644 --- a/meson.build +++ b/meson.build @@ -3,21 +3,26 @@ project( 'rust', version: '0.5.1', license: 'GPL-3.0', - meson_version: '>=0.59.0' + meson_version: '>=1.9.0', + default_options: [ + 'warning_level=2', + 'buildtype=debugoptimized', + ], ) gnome = import('gnome') base_id = 'org.pipewire.Helvum' -dependency('glib-2.0', version: '>= 2.66') -dependency('gtk4', version: '>= 4.4.0') -dependency('libadwaita-1', version: '>= 1.4') -dependency('libpipewire-0.3') +dependency('glib-2.0', version: '>= 2.88.0') +dependency('gtk4', version: '>= 4.22.0') +dependency('libadwaita-1', version: '>= 1.9.0') +dependency('libpipewire-0.3', version: '>= 1.6.0') desktop_file_validate = find_program('desktop-file-validate', required: false) appstream_util = find_program('appstream-util', required: false) cargo = find_program('cargo', required: true) +sh = find_program('sh', required: true) prefix = get_option('prefix') bindir = prefix / get_option('bindir') diff --git a/src/application.rs b/src/application.rs index 0e4dbc0..a94ac0b 100644 --- a/src/application.rs +++ b/src/application.rs @@ -16,11 +16,12 @@ use adw::{ gio, - glib::{self, clone, Receiver}, + glib::{self, clone}, gtk, prelude::*, subclass::prelude::*, }; +use async_channel::Receiver; use pipewire::channel::Sender; use crate::{graph_manager::GraphManager, ui, GtkMessage, PipewireMessage}; @@ -34,7 +35,7 @@ mod imp { use super::*; use adw::subclass::prelude::AdwApplicationImpl; - use once_cell::unsync::OnceCell; + use std::cell::OnceCell; #[derive(Default)] pub struct Application { @@ -60,13 +61,17 @@ mod imp { let zoom_set_action = gio::SimpleAction::new("set-zoom", Some(&f64::static_variant_type())); - zoom_set_action.connect_activate(clone!(@weak graphview => move|_, param| { - let zoom_factor = param.unwrap().get::().unwrap(); - graphview.set_zoom_factor(zoom_factor, None) - })); + zoom_set_action.connect_activate(clone!( + #[weak] + graphview, + move |_, param| { + let zoom_factor = param.unwrap().get::().unwrap(); + graphview.set_zoom_factor(zoom_factor, None) + } + )); self.window.add_action(&zoom_set_action); - self.window.show(); + self.window.present(); } fn startup(&self) { @@ -78,7 +83,7 @@ mod imp { // Load CSS from the STYLE variable. let provider = gtk::CssProvider::new(); - provider.load_from_data(STYLE); + provider.load_from_string(STYLE); gtk::style_context_add_provider_for_display( >k::gdk::Display::default().expect("Error initializing gtk css provider."), &provider, @@ -97,9 +102,13 @@ mod imp { // Add shortcut for quitting the application. let quit = gtk::gio::SimpleAction::new("quit", None); - quit.connect_activate(clone!(@weak obj => move |_, _| { - obj.quit(); - })); + quit.connect_activate(clone!( + #[weak] + obj, + move |_, _| { + obj.quit(); + } + )); obj.set_accels_for_action("app.quit", &["Q"]); obj.add_action(&quit); @@ -116,8 +125,7 @@ mod imp { let window = obj.active_window().unwrap(); let authors: Vec<&str> = AUTHORS.split(':').collect(); - let about_window = adw::AboutWindow::builder() - .transient_for(&window) + let about_dialog = adw::AboutDialog::builder() .application_icon(APP_ID) .application_name("Helvum") .developer_name("Tom Wagner") @@ -128,7 +136,7 @@ mod imp { .license_type(gtk::License::Gpl30Only) .build(); - about_window.present(); + about_dialog.present(Some(&window)); } } } diff --git a/src/graph_manager.rs b/src/graph_manager.rs index 34b2868..af9782d 100644 --- a/src/graph_manager.rs +++ b/src/graph_manager.rs @@ -23,20 +23,18 @@ use crate::{ui::graph::GraphView, GtkMessage, PipewireMessage}; mod imp { use super::*; - use std::{cell::RefCell, collections::HashMap}; - - use once_cell::unsync::OnceCell; + use std::{cell::OnceCell, cell::RefCell, collections::HashMap}; use crate::{ui::graph, MediaType, NodeType}; #[derive(Default, glib::Properties)] #[properties(wrapper_type = super::GraphManager)] pub struct GraphManager { - #[property(get, set, construct_only)] - pub graph: OnceCell, + #[property(get, set, construct_only, nullable)] + pub graph: RefCell>, - #[property(get, set, construct_only)] - pub connection_banner: OnceCell, + #[property(get, set, construct_only, nullable)] + pub connection_banner: RefCell>, pub pw_sender: OnceCell>, pub items: RefCell>, @@ -53,36 +51,69 @@ mod imp { impl ObjectImpl for GraphManager {} impl GraphManager { - pub fn attach_receiver(&self, receiver: glib::Receiver) { - receiver.attach(None, glib::clone!( - @weak self as imp => @default-return glib::ControlFlow::Continue, - move |msg| { + pub fn attach_receiver(&self, receiver: async_channel::Receiver) { + let obj = self.obj().clone(); + glib::MainContext::default().spawn_local(async move { + while let Ok(msg) = receiver.recv().await { + let imp = obj.imp(); match msg { - PipewireMessage::NodeAdded { id, name, node_type } => imp.add_node(id, name.as_str(), node_type), - PipewireMessage::NodeNameChanged { id, name, media_name } => imp.node_name_changed(id, &name, &media_name), - PipewireMessage::PortAdded { id, node_id, name, direction } => imp.add_port(id, name.as_str(), node_id, direction), - PipewireMessage::PortFormatChanged { id, media_type } => imp.port_media_type_changed(id, media_type), + PipewireMessage::NodeAdded { + id, + name, + node_type, + } => imp.add_node(id, name.as_str(), node_type), + PipewireMessage::NodeNameChanged { + id, + name, + media_name, + } => imp.node_name_changed(id, &name, &media_name), + PipewireMessage::PortAdded { + id, + node_id, + name, + direction, + } => imp.add_port(id, name.as_str(), node_id, direction), + PipewireMessage::PortFormatChanged { id, media_type } => { + imp.port_media_type_changed(id, media_type) + } PipewireMessage::LinkAdded { - id, port_from, port_to, active, media_type + id, + port_from, + port_to, + active, + media_type, } => imp.add_link(id, port_from, port_to, active, media_type), - PipewireMessage::LinkStateChanged { id, active } => imp.link_state_changed(id, active), - PipewireMessage::LinkFormatChanged { id, media_type } => imp.link_format_changed(id, media_type), + PipewireMessage::LinkStateChanged { id, active } => { + imp.link_state_changed(id, active) + } + PipewireMessage::LinkFormatChanged { id, media_type } => { + imp.link_format_changed(id, media_type) + } PipewireMessage::NodeRemoved { id } => imp.remove_node(id), - PipewireMessage::PortRemoved { id, node_id } => imp.remove_port(id, node_id), + PipewireMessage::PortRemoved { id, node_id } => { + imp.remove_port(id, node_id) + } PipewireMessage::LinkRemoved { id } => imp.remove_link(id), PipewireMessage::Connecting => { - imp.obj().connection_banner().set_revealed(true); + if let Some(banner) = imp.connection_banner.borrow().as_ref() { + banner.set_revealed(true); + } } PipewireMessage::Connected => { - imp.obj().connection_banner().set_revealed(false); - }, + if let Some(banner) = imp.connection_banner.borrow().as_ref() { + banner.set_revealed(false); + } + } PipewireMessage::Disconnected => { imp.clear(); - }, - }; - glib::ControlFlow::Continue + } + } } - )); + }); + } + + fn graph_view(&self) -> crate::ui::graph::GraphView { + self.graph.borrow().clone().expect("graph should be set") } /// Add a new node to the view. @@ -93,7 +124,7 @@ mod imp { self.items.borrow_mut().insert(id, node.clone().upcast()); - self.obj().graph().add_node(node, node_type); + self.graph_view().add_node(node, node_type); } /// Update a node tooltip to the view. @@ -126,11 +157,11 @@ mod imp { return; }; - self.obj().graph().remove_node(&node); + self.graph_view().remove_node(&node); } /// Add a new port to the view. - fn add_port(&self, id: u32, name: &str, node_id: u32, direction: pipewire::spa::Direction) { + fn add_port(&self, id: u32, name: &str, node_id: u32, direction: libspa::utils::Direction) { log::info!("Adding port to graph: id {}", id); let mut items = self.items.borrow_mut(); @@ -150,15 +181,20 @@ mod imp { port.connect_local( "port_toggled", false, - glib::clone!(@weak self as app => @default-return None, move |args| { - // Args always look like this: &[widget, id_port_from, id_port_to] - let port_from = args[1].get::().unwrap(); - let port_to = args[2].get::().unwrap(); + glib::clone!( + #[weak(rename_to = app)] + self, + #[upgrade_or_default] + move |args| { + // Args always look like this: &[widget, id_port_from, id_port_to] + let port_from = args[1].get::().unwrap(); + let port_to = args[2].get::().unwrap(); - app.toggle_link(port_from, port_to); + app.toggle_link(port_from, port_to); - None - }), + None + } + ), ); items.insert(id, port.clone().upcast()); @@ -248,7 +284,8 @@ mod imp { // Update graph to contain the new link. self.graph - .get() + .borrow() + .as_ref() .expect("graph should be set") .add_link(link); } @@ -273,7 +310,7 @@ mod imp { link.set_active(active); } - fn link_format_changed(&self, id: u32, media_type: pipewire::spa::format::MediaType) { + fn link_format_changed(&self, id: u32, media_type: libspa::param::format::MediaType) { let items = self.items.borrow(); let Some(link) = items.get(&id) else { @@ -308,12 +345,12 @@ mod imp { return; }; - self.obj().graph().remove_link(&link); + self.graph_view().remove_link(&link); } fn clear(&self) { self.items.borrow_mut().clear(); - self.obj().graph().clear(); + self.graph_view().clear(); } } } @@ -327,7 +364,7 @@ impl GraphManager { graph: &GraphView, connection_banner: &adw::Banner, sender: PwSender, - receiver: glib::Receiver, + receiver: async_channel::Receiver, ) -> Self { let res: Self = glib::Object::builder() .property("graph", graph) diff --git a/src/main.rs b/src/main.rs index 610c175..3222475 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,7 @@ mod pipewire_connection; mod ui; use adw::{gtk, prelude::*}; -use pipewire::spa::{format::MediaType, Direction}; +use libspa::{param::format::MediaType, utils::Direction}; /// Messages sent by the GTK thread to notify the pipewire thread. #[derive(Debug, Clone)] @@ -120,7 +120,7 @@ fn main() -> Result<(), Box> { // Start the pipewire thread with channels in both directions. - let (gtk_sender, gtk_receiver) = glib::MainContext::channel(glib::Priority::DEFAULT); + let (gtk_sender, gtk_receiver) = async_channel::bounded::(64); let (pw_sender, pw_receiver) = pipewire::channel::channel(); let pw_thread = std::thread::spawn(move || pipewire_connection::thread_main(gtk_sender, pw_receiver)); diff --git a/src/pipewire_connection/mod.rs b/src/pipewire_connection/mod.rs index 768f737..9be140f 100644 --- a/src/pipewire_connection/mod.rs +++ b/src/pipewire_connection/mod.rs @@ -23,22 +23,21 @@ use std::{ time::Duration, }; -use adw::glib::{self, clone}; +use libspa::{ + param::{ParamInfoFlags, ParamType}, + utils::dict::DictRef, +}; use log::{debug, error, info, warn}; use pipewire::{ + context::ContextRc, + core::CoreRc, keys, - link::{Link, LinkChangeMask, LinkInfo, LinkListener, LinkState}, - node::{Node, NodeInfo, NodeListener}, - port::{Port, PortChangeMask, PortInfo, PortListener}, - prelude::*, - properties, - registry::{GlobalObject, Registry}, - spa::{ - param::{ParamInfoFlags, ParamType}, - ForeignDict, SpaResult, - }, + link::{Link, LinkChangeMask, LinkListener, LinkState}, + main_loop::MainLoopRc, + node::{Node, NodeListener}, + port::{Port, PortChangeMask, PortListener}, + registry::{GlobalObject, RegistryRc}, types::ObjectType, - Context, Core, MainLoop, }; use crate::{GtkMessage, MediaType, NodeType, PipewireMessage}; @@ -61,50 +60,45 @@ enum ProxyItem { /// The "main" function of the pipewire thread. pub(super) fn thread_main( - gtk_sender: glib::Sender, + gtk_sender: async_channel::Sender, mut pw_receiver: pipewire::channel::Receiver, ) { - let mainloop = MainLoop::new().expect("Failed to create mainloop"); - let context = Rc::new(Context::new(&mainloop).expect("Failed to create context")); + let mainloop = MainLoopRc::new(None).expect("Failed to create mainloop"); + let context = ContextRc::new(&mainloop, None).expect("Failed to create context"); let is_stopped = Rc::new(Cell::new(false)); let mut is_connecting = false; while !is_stopped.get() { // Try to connect - let core = match context.connect(Some(properties! { - "media.category" => "Manager" - })) { - Ok(core) => Rc::new(core), + let core = match context.connect_rc(None) { + Ok(core) => core, Err(_) => { if !is_connecting { is_connecting = true; gtk_sender - .send(PipewireMessage::Connecting) + .send_blocking(PipewireMessage::Connecting) .expect("Failed to send message"); } - // If connection is failed, try to connect again in 200ms + // If connection failed, try again in 200ms let interval = Some(Duration::from_millis(200)); - - let timer = mainloop.add_timer(clone!(@strong mainloop => move |_| { - mainloop.quit(); - })); - + let ml = mainloop.clone(); + let timer = mainloop.loop_().add_timer(move |_| { + ml.quit(); + }); timer.update_timer(interval, None).into_result().unwrap(); - let receiver = pw_receiver.attach(&mainloop, { - clone!(@strong mainloop, @strong is_stopped => move |msg| - if let GtkMessage::Terminate = msg { - // main thread requested stop - is_stopped.set(true); - mainloop.quit(); - } - ) + let ml2 = mainloop.clone(); + let is_stopped2 = is_stopped.clone(); + let receiver = pw_receiver.attach(mainloop.loop_(), move |msg| { + if let GtkMessage::Terminate = msg { + is_stopped2.set(true); + ml2.quit(); + } }); mainloop.run(); pw_receiver = receiver.deattach(); - continue; } }; @@ -112,73 +106,92 @@ pub(super) fn thread_main( if is_connecting { is_connecting = false; gtk_sender - .send(PipewireMessage::Connected) + .send_blocking(PipewireMessage::Connected) .expect("Failed to send message"); } - let registry = Rc::new(core.get_registry().expect("Failed to get registry")); + let registry = core.get_registry_rc().expect("Failed to get registry"); - // Keep proxies and their listeners alive so that we can receive info events. - let proxies = Rc::new(RefCell::new(HashMap::new())); + let proxies: Rc>> = Rc::new(RefCell::new(HashMap::new())); let state = Rc::new(RefCell::new(State::new())); - let receiver = pw_receiver.attach(&mainloop, { - clone!(@strong mainloop, @weak core, @weak registry, @strong state, @strong is_stopped => move |msg| match msg { - GtkMessage::ToggleLink { port_from, port_to } => toggle_link(port_from, port_to, &core, ®istry, &state), - GtkMessage::Terminate => { - // main thread requested stop - is_stopped.set(true); - mainloop.quit(); - } - }) + // Attach receiver to process GTK→PW messages + let ml3 = mainloop.clone(); + let core2 = core.clone(); + let registry2 = registry.clone(); + let state2 = state.clone(); + let is_stopped2 = is_stopped.clone(); + let receiver = pw_receiver.attach(mainloop.loop_(), move |msg| match msg { + GtkMessage::ToggleLink { port_from, port_to } => { + toggle_link(port_from, port_to, &core2, ®istry2, &state2) + } + GtkMessage::Terminate => { + is_stopped2.set(true); + ml3.quit(); + } }); - let gtk_sender = gtk_sender.clone(); - let _listener = core.add_listener_local() - .error(clone!(@strong mainloop, @strong gtk_sender, @strong is_stopped => move |id, _seq, res, message| { - if id != pipewire::PW_ID_CORE { + // Listen for core errors (e.g. disconnection) + let gtk_sender2 = gtk_sender.clone(); + let ml4 = mainloop.clone(); + let _listener = core + .add_listener_local() + .error(move |id, _seq, res, message| { + if id != 0 { return; } - if res == -libc::EPIPE { - gtk_sender.send(PipewireMessage::Disconnected) + gtk_sender2 + .send_blocking(PipewireMessage::Disconnected) .expect("Failed to send message"); - mainloop.quit(); + ml4.quit(); } else { + use libspa::utils::result::SpaResult; let serr = SpaResult::from_c(res).into_result().unwrap_err(); error!("Pipewire Core received error {serr}: {message}"); } - })) + }) .register(); + // Listen for registry events (nodes, ports, links appearing / disappearing) + let gtk_sender3 = gtk_sender.clone(); + let registry3 = registry.clone(); + let proxies2 = proxies.clone(); + let state3 = state.clone(); + let proxies_remove = proxies.clone(); + let state_remove = state.clone(); + let gtk_sender4 = gtk_sender.clone(); let _listener = registry .add_listener_local() - .global(clone!(@strong gtk_sender, @weak registry, @strong proxies, @strong state => - move |global| match global.type_ { - ObjectType::Node => handle_node(global, >k_sender, ®istry, &proxies, &state), - ObjectType::Port => handle_port(global, >k_sender, ®istry, &proxies, &state), - ObjectType::Link => handle_link(global, >k_sender, ®istry, &proxies, &state), - _ => { - // Other objects are not interesting to us - } + .global(move |global| match global.type_ { + ObjectType::Node => { + handle_node(global, >k_sender3, ®istry3, &proxies2, &state3) } - )) - .global_remove(clone!(@strong proxies, @strong state => move |id| { - if let Some(item) = state.borrow_mut().remove(id) { - gtk_sender.send(match item { - Item::Node { .. } => PipewireMessage::NodeRemoved {id}, - Item::Port { node_id } => PipewireMessage::PortRemoved {id, node_id}, - Item::Link { .. } => PipewireMessage::LinkRemoved {id}, - }).expect("Failed to send message"); + ObjectType::Port => { + handle_port(global, >k_sender3, ®istry3, &proxies2, &state3) + } + ObjectType::Link => { + handle_link(global, >k_sender3, ®istry3, &proxies2, &state3) + } + _ => {} + }) + .global_remove(move |id| { + if let Some(item) = state_remove.borrow_mut().remove(id) { + gtk_sender4 + .send_blocking(match item { + Item::Node => PipewireMessage::NodeRemoved { id }, + Item::Port { node_id } => PipewireMessage::PortRemoved { id, node_id }, + Item::Link { .. } => PipewireMessage::LinkRemoved { id }, + }) + .expect("Failed to send message"); } else { warn!( "Attempted to remove item with id {} that is not saved in state", id ); } - - proxies.borrow_mut().remove(&id); - })) + proxies_remove.borrow_mut().remove(&id); + }) .register(); mainloop.run(); @@ -187,7 +200,7 @@ pub(super) fn thread_main( } /// Get the nicest possible name for the node, using a fallback chain of possible name attributes -fn get_node_name(props: &ForeignDict) -> &str { +fn get_node_name(props: &DictRef) -> &str { props .get(&keys::NODE_DESCRIPTION) .or_else(|| props.get(&keys::NODE_NICK)) @@ -197,9 +210,9 @@ fn get_node_name(props: &ForeignDict) -> &str { /// Handle a new node being added fn handle_node( - node: &GlobalObject, - sender: &glib::Sender, - registry: &Rc, + node: &GlobalObject<&DictRef>, + sender: &async_channel::Sender, + registry: &RegistryRc, proxies: &Rc>>, state: &Rc>, ) { @@ -233,7 +246,7 @@ fn handle_node( state.borrow_mut().insert(node.id, Item::Node); sender - .send(PipewireMessage::NodeAdded { + .send_blocking(PipewireMessage::NodeAdded { id: node.id, name, node_type, @@ -241,11 +254,13 @@ fn handle_node( .expect("Failed to send message"); let proxy: Node = registry.bind(node).expect("Failed to bind to node proxy"); + let sender2 = sender.clone(); + let proxies2 = proxies.clone(); let listener = proxy .add_listener_local() - .info(clone!(@strong sender, @strong proxies => move |info| { - handle_node_info(info, &sender, &proxies); - })) + .info(move |info| { + handle_node_info(info, &sender2, &proxies2); + }) .register(); proxies.borrow_mut().insert( @@ -258,8 +273,8 @@ fn handle_node( } fn handle_node_info( - info: &NodeInfo, - sender: &glib::Sender, + info: &pipewire::node::NodeInfoRef, + sender: &async_channel::Sender, proxies: &Rc>>, ) { debug!("Received node info: {:?}", info); @@ -276,7 +291,7 @@ fn handle_node_info( let name = get_node_name(props).to_string(); sender - .send(PipewireMessage::NodeNameChanged { + .send_blocking(PipewireMessage::NodeNameChanged { id, name, media_name: media_name.to_string(), @@ -287,26 +302,29 @@ fn handle_node_info( /// Handle a new port being added fn handle_port( - port: &GlobalObject, - sender: &glib::Sender, - registry: &Rc, + port: &GlobalObject<&DictRef>, + sender: &async_channel::Sender, + registry: &RegistryRc, proxies: &Rc>>, state: &Rc>, ) { let port_id = port.id; let proxy: Port = registry.bind(port).expect("Failed to bind to port proxy"); + + let sender2 = sender.clone(); + let proxies2 = proxies.clone(); + let state2 = state.clone(); + let sender3 = sender.clone(); let listener = proxy .add_listener_local() - .info( - clone!(@strong proxies, @strong state, @strong sender => move |info| { - handle_port_info(info, &proxies, &state, &sender); - }), - ) - .param(clone!(@strong sender => move |_, param_id, _, _, param| { + .info(move |info| { + handle_port_info(info, &proxies2, &state2, &sender2); + }) + .param(move |_, param_id, _, _, param| { if param_id == ParamType::EnumFormat { - handle_port_enum_format(port_id, param, &sender) + handle_port_enum_format(port_id, param, &sender3) } - })) + }) .register(); proxies.borrow_mut().insert( @@ -319,10 +337,10 @@ fn handle_port( } fn handle_port_info( - info: &PortInfo, + info: &pipewire::port::PortInfoRef, proxies: &Rc>>, state: &Rc>, - sender: &glib::Sender, + sender: &async_channel::Sender, ) { debug!("Received port info: {:?}", info); @@ -341,7 +359,7 @@ fn handle_port_info( // TODO: React to param changes } } else { - // First time we get info. We can now notify the gtk thread of a new link. + // First time we get info. We can now notify the gtk thread of a new port. let props = info.props().expect("Port object is missing properties"); let name = props.get("port.name").unwrap_or_default().to_string(); let node_id: u32 = props @@ -363,7 +381,7 @@ fn handle_port_info( } sender - .send(PipewireMessage::PortAdded { + .send_blocking(PipewireMessage::PortAdded { id, node_id, name, @@ -375,16 +393,16 @@ fn handle_port_info( fn handle_port_enum_format( port_id: u32, - param: Option<&pipewire::spa::pod::Pod>, - sender: &glib::Sender, + param: Option<&libspa::pod::Pod>, + sender: &async_channel::Sender, ) { let media_type = param - .and_then(|param| pipewire::spa::param::format_utils::parse_format(param).ok()) + .and_then(|param| libspa::param::format_utils::parse_format(param).ok()) .map(|(media_type, _media_subtype)| media_type) .unwrap_or(MediaType::Unknown); sender - .send(PipewireMessage::PortFormatChanged { + .send_blocking(PipewireMessage::PortFormatChanged { id: port_id, media_type, }) @@ -393,11 +411,11 @@ fn handle_port_enum_format( /// Handle a new link being added fn handle_link( - link: &GlobalObject, - sender: &glib::Sender, - registry: &Rc, + link: &GlobalObject<&DictRef>, + sender: &async_channel::Sender, + registry: &RegistryRc, proxies: &Rc>>, - state: &Rc>, + _state: &Rc>, ) { debug!( "New link (id:{}) appeared, setting up info listener.", @@ -405,11 +423,13 @@ fn handle_link( ); let proxy: Link = registry.bind(link).expect("Failed to bind to link proxy"); + let sender2 = sender.clone(); + let state_link = _state.clone(); let listener = proxy .add_listener_local() - .info(clone!(@strong state, @strong sender => move |info| { - handle_link_info(info, &state, &sender); - })) + .info(move |info| { + handle_link_info(info, &state_link, &sender2); + }) .register(); proxies.borrow_mut().insert( @@ -422,9 +442,9 @@ fn handle_link( } fn handle_link_info( - info: &LinkInfo, + info: &pipewire::link::LinkInfoRef, state: &Rc>, - sender: &glib::Sender, + sender: &async_channel::Sender, ) { debug!("Received link info: {:?}", info); @@ -432,10 +452,9 @@ fn handle_link_info( let mut state = state.borrow_mut(); if let Some(Item::Link { .. }) = state.get(id) { - // Info was an update - figure out if we should notify the gtk thread if info.change_mask().contains(LinkChangeMask::STATE) { sender - .send(PipewireMessage::LinkStateChanged { + .send_blocking(PipewireMessage::LinkStateChanged { id, active: matches!(info.state(), LinkState::Active), }) @@ -443,21 +462,20 @@ fn handle_link_info( } if info.change_mask().contains(LinkChangeMask::FORMAT) { sender - .send(PipewireMessage::LinkFormatChanged { + .send_blocking(PipewireMessage::LinkFormatChanged { id, media_type: get_link_media_type(info), }) .expect("Failed to send message"); } } else { - // First time we get info. We can now notify the gtk thread of a new link. let port_from = info.output_port_id(); let port_to = info.input_port_id(); state.insert(id, Item::Link { port_from, port_to }); sender - .send(PipewireMessage::LinkAdded { + .send_blocking(PipewireMessage::LinkAdded { id, port_from, port_to, @@ -472,15 +490,13 @@ fn handle_link_info( fn toggle_link( port_from: u32, port_to: u32, - core: &Rc, - registry: &Rc, + core: &CoreRc, + registry: &RegistryRc, state: &Rc>, ) { - let state = state.borrow_mut(); + let state = state.borrow(); if let Some(id) = state.get_link_id(port_from, port_to) { info!("Requesting removal of link with id {}", id); - - // FIXME: Handle error registry.destroy_global(id); } else { info!( @@ -495,9 +511,9 @@ fn toggle_link( .get_node_of_port(port_to) .expect("Requested port not in state"); - if let Err(e) = core.create_object::( + if let Err(e) = core.create_object::( "link-factory", - &properties! { + &pipewire::properties::properties! { "link.output.node" => node_from.to_string(), "link.output.port" => port_from.to_string(), "link.input.node" => node_to.to_string(), @@ -510,12 +526,10 @@ fn toggle_link( } } -fn get_link_media_type(link_info: &LinkInfo) -> MediaType { - let media_type = link_info +fn get_link_media_type(link_info: &pipewire::link::LinkInfoRef) -> MediaType { + link_info .format() - .and_then(|format| pipewire::spa::param::format_utils::parse_format(format).ok()) + .and_then(|format| libspa::param::format_utils::parse_format(format).ok()) .map(|(media_type, _media_subtype)| media_type) - .unwrap_or(MediaType::Unknown); - - media_type + .unwrap_or(MediaType::Unknown) } diff --git a/src/ui/graph/graph_view.rs b/src/ui/graph/graph_view.rs index 5825dae..9f2f990 100644 --- a/src/ui/graph/graph_view.rs +++ b/src/ui/graph/graph_view.rs @@ -40,10 +40,10 @@ mod imp { use std::collections::{HashMap, HashSet}; use adw::gtk::gdk::{self}; + use libspa::param::format::MediaType; + use libspa::utils::Direction; use log::warn; - use once_cell::sync::Lazy; - use pipewire::spa::format::MediaType; - use pipewire::spa::Direction; + use std::sync::LazyLock; pub struct Colors { audio: gdk::RGBA, @@ -151,7 +151,7 @@ mod imp { } fn properties() -> &'static [glib::ParamSpec] { - static PROPERTIES: Lazy> = Lazy::new(|| { + static PROPERTIES: LazyLock> = LazyLock::new(|| { vec![ glib::ParamSpecOverride::for_interface::("hadjustment"), glib::ParamSpecOverride::for_interface::("vadjustment"), @@ -228,14 +228,25 @@ mod imp { fn snapshot(&self, snapshot: >k::Snapshot) { let widget = &*self.obj(); - let alloc = widget.allocation(); + let (width, height) = (widget.width(), widget.height()); // Draw all visible children self.nodes .borrow() .iter() // Cull nodes from rendering when they are outside the visible canvas area - .filter(|(node, _)| alloc.intersect(&node.allocation()).is_some()) + .filter(|(node, _)| { + let n_width = node.width() as f32; + let n_height = node.height() as f32; + let p = node + .compute_point(widget, &Point::new(0.0, 0.0)) + .unwrap_or(Point::new(0.0, 0.0)); + let (n_x, n_y) = (p.x(), p.y()); + n_x < width as f32 + && n_y < height as f32 + && n_x + n_width > 0.0 + && n_y + n_height > 0.0 + }) .for_each(|(node, _)| widget.snapshot_child(node, snapshot)); self.snapshot_links(widget, snapshot); @@ -276,6 +287,7 @@ mod imp { drag_controller.connect_drag_begin(|drag_controller, x, y| { let widget = drag_controller .widget() + .unwrap() .dynamic_cast::() .expect("drag-begin event is not on the GraphView"); let mut dragged_node = widget.imp().dragged_node.borrow_mut(); @@ -315,6 +327,7 @@ mod imp { drag_controller.connect_drag_update(|drag_controller, x, y| { let widget = drag_controller .widget() + .unwrap() .dynamic_cast::() .expect("drag-update event is not on the GraphView"); let dragged_node = widget.imp().dragged_node.borrow(); @@ -348,6 +361,7 @@ mod imp { controller.connect_enter(|controller, x, y| { let graph = controller .widget() + .unwrap() .downcast::() .expect("Widget should be a graphview"); @@ -357,6 +371,7 @@ mod imp { controller.connect_motion(|controller, x, y| { let graph = controller .widget() + .unwrap() .downcast::() .expect("Widget should be a graphview"); @@ -366,6 +381,7 @@ mod imp { controller.connect_leave(|controller| { let graph = controller .widget() + .unwrap() .downcast::() .expect("Widget should be a graphview"); @@ -386,14 +402,18 @@ mod imp { Port::static_type(), glib::Priority::DEFAULT, Option::<&gio::Cancellable>::None, - clone!(@weak self as imp => move|value| { - let Ok(value) = value else { - return; - }; - let port: &Port = value.get().expect("Value should contain a port"); + clone!( + #[weak(rename_to = imp)] + self, + move |value| { + let Ok(value) = value else { + return; + }; + let port: &Port = value.get().expect("Value should contain a port"); - imp.dragged_port.set(Some(port)); - }), + imp.dragged_port.set(Some(port)); + } + ), ); self.obj().queue_draw(); @@ -430,6 +450,7 @@ mod imp { { let widget = eventcontroller .widget() + .unwrap() .downcast::() .unwrap(); widget.set_zoom_factor(widget.zoom_factor() + (0.1 * -delta_y), None); @@ -445,7 +466,11 @@ mod imp { fn setup_zoom_gesture(&self) { let zoom_gesture = gtk::GestureZoom::new(); zoom_gesture.connect_begin(|gesture, _| { - let widget = gesture.widget().downcast::().unwrap(); + let widget = gesture + .widget() + .unwrap() + .downcast::() + .unwrap(); widget .imp() @@ -457,7 +482,11 @@ mod imp { .set(gesture.bounding_box_center()); }); zoom_gesture.connect_scale_changed(move |gesture, delta| { - let widget = gesture.widget().downcast::().unwrap(); + let widget = gesture + .widget() + .unwrap() + .downcast::() + .unwrap(); let initial_zoom = widget .imp() @@ -480,6 +509,7 @@ mod imp { drag_controller.connect_drag_begin(|drag_controller, _, _| { let widget = drag_controller .widget() + .unwrap() .downcast::() .unwrap(); @@ -489,6 +519,7 @@ mod imp { drag_controller.connect_drag_update(|drag_controller, x, y| { let widget = drag_controller .widget() + .unwrap() .downcast::() .unwrap(); @@ -601,34 +632,18 @@ mod imp { } fn snapshot_links(&self, widget: &super::GraphView, snapshot: >k::Snapshot) { - let alloc = widget.allocation(); + let (width, height) = (widget.width(), widget.height()); - let link_cr = snapshot.append_cairo(&graphene::Rect::new( - 0.0, - 0.0, - alloc.width() as f32, - alloc.height() as f32, - )); + let link_cr = + snapshot.append_cairo(&graphene::Rect::new(0.0, 0.0, width as f32, height as f32)); link_cr.set_line_width(2.0 * self.zoom_factor.get()); let colors = Colors { - audio: widget - .style_context() - .lookup_color("media-type-audio") - .expect("color not found"), - video: widget - .style_context() - .lookup_color("media-type-video") - .expect("color not found"), - midi: widget - .style_context() - .lookup_color("media-type-midi") - .expect("color not found"), - unknown: widget - .style_context() - .lookup_color("media-type-unknown") - .expect("color not found"), + audio: gdk::RGBA::new(50.0 / 255.0, 100.0 / 255.0, 240.0 / 255.0, 1.0), + video: gdk::RGBA::new(200.0 / 255.0, 200.0 / 255.0, 0.0, 1.0), + midi: gdk::RGBA::new(200.0 / 255.0, 0.0, 50.0 / 255.0, 1.0), + unknown: gdk::RGBA::new(128.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0), }; for link in self.links.borrow().iter() { @@ -688,8 +703,11 @@ mod imp { } if let Some(adjustment) = adjustment { - adjustment - .connect_value_changed(clone!(@weak obj => move |_| obj.queue_allocate() )); + adjustment.connect_value_changed(clone!( + #[weak] + obj, + move |_| obj.queue_allocate() + )); } } @@ -720,7 +738,7 @@ mod imp { glib::wrapper! { pub struct GraphView(ObjectSubclass) - @extends gtk::Widget; + @extends gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Scrollable; } impl GraphView { @@ -748,12 +766,8 @@ impl GraphView { pub fn set_zoom_factor(&self, zoom_factor: f64, anchor: Option<(f64, f64)>) { let zoom_factor = zoom_factor.clamp(Self::ZOOM_MIN, Self::ZOOM_MAX); - let (anchor_x_screen, anchor_y_screen) = anchor.unwrap_or_else(|| { - ( - self.allocation().width() as f64 / 2.0, - self.allocation().height() as f64 / 2.0, - ) - }); + let (anchor_x_screen, anchor_y_screen) = + anchor.unwrap_or_else(|| (self.width() as f64 / 2.0, self.height() as f64 / 2.0)); let old_zoom = self.imp().zoom_factor.get(); let hadjustment_ref = self.imp().hadjustment.borrow(); @@ -822,15 +836,23 @@ impl GraphView { pub fn add_link(&self, link: Link) { link.connect_notify_local( Some("active"), - glib::clone!(@weak self as graph => move |_, _| { - graph.queue_draw(); - }), + glib::clone!( + #[weak(rename_to = graph)] + self, + move |_, _| { + graph.queue_draw(); + } + ), ); link.connect_notify_local( Some("media-type"), - glib::clone!(@weak self as graph => move |_, _| { - graph.queue_draw(); - }), + glib::clone!( + #[weak(rename_to = graph)] + self, + move |_, _| { + graph.queue_draw(); + } + ), ); self.imp().links.borrow_mut().insert(link); self.queue_draw(); diff --git a/src/ui/graph/link.rs b/src/ui/graph/link.rs index 140c74c..491a5d3 100644 --- a/src/ui/graph/link.rs +++ b/src/ui/graph/link.rs @@ -15,7 +15,7 @@ // SPDX-License-Identifier: GPL-3.0-only use adw::{glib, prelude::*, subclass::prelude::*}; -use pipewire::spa::format::MediaType; +use libspa::param::format::MediaType; use super::Port; @@ -24,7 +24,7 @@ mod imp { use std::cell::Cell; - use once_cell::sync::Lazy; + use std::sync::LazyLock; pub struct Link { pub output_port: glib::WeakRef, @@ -53,7 +53,7 @@ mod imp { impl ObjectImpl for Link { fn properties() -> &'static [glib::ParamSpec] { - static PROPERTIES: Lazy> = Lazy::new(|| { + static PROPERTIES: LazyLock> = LazyLock::new(|| { vec![ glib::ParamSpecObject::builder::("output-port") .flags(glib::ParamFlags::READWRITE) diff --git a/src/ui/graph/node.rs b/src/ui/graph/node.rs index a3f1afb..062c82f 100644 --- a/src/ui/graph/node.rs +++ b/src/ui/graph/node.rs @@ -15,7 +15,7 @@ // SPDX-License-Identifier: GPL-3.0-only use adw::{glib, gtk, prelude::*, subclass::prelude::*}; -use pipewire::spa::Direction; +use libspa::utils::Direction; use super::Port; @@ -149,7 +149,7 @@ mod imp { glib::wrapper! { pub struct Node(ObjectSubclass) - @extends gtk::Widget; + @extends gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; } impl Node { diff --git a/src/ui/graph/port.rs b/src/ui/graph/port.rs index 13a5d9b..cbf066c 100644 --- a/src/ui/graph/port.rs +++ b/src/ui/graph/port.rs @@ -21,7 +21,7 @@ use adw::{ prelude::*, subclass::prelude::*, }; -use pipewire::spa::Direction; +use libspa::utils::Direction; use super::PortHandle; @@ -30,8 +30,8 @@ mod imp { use std::cell::Cell; - use once_cell::{sync::Lazy, unsync::OnceCell}; - use pipewire::spa::{format::MediaType, Direction}; + use libspa::{param::format::MediaType, utils::Direction}; + use std::sync::LazyLock; /// Graphical representation of a pipewire port. #[derive(gtk::CompositeTemplate, glib::Properties)] @@ -39,7 +39,7 @@ mod imp { #[template(file = "port.ui")] pub struct Port { #[property(get, set, construct_only)] - pub(super) pipewire_id: OnceCell, + pub(super) pipewire_id: Cell, #[property( type = u32, get = |_| self.media_type.get().as_raw(), @@ -70,7 +70,7 @@ mod imp { impl Default for Port { fn default() -> Self { Self { - pipewire_id: OnceCell::default(), + pipewire_id: Cell::new(0), media_type: Cell::new(MediaType::Unknown), direction: Cell::new(Direction::Output), label: TemplateChild::default(), @@ -112,7 +112,7 @@ mod imp { } fn signals() -> &'static [Signal] { - static SIGNALS: Lazy> = Lazy::new(|| { + static SIGNALS: LazyLock> = LazyLock::new(|| { vec![Signal::builder("port-toggled") // Provide id of output port and input port to signal handler. .param_types([::static_type(), ::static_type()]) @@ -218,6 +218,7 @@ mod imp { drag_src.connect_drag_begin(|drag_source, _| { let port = drag_source .widget() + .unwrap() .dynamic_cast::() .expect("Widget should be a Port"); @@ -226,6 +227,7 @@ mod imp { drag_src.connect_drag_cancel(|drag_source, _, _| { let port = drag_source .widget() + .unwrap() .dynamic_cast::() .expect("Widget should be a Port"); @@ -241,6 +243,7 @@ mod imp { drop_target.connect_value_notify(|drop_target| { let port = drop_target .widget() + .unwrap() .dynamic_cast::() .expect("Widget should be a Port"); @@ -260,6 +263,7 @@ mod imp { drop_target.connect_drop(|drop_target, val, _, _| { let port = drop_target .widget() + .unwrap() .dynamic_cast::() .expect("Widget should be a Port"); let other_port = val @@ -328,7 +332,7 @@ mod imp { glib::wrapper! { pub struct Port(ObjectSubclass) - @extends gtk::Widget; + @extends gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; } impl Port { @@ -341,21 +345,13 @@ impl Port { } pub fn link_anchor(&self) -> graphene::Point { - let style_context = self.style_context(); - let padding_right: f32 = style_context.padding().right().into(); - let border_right: f32 = style_context.border().right().into(); - let padding_left: f32 = style_context.padding().left().into(); - let border_left: f32 = style_context.border().left().into(); + let imp = self.imp(); + let handle = &imp.handle; + let (width, height) = (handle.width() as f32, handle.height() as f32); - let direction = Direction::from_raw(self.direction()); - graphene::Point::new( - match direction { - Direction::Output => self.width() as f32 + padding_right + border_right, - Direction::Input => 0.0 - padding_left - border_left, - _ => unreachable!(), - }, - self.height() as f32 / 2.0, - ) + handle + .compute_point(self, &graphene::Point::new(width / 2.0, height / 2.0)) + .expect("Failed to compute link anchor") } pub fn is_linkable_to(&self, other_port: &Self) -> bool { diff --git a/src/ui/graph/port_handle.rs b/src/ui/graph/port_handle.rs index a9ca8cc..7eb3a9a 100644 --- a/src/ui/graph/port_handle.rs +++ b/src/ui/graph/port_handle.rs @@ -61,7 +61,7 @@ mod imp { glib::wrapper! { pub struct PortHandle(ObjectSubclass) - @extends gtk::Widget; + @extends gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; } impl PortHandle { diff --git a/src/ui/graph/zoomentry.rs b/src/ui/graph/zoomentry.rs index 667236b..a53697b 100644 --- a/src/ui/graph/zoomentry.rs +++ b/src/ui/graph/zoomentry.rs @@ -8,7 +8,7 @@ mod imp { use super::*; use gtk::{gio, glib::clone}; - use once_cell::sync::Lazy; + use std::sync::LazyLock; #[derive(gtk::CompositeTemplate)] #[template(file = "zoomentry.ui")] @@ -65,37 +65,49 @@ mod imp { fn constructed(&self) { self.parent_constructed(); - self.zoom_out_button - .connect_clicked(clone!(@weak self as imp => move |_| { + self.zoom_out_button.connect_clicked(clone!( + #[weak(rename_to = imp)] + self, + move |_| { let graphview = imp.graphview.borrow(); if let Some(ref graphview) = *graphview { graphview.set_zoom_factor(graphview.zoom_factor() - 0.1, None); } - })); + } + )); - self.zoom_in_button - .connect_clicked(clone!(@weak self as imp => move |_| { + self.zoom_in_button.connect_clicked(clone!( + #[weak(rename_to = imp)] + self, + move |_| { let graphview = imp.graphview.borrow(); if let Some(ref graphview) = *graphview { graphview.set_zoom_factor(graphview.zoom_factor() + 0.1, None); } - })); + } + )); - self.entry - .connect_activate(clone!(@weak self as imp => move |entry| { + self.entry.connect_activate(clone!( + #[weak(rename_to = imp)] + self, + move |entry| { if let Ok(zoom_factor) = entry.text().trim_matches('%').parse::() { let graphview = imp.graphview.borrow(); if let Some(ref graphview) = *graphview { graphview.set_zoom_factor(zoom_factor / 100.0, None); } } - })); - self.entry - .connect_icon_press(clone!(@weak self as imp => move |_, pos| { + } + )); + self.entry.connect_icon_press(clone!( + #[weak(rename_to = imp)] + self, + move |_, pos| { if pos == gtk::EntryIconPosition::Secondary { - imp.popover.show(); + imp.popover.set_visible(true); } - })); + } + )); self.popover.set_parent(&self.entry.get()); } @@ -109,7 +121,7 @@ mod imp { } fn properties() -> &'static [glib::ParamSpec] { - static PROPERTIES: Lazy> = Lazy::new(|| { + static PROPERTIES: LazyLock> = LazyLock::new(|| { vec![glib::ParamSpecObject::builder::("zoomed-widget") .flags(glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT) .build()] @@ -132,9 +144,13 @@ mod imp { if let Some(ref widget) = widget { widget.connect_notify_local( Some("zoom-factor"), - clone!(@weak self as imp => move |graphview, _| { - imp.update_zoom_factor_text(graphview.zoom_factor()); - }), + clone!( + #[weak(rename_to = imp)] + self, + move |graphview, _| { + imp.update_zoom_factor_text(graphview.zoom_factor()); + } + ), ); self.update_zoom_factor_text(widget.zoom_factor()); } @@ -161,7 +177,7 @@ mod imp { glib::wrapper! { pub struct ZoomEntry(ObjectSubclass) - @extends gtk::Box, gtk::Widget; + @extends gtk::Box, gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable; } impl ZoomEntry { diff --git a/src/ui/window.rs b/src/ui/window.rs index e1e410b..1f321d0 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -49,7 +49,7 @@ mod imp { glib::wrapper! { pub struct Window(ObjectSubclass) @extends adw::ApplicationWindow, gtk::ApplicationWindow, gtk::Window, gtk::Widget, - @implements gio::ActionGroup, gio::ActionMap; + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager, gio::ActionGroup, gio::ActionMap; } impl Window {