mirror of
https://gitlab.freedesktop.org/pipewire/helvum
synced 2026-03-15 19:46:10 +08:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7dd6033a6 | ||
|
|
f32559511d | ||
|
|
57cba6381b | ||
|
|
d1b9b0f11f | ||
|
|
4549ba6ff5 | ||
|
|
e78d6f5fb4 | ||
|
|
96c079d29e | ||
|
|
5d4931b418 | ||
|
|
b983ade736 | ||
|
|
94d5e95695 | ||
|
|
e1f63ddd28 | ||
|
|
903df21ba3 | ||
|
|
39437eaf29 | ||
|
|
a1a4594a25 |
@@ -3,7 +3,7 @@ stages:
|
|||||||
- lint
|
- lint
|
||||||
|
|
||||||
.flatpak:
|
.flatpak:
|
||||||
image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-45'
|
image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-46'
|
||||||
variables:
|
variables:
|
||||||
FLATPAK_BUILD_DIR: _build
|
FLATPAK_BUILD_DIR: _build
|
||||||
MANIFEST_PATH: build-aux/org.pipewire.Helvum.json
|
MANIFEST_PATH: build-aux/org.pipewire.Helvum.json
|
||||||
|
|||||||
373
Cargo.lock
generated
373
Cargo.lock
generated
@@ -11,12 +11,35 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "annotate-snippets"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
"yansi-term",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.75"
|
version = "1.0.75"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-channel"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"event-listener",
|
||||||
|
"event-listener-strategy",
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -25,16 +48,17 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.66.1"
|
version = "0.69.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
|
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"annotate-snippets",
|
||||||
|
"bitflags",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
|
"itertools",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lazycell",
|
"lazycell",
|
||||||
"peeking_take_while",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex",
|
"regex",
|
||||||
@@ -43,12 +67,6 @@ dependencies = [
|
|||||||
"syn 2.0.37",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
@@ -57,23 +75,22 @@ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairo-rs"
|
name = "cairo-rs"
|
||||||
version = "0.18.2"
|
version = "0.19.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c0466dfa8c0ee78deef390c274ad756801e0a6dbb86c5ef0924a298c5761c4d"
|
checksum = "2650f66005301bd33cc486dec076e1293c4cecf768bc7ba9bf5d2b1be339b99c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags",
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
"glib",
|
"glib",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairo-sys-rs"
|
name = "cairo-sys-rs"
|
||||||
version = "0.18.2"
|
version = "0.19.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51"
|
checksum = "fd3bb3119664efbd78b5e6c93957447944f16bdbced84c17a9f41c7829b81e64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -125,6 +142,15 @@ dependencies = [
|
|||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concurrent-queue"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -140,19 +166,52 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b"
|
checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener"
|
||||||
|
version = "5.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"parking",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener-strategy"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "field-offset"
|
name = "field-offset"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
|
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memoffset 0.9.0",
|
"memoffset",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -221,22 +280,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk-pixbuf"
|
name = "gdk-pixbuf"
|
||||||
version = "0.18.0"
|
version = "0.19.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbc9c2ed73a81d556b65d08879ba4ee58808a6b1927ce915262185d6d547c6f3"
|
checksum = "f6a23f8a0b5090494fd04924662d463f8386cc678dd3915015a838c1a3679b92"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gdk-pixbuf-sys",
|
"gdk-pixbuf-sys",
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk-pixbuf-sys"
|
name = "gdk-pixbuf-sys"
|
||||||
version = "0.18.0"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7"
|
checksum = "3dcbd04c1b2c4834cc008b4828bc917d062483b88d26effde6342e5622028f96"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gio-sys",
|
"gio-sys",
|
||||||
"glib-sys",
|
"glib-sys",
|
||||||
@@ -247,9 +305,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk4"
|
name = "gdk4"
|
||||||
version = "0.7.3"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7edb019ad581f8ecf8ea8e4baa6df7c483a95b5a59be3140be6a9c3b0c632af6"
|
checksum = "9100b25604183f2fd97f55ef087fae96ab4934d7215118a35303e422688e6e4b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
"gdk-pixbuf",
|
"gdk-pixbuf",
|
||||||
@@ -262,9 +320,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk4-sys"
|
name = "gdk4-sys"
|
||||||
version = "0.7.2"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dbab43f332a3cf1df9974da690b5bb0e26720ed09a228178ce52175372dcfef0"
|
checksum = "d0b76874c40bb8d1c7d03a7231e23ac75fa577a456cd53af32ec17ec8f121626"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
"gdk-pixbuf-sys",
|
"gdk-pixbuf-sys",
|
||||||
@@ -279,9 +337,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gio"
|
name = "gio"
|
||||||
version = "0.18.2"
|
version = "0.19.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57052f84e8e5999b258e8adf8f5f2af0ac69033864936b8b6838321db2f759b1"
|
checksum = "c64947d08d7fbb03bf8ad1f25a8ac6cf4329bc772c9b7e5abe7bf9493c81194f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -290,7 +348,6 @@ dependencies = [
|
|||||||
"gio-sys",
|
"gio-sys",
|
||||||
"glib",
|
"glib",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -298,24 +355,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gio-sys"
|
name = "gio-sys"
|
||||||
version = "0.18.1"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
|
checksum = "bcf8e1d9219bb294636753d307b030c1e8a032062cba74f493c431a5c8b81ce4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys",
|
||||||
"gobject-sys",
|
"gobject-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
"winapi",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glib"
|
name = "glib"
|
||||||
version = "0.18.2"
|
version = "0.19.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c316afb01ce8067c5eaab1fc4f2cd47dc21ce7b6296358605e2ffab23ccbd19"
|
checksum = "01e191cc1af1f35b9699213107068cd3fe05d9816275ac118dc785a0dd8faebf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-executor",
|
"futures-executor",
|
||||||
@@ -328,20 +385,18 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glib-macros"
|
name = "glib-macros"
|
||||||
version = "0.18.2"
|
version = "0.19.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8da903822b136d42360518653fcf154455defc437d3e7a81475bf9a95ff1e47"
|
checksum = "9972bb91643d589c889654693a4f1d07697fdcb5d104b5c44fb68649ba1bf68d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.5.0",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro-error",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.37",
|
"syn 2.0.37",
|
||||||
@@ -349,9 +404,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glib-sys"
|
name = "glib-sys"
|
||||||
version = "0.18.1"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
|
checksum = "630f097773d7c7a0bb3258df4e8157b47dc98bbfa0e60ad9ab56174813feced4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"system-deps",
|
"system-deps",
|
||||||
@@ -365,9 +420,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gobject-sys"
|
name = "gobject-sys"
|
||||||
version = "0.18.0"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
|
checksum = "c85e2b1080b9418dd0c58b498da3a5c826030343e0ef07bde6a955d28de54979"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -376,9 +431,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphene-rs"
|
name = "graphene-rs"
|
||||||
version = "0.18.1"
|
version = "0.19.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b2228cda1505613a7a956cca69076892cfbda84fc2b7a62b94a41a272c0c401"
|
checksum = "99e4d388e96c5f29e2b2f67045d229ddf826d0a8d6d282f94ed3b34452222c91"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib",
|
"glib",
|
||||||
"graphene-sys",
|
"graphene-sys",
|
||||||
@@ -387,9 +442,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphene-sys"
|
name = "graphene-sys"
|
||||||
version = "0.18.1"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc4144cee8fc8788f2a9b73dc5f1d4e1189d1f95305c4cb7bd9c1af1cfa31f59"
|
checksum = "236ed66cc9b18d8adf233716f75de803d0bf6fc806f60d14d948974a12e240d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -399,9 +454,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gsk4"
|
name = "gsk4"
|
||||||
version = "0.7.3"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d958e351d2f210309b32d081c832d7de0aca0b077aa10d88336c6379bd01f7e"
|
checksum = "c65036fc8f99579e8cb37b12487969b707ab23ec8ab953682ff347cbd15d396e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
"gdk4",
|
"gdk4",
|
||||||
@@ -414,9 +469,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gsk4-sys"
|
name = "gsk4-sys"
|
||||||
version = "0.7.3"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12bd9e3effea989f020e8f1ff3fa3b8c63ba93d43b899c11a118868853a56d55"
|
checksum = "bd24c814379f9c3199dc53e52253ee8d0f657eae389ab282c330505289d24738"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
"gdk4-sys",
|
"gdk4-sys",
|
||||||
@@ -430,9 +485,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gtk4"
|
name = "gtk4"
|
||||||
version = "0.7.3"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5aeb51aa3e9728575a053e1f43543cd9992ac2477e1b186ad824fd4adfb70842"
|
checksum = "aa82753b8c26277e4af1446c70e35b19aad4fb794a7b143859e7eeb9a4025d83"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
"field-offset",
|
"field-offset",
|
||||||
@@ -451,9 +506,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gtk4-macros"
|
name = "gtk4-macros"
|
||||||
version = "0.7.2"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d57ec49cf9b657f69a05bca8027cff0a8dfd0c49e812be026fc7311f2163832f"
|
checksum = "40300bf071d2fcd4c94eacc09e84ec6fe73129d2ceb635cf7e55b026b5443567"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
@@ -465,9 +520,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gtk4-sys"
|
name = "gtk4-sys"
|
||||||
version = "0.7.3"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "54d8c4aa23638ce9faa2caf7e2a27d4a1295af2155c8e8d28c4d4eeca7a65eb8"
|
checksum = "0db1b104138f087ccdc81d2c332de5dd049b89de3d384437cc1093b17cd2da18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
"gdk-pixbuf-sys",
|
"gdk-pixbuf-sys",
|
||||||
@@ -495,10 +550,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "helvum"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "helvum"
|
||||||
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-channel",
|
||||||
"glib",
|
"glib",
|
||||||
|
"gtk4",
|
||||||
"libadwaita",
|
"libadwaita",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -516,6 +579,15 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -530,9 +602,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libadwaita"
|
name = "libadwaita"
|
||||||
version = "0.5.3"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fe7e70c06507ed10a16cda707f358fbe60fe0dc237498f78c686ade92fd979c"
|
checksum = "91b4990248b9e1ec5e72094a2ccaea70ec3809f88f6fd52192f2af306b87c5d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gdk-pixbuf",
|
"gdk-pixbuf",
|
||||||
"gdk4",
|
"gdk4",
|
||||||
@@ -546,9 +618,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libadwaita-sys"
|
name = "libadwaita-sys"
|
||||||
version = "0.5.3"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e10aaa38de1d53374f90deeb4535209adc40cc5dba37f9704724169bceec69a"
|
checksum = "23a748e4e92be1265cd9e93d569c0b5dfc7814107985aa6743d670ab281ea1a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gdk4-sys",
|
"gdk4-sys",
|
||||||
"gio-sys",
|
"gio-sys",
|
||||||
@@ -578,11 +650,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libspa"
|
name = "libspa"
|
||||||
version = "0.7.2"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0434617020ddca18b86067912970c55410ca654cdafd775480322f50b857a8c4"
|
checksum = "65f3a4b81b2a2d8c7f300643676202debd1b7c929dbf5c9bb89402ea11d19810"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags",
|
||||||
"cc",
|
"cc",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"cookie-factory",
|
"cookie-factory",
|
||||||
@@ -595,9 +667,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libspa-sys"
|
name = "libspa-sys"
|
||||||
version = "0.7.2"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3e70ca3f3e70f858ef363046d06178c427b4e0b63d210c95fd87d752679d345"
|
checksum = "bf0d9716420364790e85cbb9d3ac2c950bde16a7dd36f3209b7dfdfc4a24d01f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -612,18 +684,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.3"
|
version = "2.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
|
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memoffset"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
@@ -642,15 +705,13 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.26.4"
|
version = "0.27.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"memoffset 0.7.1",
|
|
||||||
"pin-utils",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -665,28 +726,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.18.0"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pango"
|
name = "pango"
|
||||||
version = "0.18.0"
|
version = "0.19.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06a9e54b831d033206160096b825f2070cf5fda7e35167b1c01e9e774f9202d1"
|
checksum = "b1264d13deb823cc652f26cfe59afb1ec4b9db2a5bd27c41b738c879cc1bfaa1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
|
||||||
"pango-sys",
|
"pango-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pango-sys"
|
name = "pango-sys"
|
||||||
version = "0.18.0"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5"
|
checksum = "f52ef6a881c19fbfe3b1484df5cad411acaaba29dbec843941c3110d19f340ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib-sys",
|
"glib-sys",
|
||||||
"gobject-sys",
|
"gobject-sys",
|
||||||
@@ -695,10 +755,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peeking_take_while"
|
name = "parking"
|
||||||
version = "0.1.2"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
@@ -714,12 +774,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pipewire"
|
name = "pipewire"
|
||||||
version = "0.7.2"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2d009c8dd65e890b515a71950f7e4c801523b8894ff33863a40830bf762e9e9"
|
checksum = "08e645ba5c45109106d56610b3ee60eb13a6f2beb8b74f8dc8186cf261788dda"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.4.0",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
"libspa",
|
"libspa",
|
||||||
"libspa-sys",
|
"libspa-sys",
|
||||||
@@ -731,9 +791,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pipewire-sys"
|
name = "pipewire-sys"
|
||||||
version = "0.7.2"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "890c084e7b737246cb4799c86b71a0e4da536031ff7473dd639eba9f95039f64"
|
checksum = "849e188f90b1dda88fe2bfe1ad31fe5f158af2c98f80fb5d13726c44f3f01112"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"libspa-sys",
|
"libspa-sys",
|
||||||
@@ -742,18 +802,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.27"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "proc-macro-crate"
|
||||||
version = "1.3.1"
|
version = "3.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"toml_edit 0.21.1",
|
||||||
"toml_edit",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -894,9 +953,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.11.1"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
@@ -927,7 +986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3"
|
checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-expr",
|
"cfg-expr",
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"toml",
|
"toml",
|
||||||
"version-compare",
|
"version-compare",
|
||||||
@@ -968,14 +1027,14 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"toml_edit",
|
"toml_edit 0.19.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.3"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -993,6 +1052,17 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.21.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
@@ -1005,6 +1075,12 @@ version = "1.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version-compare"
|
name = "version-compare"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -1039,6 +1115,72 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.15"
|
version = "0.5.15"
|
||||||
@@ -1047,3 +1189,12 @@ checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yansi-term"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|||||||
12
Cargo.toml
12
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "helvum"
|
name = "helvum"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
authors = ["Tom Wagner <tom.a.wagner@protonmail.com>"]
|
authors = ["Tom Wagner <tom.a.wagner@protonmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
@@ -14,12 +14,14 @@ categories = ["gui", "multimedia"]
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pipewire = "0.7.1"
|
pipewire = "0.8.0"
|
||||||
adw = { version = "0.5", package = "libadwaita", features = ["v1_3"] }
|
adw = { version = "0.6", package = "libadwaita", features = ["v1_4"] }
|
||||||
glib = { version = "0.18", features = ["log"] }
|
gtk = { version = "0.8", package = "gtk4", features = ["v4_14"] }
|
||||||
|
glib = { version = "0.19", features = ["log"] }
|
||||||
|
async-channel = "2.2"
|
||||||
|
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
|
|
||||||
once_cell = "1.7.2"
|
once_cell = "1.19"
|
||||||
|
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ $ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.fl
|
|||||||
|
|
||||||
Then install the required flatpak platform and SDK, if you dont have them already:
|
Then install the required flatpak platform and SDK, if you dont have them already:
|
||||||
```shell
|
```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}//46 org.freedesktop.Sdk.Extension.rust-stable//23.08 org.freedesktop.Sdk.Extension.llvm16//23.08
|
||||||
```
|
```
|
||||||
|
|
||||||
To compile and install as a flatpak, clone the project, change to the project directory, and run:
|
To compile and install as a flatpak, clone the project, change to the project directory, and run:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "org.pipewire.Helvum",
|
"id": "org.pipewire.Helvum",
|
||||||
"runtime": "org.gnome.Platform",
|
"runtime": "org.gnome.Platform",
|
||||||
"runtime-version": "45",
|
"runtime-version": "46",
|
||||||
"sdk": "org.gnome.Sdk",
|
"sdk": "org.gnome.Sdk",
|
||||||
"sdk-extensions": [
|
"sdk-extensions": [
|
||||||
"org.freedesktop.Sdk.Extension.rust-stable",
|
"org.freedesktop.Sdk.Extension.rust-stable",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
<url type="bugtracker">https://gitlab.freedesktop.org/pipewire/helvum/-/issues</url>
|
<url type="bugtracker">https://gitlab.freedesktop.org/pipewire/helvum/-/issues</url>
|
||||||
<content_rating type="oars-1.0" />
|
<content_rating type="oars-1.0" />
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="0.5.1" date="2023-09-28" />
|
||||||
<release version="0.5.0" date="2023-09-28" />
|
<release version="0.5.0" date="2023-09-28" />
|
||||||
<release version="0.4.1" date="2023-08-18" />
|
<release version="0.4.1" date="2023-08-18" />
|
||||||
<release version="0.4.0" date="2023-02-12" />
|
<release version="0.4.0" date="2023-02-12" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'helvum',
|
'helvum',
|
||||||
'rust',
|
'rust',
|
||||||
version: '0.5.0',
|
version: '0.5.1',
|
||||||
license: 'GPL-3.0',
|
license: 'GPL-3.0',
|
||||||
meson_version: '>=0.59.0'
|
meson_version: '>=0.59.0'
|
||||||
)
|
)
|
||||||
@@ -11,8 +11,8 @@ gnome = import('gnome')
|
|||||||
base_id = 'org.pipewire.Helvum'
|
base_id = 'org.pipewire.Helvum'
|
||||||
|
|
||||||
dependency('glib-2.0', version: '>= 2.66')
|
dependency('glib-2.0', version: '>= 2.66')
|
||||||
dependency('gtk4', version: '>= 4.4.0')
|
dependency('gtk4', version: '>= 4.14.0')
|
||||||
dependency('libadwaita-1', version: '>= 1.3')
|
dependency('libadwaita-1', version: '>= 1.4')
|
||||||
dependency('libpipewire-0.3')
|
dependency('libpipewire-0.3')
|
||||||
|
|
||||||
desktop_file_validate = find_program('desktop-file-validate', required: false)
|
desktop_file_validate = find_program('desktop-file-validate', required: false)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use adw::{
|
use adw::{
|
||||||
gio,
|
gio,
|
||||||
glib::{self, clone, Receiver},
|
glib::{self, clone},
|
||||||
gtk,
|
gtk,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
subclass::prelude::*,
|
subclass::prelude::*,
|
||||||
@@ -33,8 +33,9 @@ static AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
|||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use std::cell::OnceCell;
|
||||||
|
|
||||||
use adw::subclass::prelude::AdwApplicationImpl;
|
use adw::subclass::prelude::AdwApplicationImpl;
|
||||||
use once_cell::unsync::OnceCell;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Application {
|
pub struct Application {
|
||||||
@@ -112,9 +113,12 @@ mod imp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn show_about_dialog(&self) {
|
fn show_about_dialog(&self) {
|
||||||
|
let obj = &*self.obj();
|
||||||
|
let window = obj.active_window().unwrap();
|
||||||
let authors: Vec<&str> = AUTHORS.split(':').collect();
|
let authors: Vec<&str> = AUTHORS.split(':').collect();
|
||||||
|
|
||||||
let about_window = adw::AboutWindow::builder()
|
let about_window = adw::AboutWindow::builder()
|
||||||
|
.transient_for(&window)
|
||||||
.application_icon(APP_ID)
|
.application_icon(APP_ID)
|
||||||
.application_name("Helvum")
|
.application_name("Helvum")
|
||||||
.developer_name("Tom Wagner")
|
.developer_name("Tom Wagner")
|
||||||
@@ -140,7 +144,7 @@ impl Application {
|
|||||||
/// Create the view.
|
/// Create the view.
|
||||||
/// This will set up the entire user interface and prepare it for being run.
|
/// This will set up the entire user interface and prepare it for being run.
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
gtk_receiver: Receiver<PipewireMessage>,
|
gtk_receiver: async_channel::Receiver<PipewireMessage>,
|
||||||
pw_sender: Sender<GtkMessage>,
|
pw_sender: Sender<GtkMessage>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let app: Application = glib::Object::builder()
|
let app: Application = glib::Object::builder()
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ use crate::{ui::graph::GraphView, GtkMessage, PipewireMessage};
|
|||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::{cell::RefCell, collections::HashMap};
|
use std::{cell::OnceCell, cell::RefCell, collections::HashMap};
|
||||||
|
|
||||||
use once_cell::unsync::OnceCell;
|
|
||||||
|
|
||||||
use crate::{ui::graph, MediaType, NodeType};
|
use crate::{ui::graph, MediaType, NodeType};
|
||||||
|
|
||||||
@@ -53,35 +51,58 @@ mod imp {
|
|||||||
impl ObjectImpl for GraphManager {}
|
impl ObjectImpl for GraphManager {}
|
||||||
|
|
||||||
impl GraphManager {
|
impl GraphManager {
|
||||||
pub fn attach_receiver(&self, receiver: glib::Receiver<crate::PipewireMessage>) {
|
pub async fn receive(&self, receiver: async_channel::Receiver<crate::PipewireMessage>) {
|
||||||
receiver.attach(None, glib::clone!(
|
loop {
|
||||||
@weak self as imp => @default-return glib::ControlFlow::Continue,
|
let Ok(msg) = receiver.recv().await else {
|
||||||
move |msg| {
|
continue;
|
||||||
|
};
|
||||||
match msg {
|
match msg {
|
||||||
PipewireMessage::NodeAdded { id, name, node_type } => imp.add_node(id, name.as_str(), node_type),
|
PipewireMessage::NodeAdded {
|
||||||
PipewireMessage::PortAdded { id, node_id, name, direction } => imp.add_port(id, name.as_str(), node_id, direction),
|
id,
|
||||||
PipewireMessage::PortFormatChanged { id, media_type } => imp.port_media_type_changed(id, media_type),
|
name,
|
||||||
|
node_type,
|
||||||
|
} => self.add_node(id, name.as_str(), node_type),
|
||||||
|
PipewireMessage::NodeNameChanged {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
media_name,
|
||||||
|
} => self.node_name_changed(id, &name, &media_name),
|
||||||
|
PipewireMessage::PortAdded {
|
||||||
|
id,
|
||||||
|
node_id,
|
||||||
|
name,
|
||||||
|
direction,
|
||||||
|
} => self.add_port(id, name.as_str(), node_id, direction),
|
||||||
|
PipewireMessage::PortFormatChanged { id, media_type } => {
|
||||||
|
self.port_media_type_changed(id, media_type)
|
||||||
|
}
|
||||||
PipewireMessage::LinkAdded {
|
PipewireMessage::LinkAdded {
|
||||||
id, port_from, port_to, active, media_type
|
id,
|
||||||
} => imp.add_link(id, port_from, port_to, active, media_type),
|
port_from,
|
||||||
PipewireMessage::LinkStateChanged { id, active } => imp.link_state_changed(id, active),
|
port_to,
|
||||||
PipewireMessage::LinkFormatChanged { id, media_type } => imp.link_format_changed(id, media_type),
|
active,
|
||||||
PipewireMessage::NodeRemoved { id } => imp.remove_node(id),
|
media_type,
|
||||||
PipewireMessage::PortRemoved { id, node_id } => imp.remove_port(id, node_id),
|
} => self.add_link(id, port_from, port_to, active, media_type),
|
||||||
PipewireMessage::LinkRemoved { id } => imp.remove_link(id),
|
PipewireMessage::LinkStateChanged { id, active } => {
|
||||||
|
self.link_state_changed(id, active)
|
||||||
|
}
|
||||||
|
PipewireMessage::LinkFormatChanged { id, media_type } => {
|
||||||
|
self.link_format_changed(id, media_type)
|
||||||
|
}
|
||||||
|
PipewireMessage::NodeRemoved { id } => self.remove_node(id),
|
||||||
|
PipewireMessage::PortRemoved { id, node_id } => self.remove_port(id, node_id),
|
||||||
|
PipewireMessage::LinkRemoved { id } => self.remove_link(id),
|
||||||
PipewireMessage::Connecting => {
|
PipewireMessage::Connecting => {
|
||||||
imp.obj().connection_banner().set_revealed(true);
|
self.obj().connection_banner().set_revealed(true);
|
||||||
}
|
}
|
||||||
PipewireMessage::Connected => {
|
PipewireMessage::Connected => {
|
||||||
imp.obj().connection_banner().set_revealed(false);
|
self.obj().connection_banner().set_revealed(false);
|
||||||
},
|
}
|
||||||
PipewireMessage::Disconnected => {
|
PipewireMessage::Disconnected => {
|
||||||
imp.clear();
|
self.clear();
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
glib::ControlFlow::Continue
|
|
||||||
}
|
}
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new node to the view.
|
/// Add a new node to the view.
|
||||||
@@ -95,6 +116,23 @@ mod imp {
|
|||||||
self.obj().graph().add_node(node, node_type);
|
self.obj().graph().add_node(node, node_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update a node tooltip to the view.
|
||||||
|
fn node_name_changed(&self, id: u32, node_name: &str, media_name: &str) {
|
||||||
|
let items = self.items.borrow();
|
||||||
|
|
||||||
|
let Some(node) = items.get(&id) else {
|
||||||
|
log::warn!("Node (id: {id}) for changed name not found in graph manager");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(node) = node.dynamic_cast_ref::<graph::Node>() else {
|
||||||
|
log::warn!("Graph Manager item under node (id: {id}) is not a node");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
node.set_node_name(node_name);
|
||||||
|
node.set_media_name(media_name);
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove the node with the specified id from the view.
|
/// Remove the node with the specified id from the view.
|
||||||
fn remove_node(&self, id: u32) {
|
fn remove_node(&self, id: u32) {
|
||||||
log::info!("Removing node from graph: id {}", id);
|
log::info!("Removing node from graph: id {}", id);
|
||||||
@@ -112,7 +150,13 @@ mod imp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new port to the view.
|
/// 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: pipewire::spa::utils::Direction,
|
||||||
|
) {
|
||||||
log::info!("Adding port to graph: id {}", id);
|
log::info!("Adding port to graph: id {}", id);
|
||||||
|
|
||||||
let mut items = self.items.borrow_mut();
|
let mut items = self.items.borrow_mut();
|
||||||
@@ -255,7 +299,11 @@ mod imp {
|
|||||||
link.set_active(active);
|
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: pipewire::spa::param::format::MediaType,
|
||||||
|
) {
|
||||||
let items = self.items.borrow();
|
let items = self.items.borrow();
|
||||||
|
|
||||||
let Some(link) = items.get(&id) else {
|
let Some(link) = items.get(&id) else {
|
||||||
@@ -304,19 +352,23 @@ glib::wrapper! {
|
|||||||
pub struct GraphManager(ObjectSubclass<imp::GraphManager>);
|
pub struct GraphManager(ObjectSubclass<imp::GraphManager>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn receive(graph_manager: GraphManager, receiver: async_channel::Receiver<PipewireMessage>) {
|
||||||
|
graph_manager.imp().receive(receiver).await
|
||||||
|
}
|
||||||
|
|
||||||
impl GraphManager {
|
impl GraphManager {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
graph: &GraphView,
|
graph: &GraphView,
|
||||||
connection_banner: &adw::Banner,
|
connection_banner: &adw::Banner,
|
||||||
sender: PwSender<GtkMessage>,
|
sender: PwSender<GtkMessage>,
|
||||||
receiver: glib::Receiver<PipewireMessage>,
|
receiver: async_channel::Receiver<PipewireMessage>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let res: Self = glib::Object::builder()
|
let res: Self = glib::Object::builder()
|
||||||
.property("graph", graph)
|
.property("graph", graph)
|
||||||
.property("connection-banner", connection_banner)
|
.property("connection-banner", connection_banner)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
res.imp().attach_receiver(receiver);
|
glib::MainContext::default().spawn_local(receive(res.clone(), receiver));
|
||||||
assert!(
|
assert!(
|
||||||
res.imp().pw_sender.set(sender).is_ok(),
|
res.imp().pw_sender.set(sender).is_ok(),
|
||||||
"Should be able to set pw_sender)"
|
"Should be able to set pw_sender)"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ mod pipewire_connection;
|
|||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use adw::{gtk, prelude::*};
|
use adw::{gtk, prelude::*};
|
||||||
use pipewire::spa::{format::MediaType, Direction};
|
use pipewire::spa::{param::format::MediaType, utils::Direction};
|
||||||
|
|
||||||
/// Messages sent by the GTK thread to notify the pipewire thread.
|
/// Messages sent by the GTK thread to notify the pipewire thread.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -39,6 +39,11 @@ pub enum PipewireMessage {
|
|||||||
name: String,
|
name: String,
|
||||||
node_type: Option<NodeType>,
|
node_type: Option<NodeType>,
|
||||||
},
|
},
|
||||||
|
NodeNameChanged {
|
||||||
|
id: u32,
|
||||||
|
name: String,
|
||||||
|
media_name: String,
|
||||||
|
},
|
||||||
PortAdded {
|
PortAdded {
|
||||||
id: u32,
|
id: u32,
|
||||||
node_id: u32,
|
node_id: u32,
|
||||||
@@ -115,7 +120,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
// Start the pipewire thread with channels in both directions.
|
// 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::unbounded();
|
||||||
let (pw_sender, pw_receiver) = pipewire::channel::channel();
|
let (pw_sender, pw_receiver) = pipewire::channel::channel();
|
||||||
let pw_thread =
|
let pw_thread =
|
||||||
std::thread::spawn(move || pipewire_connection::thread_main(gtk_sender, pw_receiver));
|
std::thread::spawn(move || pipewire_connection::thread_main(gtk_sender, pw_receiver));
|
||||||
|
|||||||
@@ -26,23 +26,31 @@ use std::{
|
|||||||
use adw::glib::{self, clone};
|
use adw::glib::{self, clone};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use pipewire::{
|
use pipewire::{
|
||||||
link::{Link, LinkChangeMask, LinkInfo, LinkListener, LinkState},
|
context::Context,
|
||||||
port::{Port, PortChangeMask, PortInfo, PortListener},
|
core::{Core, PW_ID_CORE},
|
||||||
prelude::*,
|
keys,
|
||||||
properties,
|
link::{Link, LinkChangeMask, LinkInfoRef, LinkListener, LinkState},
|
||||||
|
main_loop::MainLoop,
|
||||||
|
node::{Node, NodeInfoRef, NodeListener},
|
||||||
|
port::{Port, PortChangeMask, PortInfoRef, PortListener},
|
||||||
|
properties::properties,
|
||||||
registry::{GlobalObject, Registry},
|
registry::{GlobalObject, Registry},
|
||||||
spa::{
|
spa::{
|
||||||
param::{ParamInfoFlags, ParamType},
|
param::{ParamInfoFlags, ParamType},
|
||||||
ForeignDict, SpaResult,
|
utils::dict::DictRef,
|
||||||
|
utils::result::SpaResult,
|
||||||
},
|
},
|
||||||
types::ObjectType,
|
types::ObjectType,
|
||||||
Context, Core, MainLoop,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{GtkMessage, MediaType, NodeType, PipewireMessage};
|
use crate::{GtkMessage, MediaType, NodeType, PipewireMessage};
|
||||||
use state::{Item, State};
|
use state::{Item, State};
|
||||||
|
|
||||||
enum ProxyItem {
|
enum ProxyItem {
|
||||||
|
Node {
|
||||||
|
_proxy: Node,
|
||||||
|
_listener: NodeListener,
|
||||||
|
},
|
||||||
Port {
|
Port {
|
||||||
proxy: Port,
|
proxy: Port,
|
||||||
_listener: PortListener,
|
_listener: PortListener,
|
||||||
@@ -55,36 +63,40 @@ enum ProxyItem {
|
|||||||
|
|
||||||
/// The "main" function of the pipewire thread.
|
/// The "main" function of the pipewire thread.
|
||||||
pub(super) fn thread_main(
|
pub(super) fn thread_main(
|
||||||
gtk_sender: glib::Sender<PipewireMessage>,
|
gtk_sender: async_channel::Sender<PipewireMessage>,
|
||||||
mut pw_receiver: pipewire::channel::Receiver<GtkMessage>,
|
mut pw_receiver: pipewire::channel::Receiver<GtkMessage>,
|
||||||
) {
|
) {
|
||||||
let mainloop = MainLoop::new().expect("Failed to create mainloop");
|
let mainloop = MainLoop::new(None).expect("Failed to create mainloop");
|
||||||
let context = Rc::new(Context::new(&mainloop).expect("Failed to create context"));
|
let context = Rc::new(Context::new(&mainloop).expect("Failed to create context"));
|
||||||
let is_stopped = Rc::new(Cell::new(false));
|
let is_stopped = Rc::new(Cell::new(false));
|
||||||
let mut is_connecting = false;
|
let mut is_connecting = false;
|
||||||
|
|
||||||
while !is_stopped.get() {
|
while !is_stopped.get() {
|
||||||
// Try to connect
|
// Try to connect
|
||||||
let core = match context.connect(None) {
|
let core = match context.connect(Some(properties! {
|
||||||
|
"media.category" => "Manager"
|
||||||
|
})) {
|
||||||
Ok(core) => Rc::new(core),
|
Ok(core) => Rc::new(core),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
if !is_connecting {
|
if !is_connecting {
|
||||||
is_connecting = true;
|
is_connecting = true;
|
||||||
gtk_sender
|
gtk_sender
|
||||||
.send(PipewireMessage::Connecting)
|
.send_blocking(PipewireMessage::Connecting)
|
||||||
.expect("Failed to send message");
|
.expect("Failed to send message");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If connection is failed, try to connect again in 200ms
|
// If connection is failed, try to connect again in 200ms
|
||||||
let interval = Some(Duration::from_millis(200));
|
let interval = Some(Duration::from_millis(200));
|
||||||
|
|
||||||
let timer = mainloop.add_timer(clone!(@strong mainloop => move |_| {
|
let timer = mainloop
|
||||||
|
.loop_()
|
||||||
|
.add_timer(clone!(@strong mainloop => move |_| {
|
||||||
mainloop.quit();
|
mainloop.quit();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
timer.update_timer(interval, None).into_result().unwrap();
|
timer.update_timer(interval, None).into_result().unwrap();
|
||||||
|
|
||||||
let receiver = pw_receiver.attach(&mainloop, {
|
let receiver = pw_receiver.attach(mainloop.loop_(), {
|
||||||
clone!(@strong mainloop, @strong is_stopped => move |msg|
|
clone!(@strong mainloop, @strong is_stopped => move |msg|
|
||||||
if let GtkMessage::Terminate = msg {
|
if let GtkMessage::Terminate = msg {
|
||||||
// main thread requested stop
|
// main thread requested stop
|
||||||
@@ -104,7 +116,7 @@ pub(super) fn thread_main(
|
|||||||
if is_connecting {
|
if is_connecting {
|
||||||
is_connecting = false;
|
is_connecting = false;
|
||||||
gtk_sender
|
gtk_sender
|
||||||
.send(PipewireMessage::Connected)
|
.send_blocking(PipewireMessage::Connected)
|
||||||
.expect("Failed to send message");
|
.expect("Failed to send message");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +126,7 @@ pub(super) fn thread_main(
|
|||||||
let proxies = Rc::new(RefCell::new(HashMap::new()));
|
let proxies = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let state = Rc::new(RefCell::new(State::new()));
|
let state = Rc::new(RefCell::new(State::new()));
|
||||||
|
|
||||||
let receiver = pw_receiver.attach(&mainloop, {
|
let receiver = pw_receiver.attach(mainloop.loop_(), {
|
||||||
clone!(@strong mainloop, @weak core, @weak registry, @strong state, @strong is_stopped => move |msg| match msg {
|
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::ToggleLink { port_from, port_to } => toggle_link(port_from, port_to, &core, ®istry, &state),
|
||||||
GtkMessage::Terminate => {
|
GtkMessage::Terminate => {
|
||||||
@@ -128,12 +140,12 @@ pub(super) fn thread_main(
|
|||||||
let gtk_sender = gtk_sender.clone();
|
let gtk_sender = gtk_sender.clone();
|
||||||
let _listener = core.add_listener_local()
|
let _listener = core.add_listener_local()
|
||||||
.error(clone!(@strong mainloop, @strong gtk_sender, @strong is_stopped => move |id, _seq, res, message| {
|
.error(clone!(@strong mainloop, @strong gtk_sender, @strong is_stopped => move |id, _seq, res, message| {
|
||||||
if id != pipewire::PW_ID_CORE {
|
if id != PW_ID_CORE {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if res == -libc::EPIPE {
|
if res == -libc::EPIPE {
|
||||||
gtk_sender.send(PipewireMessage::Disconnected)
|
gtk_sender.send_blocking(PipewireMessage::Disconnected)
|
||||||
.expect("Failed to send message");
|
.expect("Failed to send message");
|
||||||
mainloop.quit();
|
mainloop.quit();
|
||||||
} else {
|
} else {
|
||||||
@@ -147,7 +159,7 @@ pub(super) fn thread_main(
|
|||||||
.add_listener_local()
|
.add_listener_local()
|
||||||
.global(clone!(@strong gtk_sender, @weak registry, @strong proxies, @strong state =>
|
.global(clone!(@strong gtk_sender, @weak registry, @strong proxies, @strong state =>
|
||||||
move |global| match global.type_ {
|
move |global| match global.type_ {
|
||||||
ObjectType::Node => handle_node(global, >k_sender, &state),
|
ObjectType::Node => handle_node(global, >k_sender, ®istry, &proxies, &state),
|
||||||
ObjectType::Port => handle_port(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),
|
ObjectType::Link => handle_link(global, >k_sender, ®istry, &proxies, &state),
|
||||||
_ => {
|
_ => {
|
||||||
@@ -157,7 +169,7 @@ pub(super) fn thread_main(
|
|||||||
))
|
))
|
||||||
.global_remove(clone!(@strong proxies, @strong state => move |id| {
|
.global_remove(clone!(@strong proxies, @strong state => move |id| {
|
||||||
if let Some(item) = state.borrow_mut().remove(id) {
|
if let Some(item) = state.borrow_mut().remove(id) {
|
||||||
gtk_sender.send(match item {
|
gtk_sender.send_blocking(match item {
|
||||||
Item::Node { .. } => PipewireMessage::NodeRemoved {id},
|
Item::Node { .. } => PipewireMessage::NodeRemoved {id},
|
||||||
Item::Port { node_id } => PipewireMessage::PortRemoved {id, node_id},
|
Item::Port { node_id } => PipewireMessage::PortRemoved {id, node_id},
|
||||||
Item::Link { .. } => PipewireMessage::LinkRemoved {id},
|
Item::Link { .. } => PipewireMessage::LinkRemoved {id},
|
||||||
@@ -178,10 +190,21 @@ 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: &DictRef) -> &str {
|
||||||
|
props
|
||||||
|
.get(&keys::NODE_DESCRIPTION)
|
||||||
|
.or_else(|| props.get(&keys::NODE_NICK))
|
||||||
|
.or_else(|| props.get(&keys::NODE_NAME))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle a new node being added
|
/// Handle a new node being added
|
||||||
fn handle_node(
|
fn handle_node(
|
||||||
node: &GlobalObject<ForeignDict>,
|
node: &GlobalObject<&DictRef>,
|
||||||
sender: &glib::Sender<PipewireMessage>,
|
sender: &async_channel::Sender<PipewireMessage>,
|
||||||
|
registry: &Rc<Registry>,
|
||||||
|
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
|
||||||
state: &Rc<RefCell<State>>,
|
state: &Rc<RefCell<State>>,
|
||||||
) {
|
) {
|
||||||
let props = node
|
let props = node
|
||||||
@@ -189,15 +212,7 @@ fn handle_node(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("Node object is missing properties");
|
.expect("Node object is missing properties");
|
||||||
|
|
||||||
// Get the nicest possible name for the node, using a fallback chain of possible name attributes.
|
let name = get_node_name(props).to_string();
|
||||||
let name = String::from(
|
|
||||||
props
|
|
||||||
.get("node.description")
|
|
||||||
.or_else(|| props.get("node.nick"))
|
|
||||||
.or_else(|| props.get("node.name"))
|
|
||||||
.unwrap_or_default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let media_class = |class: &str| {
|
let media_class = |class: &str| {
|
||||||
if class.contains("Sink") || class.contains("Input") {
|
if class.contains("Sink") || class.contains("Input") {
|
||||||
Some(NodeType::Input)
|
Some(NodeType::Input)
|
||||||
@@ -222,18 +237,62 @@ fn handle_node(
|
|||||||
state.borrow_mut().insert(node.id, Item::Node);
|
state.borrow_mut().insert(node.id, Item::Node);
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send(PipewireMessage::NodeAdded {
|
.send_blocking(PipewireMessage::NodeAdded {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
name,
|
name,
|
||||||
node_type,
|
node_type,
|
||||||
})
|
})
|
||||||
.expect("Failed to send message");
|
.expect("Failed to send message");
|
||||||
|
|
||||||
|
let proxy: Node = registry.bind(node).expect("Failed to bind to node proxy");
|
||||||
|
let listener = proxy
|
||||||
|
.add_listener_local()
|
||||||
|
.info(clone!(@strong sender, @strong proxies => move |info| {
|
||||||
|
handle_node_info(info, &sender, &proxies);
|
||||||
|
}))
|
||||||
|
.register();
|
||||||
|
|
||||||
|
proxies.borrow_mut().insert(
|
||||||
|
node.id,
|
||||||
|
ProxyItem::Node {
|
||||||
|
_proxy: proxy,
|
||||||
|
_listener: listener,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_node_info(
|
||||||
|
info: &NodeInfoRef,
|
||||||
|
sender: &async_channel::Sender<PipewireMessage>,
|
||||||
|
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
|
||||||
|
) {
|
||||||
|
debug!("Received node info: {:?}", info);
|
||||||
|
|
||||||
|
let id = info.id();
|
||||||
|
let proxies = proxies.borrow();
|
||||||
|
let Some(ProxyItem::Node { .. }) = proxies.get(&id) else {
|
||||||
|
error!("Received info on unknown node with id {id}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let props = info.props().expect("NodeInfo object is missing properties");
|
||||||
|
if let Some(media_name) = props.get(&keys::MEDIA_NAME) {
|
||||||
|
let name = get_node_name(props).to_string();
|
||||||
|
|
||||||
|
sender
|
||||||
|
.send_blocking(PipewireMessage::NodeNameChanged {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
media_name: media_name.to_string(),
|
||||||
|
})
|
||||||
|
.expect("Failed to send message");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a new port being added
|
/// Handle a new port being added
|
||||||
fn handle_port(
|
fn handle_port(
|
||||||
port: &GlobalObject<ForeignDict>,
|
port: &GlobalObject<&DictRef>,
|
||||||
sender: &glib::Sender<PipewireMessage>,
|
sender: &async_channel::Sender<PipewireMessage>,
|
||||||
registry: &Rc<Registry>,
|
registry: &Rc<Registry>,
|
||||||
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
|
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
|
||||||
state: &Rc<RefCell<State>>,
|
state: &Rc<RefCell<State>>,
|
||||||
@@ -264,10 +323,10 @@ fn handle_port(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_port_info(
|
fn handle_port_info(
|
||||||
info: &PortInfo,
|
info: &PortInfoRef,
|
||||||
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
|
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
|
||||||
state: &Rc<RefCell<State>>,
|
state: &Rc<RefCell<State>>,
|
||||||
sender: &glib::Sender<PipewireMessage>,
|
sender: &async_channel::Sender<PipewireMessage>,
|
||||||
) {
|
) {
|
||||||
debug!("Received port info: {:?}", info);
|
debug!("Received port info: {:?}", info);
|
||||||
|
|
||||||
@@ -308,7 +367,7 @@ fn handle_port_info(
|
|||||||
}
|
}
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send(PipewireMessage::PortAdded {
|
.send_blocking(PipewireMessage::PortAdded {
|
||||||
id,
|
id,
|
||||||
node_id,
|
node_id,
|
||||||
name,
|
name,
|
||||||
@@ -321,7 +380,7 @@ fn handle_port_info(
|
|||||||
fn handle_port_enum_format(
|
fn handle_port_enum_format(
|
||||||
port_id: u32,
|
port_id: u32,
|
||||||
param: Option<&pipewire::spa::pod::Pod>,
|
param: Option<&pipewire::spa::pod::Pod>,
|
||||||
sender: &glib::Sender<PipewireMessage>,
|
sender: &async_channel::Sender<PipewireMessage>,
|
||||||
) {
|
) {
|
||||||
let media_type = param
|
let media_type = param
|
||||||
.and_then(|param| pipewire::spa::param::format_utils::parse_format(param).ok())
|
.and_then(|param| pipewire::spa::param::format_utils::parse_format(param).ok())
|
||||||
@@ -329,7 +388,7 @@ fn handle_port_enum_format(
|
|||||||
.unwrap_or(MediaType::Unknown);
|
.unwrap_or(MediaType::Unknown);
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send(PipewireMessage::PortFormatChanged {
|
.send_blocking(PipewireMessage::PortFormatChanged {
|
||||||
id: port_id,
|
id: port_id,
|
||||||
media_type,
|
media_type,
|
||||||
})
|
})
|
||||||
@@ -338,8 +397,8 @@ fn handle_port_enum_format(
|
|||||||
|
|
||||||
/// Handle a new link being added
|
/// Handle a new link being added
|
||||||
fn handle_link(
|
fn handle_link(
|
||||||
link: &GlobalObject<ForeignDict>,
|
link: &GlobalObject<&DictRef>,
|
||||||
sender: &glib::Sender<PipewireMessage>,
|
sender: &async_channel::Sender<PipewireMessage>,
|
||||||
registry: &Rc<Registry>,
|
registry: &Rc<Registry>,
|
||||||
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
|
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
|
||||||
state: &Rc<RefCell<State>>,
|
state: &Rc<RefCell<State>>,
|
||||||
@@ -367,9 +426,9 @@ fn handle_link(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_link_info(
|
fn handle_link_info(
|
||||||
info: &LinkInfo,
|
info: &LinkInfoRef,
|
||||||
state: &Rc<RefCell<State>>,
|
state: &Rc<RefCell<State>>,
|
||||||
sender: &glib::Sender<PipewireMessage>,
|
sender: &async_channel::Sender<PipewireMessage>,
|
||||||
) {
|
) {
|
||||||
debug!("Received link info: {:?}", info);
|
debug!("Received link info: {:?}", info);
|
||||||
|
|
||||||
@@ -380,7 +439,7 @@ fn handle_link_info(
|
|||||||
// Info was an update - figure out if we should notify the gtk thread
|
// Info was an update - figure out if we should notify the gtk thread
|
||||||
if info.change_mask().contains(LinkChangeMask::STATE) {
|
if info.change_mask().contains(LinkChangeMask::STATE) {
|
||||||
sender
|
sender
|
||||||
.send(PipewireMessage::LinkStateChanged {
|
.send_blocking(PipewireMessage::LinkStateChanged {
|
||||||
id,
|
id,
|
||||||
active: matches!(info.state(), LinkState::Active),
|
active: matches!(info.state(), LinkState::Active),
|
||||||
})
|
})
|
||||||
@@ -388,7 +447,7 @@ fn handle_link_info(
|
|||||||
}
|
}
|
||||||
if info.change_mask().contains(LinkChangeMask::FORMAT) {
|
if info.change_mask().contains(LinkChangeMask::FORMAT) {
|
||||||
sender
|
sender
|
||||||
.send(PipewireMessage::LinkFormatChanged {
|
.send_blocking(PipewireMessage::LinkFormatChanged {
|
||||||
id,
|
id,
|
||||||
media_type: get_link_media_type(info),
|
media_type: get_link_media_type(info),
|
||||||
})
|
})
|
||||||
@@ -402,7 +461,7 @@ fn handle_link_info(
|
|||||||
state.insert(id, Item::Link { port_from, port_to });
|
state.insert(id, Item::Link { port_from, port_to });
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send(PipewireMessage::LinkAdded {
|
.send_blocking(PipewireMessage::LinkAdded {
|
||||||
id,
|
id,
|
||||||
port_from,
|
port_from,
|
||||||
port_to,
|
port_to,
|
||||||
@@ -440,7 +499,7 @@ fn toggle_link(
|
|||||||
.get_node_of_port(port_to)
|
.get_node_of_port(port_to)
|
||||||
.expect("Requested port not in state");
|
.expect("Requested port not in state");
|
||||||
|
|
||||||
if let Err(e) = core.create_object::<Link, _>(
|
if let Err(e) = core.create_object::<Link>(
|
||||||
"link-factory",
|
"link-factory",
|
||||||
&properties! {
|
&properties! {
|
||||||
"link.output.node" => node_from.to_string(),
|
"link.output.node" => node_from.to_string(),
|
||||||
@@ -455,7 +514,7 @@ fn toggle_link(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_link_media_type(link_info: &LinkInfo) -> MediaType {
|
fn get_link_media_type(link_info: &LinkInfoRef) -> MediaType {
|
||||||
let media_type = link_info
|
let media_type = link_info
|
||||||
.format()
|
.format()
|
||||||
.and_then(|format| pipewire::spa::param::format_utils::parse_format(format).ok())
|
.and_then(|format| pipewire::spa::param::format_utils::parse_format(format).ok())
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ node {
|
|||||||
background-color: @headerbar_bg_color;
|
background-color: @headerbar_bg_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
node label.heading {
|
node .node-title {
|
||||||
padding: 4px 7px;
|
padding: 4px 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,3 +53,20 @@ port-handle {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: @media-type-unknown;
|
background-color: @media-type-unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.rounded {
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.rounded {
|
||||||
|
border-radius: 9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.rounded > :first-child {
|
||||||
|
padding-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.rounded > :nth-child(2) {
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ mod imp {
|
|||||||
use adw::gtk::gdk::{self};
|
use adw::gtk::gdk::{self};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use pipewire::spa::format::MediaType;
|
use pipewire::spa::param::format::MediaType;
|
||||||
use pipewire::spa::Direction;
|
use pipewire::spa::utils::Direction;
|
||||||
|
|
||||||
pub struct Colors {
|
pub struct Colors {
|
||||||
audio: gdk::RGBA,
|
audio: gdk::RGBA,
|
||||||
@@ -93,6 +93,9 @@ mod imp {
|
|||||||
// Memorized data for an in-progress zoom gesture
|
// Memorized data for an in-progress zoom gesture
|
||||||
pub zoom_gesture_initial_zoom: Cell<Option<f64>>,
|
pub zoom_gesture_initial_zoom: Cell<Option<f64>>,
|
||||||
pub zoom_gesture_anchor: Cell<Option<(f64, f64)>>,
|
pub zoom_gesture_anchor: Cell<Option<(f64, f64)>>,
|
||||||
|
|
||||||
|
// This keeps track of an ongoing move view gesture.
|
||||||
|
pub move_view_state: Cell<(f64, f64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GraphView {
|
impl Default for GraphView {
|
||||||
@@ -108,6 +111,7 @@ mod imp {
|
|||||||
port_drag_cursor: Cell::new(Point::new(0.0, 0.0)),
|
port_drag_cursor: Cell::new(Point::new(0.0, 0.0)),
|
||||||
zoom_gesture_initial_zoom: Default::default(),
|
zoom_gesture_initial_zoom: Default::default(),
|
||||||
zoom_gesture_anchor: Default::default(),
|
zoom_gesture_anchor: Default::default(),
|
||||||
|
move_view_state: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,6 +140,7 @@ mod imp {
|
|||||||
self.setup_port_drag_and_drop();
|
self.setup_port_drag_and_drop();
|
||||||
self.setup_scroll_zooming();
|
self.setup_scroll_zooming();
|
||||||
self.setup_zoom_gesture();
|
self.setup_zoom_gesture();
|
||||||
|
self.setup_move_view();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispose(&self) {
|
fn dispose(&self) {
|
||||||
@@ -465,38 +470,66 @@ mod imp {
|
|||||||
self.obj().add_controller(zoom_gesture);
|
self.obj().add_controller(zoom_gesture);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_link(
|
fn setup_move_view(&self) {
|
||||||
|
let drag_controller = gtk::GestureDrag::new();
|
||||||
|
|
||||||
|
drag_controller.set_button(gtk::gdk::BUTTON_MIDDLE);
|
||||||
|
|
||||||
|
// TODO: set `all-scroll` cursor while dragging view
|
||||||
|
|
||||||
|
drag_controller.connect_drag_begin(|drag_controller, _, _| {
|
||||||
|
let widget = drag_controller
|
||||||
|
.widget()
|
||||||
|
.downcast::<super::GraphView>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
widget.imp().move_view_state.set((0.0, 0.0));
|
||||||
|
});
|
||||||
|
|
||||||
|
drag_controller.connect_drag_update(|drag_controller, x, y| {
|
||||||
|
let widget = drag_controller
|
||||||
|
.widget()
|
||||||
|
.downcast::<super::GraphView>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let imp = widget.imp();
|
||||||
|
let state = imp.move_view_state.replace((x, y));
|
||||||
|
let delta_x = state.0 - x;
|
||||||
|
let delta_y = state.1 - y;
|
||||||
|
|
||||||
|
let hadjustment_ref = imp.hadjustment.borrow();
|
||||||
|
let vadjustment_ref = imp.vadjustment.borrow();
|
||||||
|
let hadjustment = hadjustment_ref.as_ref().unwrap();
|
||||||
|
let vadjustment = vadjustment_ref.as_ref().unwrap();
|
||||||
|
|
||||||
|
let new_hadjustment = hadjustment.value() + delta_x;
|
||||||
|
let new_vadjustment = vadjustment.value() + delta_y;
|
||||||
|
|
||||||
|
hadjustment.set_value(new_hadjustment);
|
||||||
|
vadjustment.set_value(new_vadjustment);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.obj().add_controller(drag_controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot_link(
|
||||||
&self,
|
&self,
|
||||||
link_cr: &cairo::Context,
|
snapshot: >k::Snapshot,
|
||||||
output_anchor: &Point,
|
output_anchor: &Point,
|
||||||
input_anchor: &Point,
|
input_anchor: &Point,
|
||||||
active: bool,
|
active: bool,
|
||||||
color: &gdk::RGBA,
|
color: &gdk::RGBA,
|
||||||
) {
|
) {
|
||||||
let output_x: f64 = output_anchor.x().into();
|
let output_x = output_anchor.x();
|
||||||
let output_y: f64 = output_anchor.y().into();
|
let output_y = output_anchor.y();
|
||||||
let input_x: f64 = input_anchor.x().into();
|
let input_x = input_anchor.x();
|
||||||
let input_y: f64 = input_anchor.y().into();
|
let input_y = input_anchor.y();
|
||||||
|
|
||||||
// Use dashed line for inactive links, full line otherwise.
|
|
||||||
if active {
|
|
||||||
link_cr.set_dash(&[], 0.0);
|
|
||||||
} else {
|
|
||||||
link_cr.set_dash(&[10.0, 5.0], 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
link_cr.set_source_rgba(
|
|
||||||
color.red().into(),
|
|
||||||
color.green().into(),
|
|
||||||
color.blue().into(),
|
|
||||||
color.alpha().into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the output port is farther right than the input port and they have
|
// If the output port is farther right than the input port and they have
|
||||||
// a similar y coordinate, apply a y offset to the control points
|
// a similar y coordinate, apply a y offset to the control points
|
||||||
// so that the curve sticks out a bit.
|
// so that the curve sticks out a bit.
|
||||||
let y_control_offset = if output_x > input_x {
|
let y_control_offset = if output_x > input_x {
|
||||||
f64::max(0.0, 25.0 - (output_y - input_y).abs())
|
f32::max(0.0, 25.0 - (output_y - input_y).abs())
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
@@ -504,9 +537,10 @@ mod imp {
|
|||||||
// Place curve control offset by half the x distance between the two points.
|
// Place curve control offset by half the x distance between the two points.
|
||||||
// This makes the curve scale well for varying distances between the two ports,
|
// This makes the curve scale well for varying distances between the two ports,
|
||||||
// especially when the output port is farther right than the input port.
|
// especially when the output port is farther right than the input port.
|
||||||
let half_x_dist = f64::abs(output_x - input_x) / 2.0;
|
let half_x_dist = f32::abs(output_x - input_x) / 2.0;
|
||||||
link_cr.move_to(output_x, output_y);
|
let path_builder = gsk::PathBuilder::new();
|
||||||
link_cr.curve_to(
|
path_builder.move_to(output_x, output_y);
|
||||||
|
path_builder.cubic_to(
|
||||||
output_x + half_x_dist,
|
output_x + half_x_dist,
|
||||||
output_y - y_control_offset,
|
output_y - y_control_offset,
|
||||||
input_x - half_x_dist,
|
input_x - half_x_dist,
|
||||||
@@ -514,13 +548,20 @@ mod imp {
|
|||||||
input_x,
|
input_x,
|
||||||
input_y,
|
input_y,
|
||||||
);
|
);
|
||||||
|
let path = path_builder.to_path();
|
||||||
|
|
||||||
if let Err(e) = link_cr.stroke() {
|
let stroke = gsk::Stroke::new(2.0 * (self.zoom_factor.get() as f32));
|
||||||
warn!("Failed to draw graphview links: {}", e);
|
// Use dashed line for inactive links, full line otherwise.
|
||||||
};
|
if active {
|
||||||
|
stroke.set_dash(&[]);
|
||||||
|
} else {
|
||||||
|
stroke.set_dash(&[10.0, 5.0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_dragged_link(&self, port: &Port, link_cr: &cairo::Context, colors: &Colors) {
|
snapshot.append_stroke(&path, &stroke, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot_dragged_link(&self, snapshot: >k::Snapshot, port: &Port, colors: &Colors) {
|
||||||
let Some(port_anchor) = port.compute_point(&*self.obj(), &port.link_anchor()) else {
|
let Some(port_anchor) = port.compute_point(&*self.obj(), &port.link_anchor()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -550,21 +591,10 @@ mod imp {
|
|||||||
|
|
||||||
let color = &colors.color_for_media_type(MediaType::from_raw(port.media_type()));
|
let color = &colors.color_for_media_type(MediaType::from_raw(port.media_type()));
|
||||||
|
|
||||||
self.draw_link(link_cr, output_anchor, input_anchor, false, color);
|
self.snapshot_link(snapshot, output_anchor, input_anchor, false, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot_links(&self, widget: &super::GraphView, snapshot: >k::Snapshot) {
|
fn snapshot_links(&self, widget: &super::GraphView, snapshot: >k::Snapshot) {
|
||||||
let alloc = widget.allocation();
|
|
||||||
|
|
||||||
let link_cr = snapshot.append_cairo(&graphene::Rect::new(
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
alloc.width() as f32,
|
|
||||||
alloc.height() as f32,
|
|
||||||
));
|
|
||||||
|
|
||||||
link_cr.set_line_width(2.0 * self.zoom_factor.get());
|
|
||||||
|
|
||||||
let colors = Colors {
|
let colors = Colors {
|
||||||
audio: widget
|
audio: widget
|
||||||
.style_context()
|
.style_context()
|
||||||
@@ -593,8 +623,8 @@ mod imp {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.draw_link(
|
self.snapshot_link(
|
||||||
&link_cr,
|
snapshot,
|
||||||
&output_anchor,
|
&output_anchor,
|
||||||
&input_anchor,
|
&input_anchor,
|
||||||
link.active(),
|
link.active(),
|
||||||
@@ -603,7 +633,7 @@ mod imp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(port) = self.dragged_port.upgrade() {
|
if let Some(port) = self.dragged_port.upgrade() {
|
||||||
self.draw_dragged_link(&port, &link_cr, &colors);
|
self.snapshot_dragged_link(&snapshot, &port, &colors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use adw::{glib, prelude::*, subclass::prelude::*};
|
use adw::{glib, prelude::*, subclass::prelude::*};
|
||||||
use pipewire::spa::format::MediaType;
|
use pipewire::spa::param::format::MediaType;
|
||||||
|
|
||||||
use super::Port;
|
use super::Port;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use adw::{glib, gtk, prelude::*, subclass::prelude::*};
|
use adw::{glib, gtk, prelude::*, subclass::prelude::*};
|
||||||
use pipewire::spa::Direction;
|
use pipewire::spa::utils::Direction;
|
||||||
|
|
||||||
use super::Port;
|
use super::Port;
|
||||||
|
|
||||||
@@ -34,15 +34,26 @@ mod imp {
|
|||||||
#[property(get, set, construct_only)]
|
#[property(get, set, construct_only)]
|
||||||
pub(super) pipewire_id: Cell<u32>,
|
pub(super) pipewire_id: Cell<u32>,
|
||||||
#[property(
|
#[property(
|
||||||
name = "name", type = String,
|
name = "node-name", type = String,
|
||||||
get = |this: &Self| this.label.text().to_string(),
|
get = |this: &Self| this.node_name.text().to_string(),
|
||||||
set = |this: &Self, val| {
|
set = |this: &Self, val| {
|
||||||
this.label.set_text(val);
|
this.node_name.set_text(val);
|
||||||
this.label.set_tooltip_text(Some(val));
|
this.node_name.set_tooltip_text(Some(val));
|
||||||
}
|
}
|
||||||
)]
|
)]
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub(super) label: TemplateChild<gtk::Label>,
|
pub(super) node_name: TemplateChild<gtk::Label>,
|
||||||
|
#[property(
|
||||||
|
name = "media-name", type = String,
|
||||||
|
get = |this: &Self| this.media_name.text().to_string(),
|
||||||
|
set = |this: &Self, val| {
|
||||||
|
this.media_name.set_text(val);
|
||||||
|
this.media_name.set_tooltip_text(Some(val));
|
||||||
|
this.media_name.set_visible(!val.is_empty());
|
||||||
|
}
|
||||||
|
)]
|
||||||
|
#[template_child]
|
||||||
|
pub(super) media_name: TemplateChild<gtk::Label>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub(super) separator: TemplateChild<gtk::Separator>,
|
pub(super) separator: TemplateChild<gtk::Separator>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
@@ -74,8 +85,11 @@ mod imp {
|
|||||||
fn constructed(&self) {
|
fn constructed(&self) {
|
||||||
self.parent_constructed();
|
self.parent_constructed();
|
||||||
|
|
||||||
|
// Force left-to-right direction for the ports grid to avoid messed up UI when defaulting to right-to-left
|
||||||
|
self.port_grid.set_direction(gtk::TextDirection::Ltr);
|
||||||
|
|
||||||
// Display a grab cursor when the mouse is over the label so the user knows the node can be dragged.
|
// Display a grab cursor when the mouse is over the label so the user knows the node can be dragged.
|
||||||
self.label
|
self.node_name
|
||||||
.set_cursor(gtk::gdk::Cursor::from_name("grab", None).as_ref());
|
.set_cursor(gtk::gdk::Cursor::from_name("grab", None).as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +155,7 @@ glib::wrapper! {
|
|||||||
impl Node {
|
impl Node {
|
||||||
pub fn new(name: &str, pipewire_id: u32) -> Self {
|
pub fn new(name: &str, pipewire_id: u32) -> Self {
|
||||||
glib::Object::builder()
|
glib::Object::builder()
|
||||||
.property("name", name)
|
.property("node-name", name)
|
||||||
.property("pipewire-id", pipewire_id)
|
.property("pipewire-id", pipewire_id)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,14 @@
|
|||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="label">
|
<object class="GtkBox">
|
||||||
|
<style>
|
||||||
|
<class name="node-title"></class>
|
||||||
|
</style>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">1</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="node_name">
|
||||||
<style>
|
<style>
|
||||||
<class name="heading"></class>
|
<class name="heading"></class>
|
||||||
</style>
|
</style>
|
||||||
@@ -19,6 +26,21 @@
|
|||||||
<property name="max-width-chars">20</property>
|
<property name="max-width-chars">20</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="media_name">
|
||||||
|
<style>
|
||||||
|
<class name="dim-label"></class>
|
||||||
|
<class name="caption"></class>
|
||||||
|
</style>
|
||||||
|
<property name="visible">false</property>
|
||||||
|
<property name="wrap">true</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_END</property>
|
||||||
|
<property name="lines">2</property>
|
||||||
|
<property name="max-width-chars">20</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparator" id="separator">
|
<object class="GtkSeparator" id="separator">
|
||||||
<!-- The node will show the seperator only once ports are added to it -->
|
<!-- The node will show the seperator only once ports are added to it -->
|
||||||
|
|||||||
@@ -21,17 +21,17 @@ use adw::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
subclass::prelude::*,
|
subclass::prelude::*,
|
||||||
};
|
};
|
||||||
use pipewire::spa::Direction;
|
use pipewire::spa::utils::Direction;
|
||||||
|
|
||||||
use super::PortHandle;
|
use super::PortHandle;
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, OnceCell};
|
||||||
|
|
||||||
use once_cell::{sync::Lazy, unsync::OnceCell};
|
use once_cell::sync::Lazy;
|
||||||
use pipewire::spa::{format::MediaType, Direction};
|
use pipewire::spa::{param::format::MediaType, utils::Direction};
|
||||||
|
|
||||||
/// Graphical representation of a pipewire port.
|
/// Graphical representation of a pipewire port.
|
||||||
#[derive(gtk::CompositeTemplate, glib::Properties)]
|
#[derive(gtk::CompositeTemplate, glib::Properties)]
|
||||||
@@ -101,6 +101,9 @@ mod imp {
|
|||||||
fn constructed(&self) {
|
fn constructed(&self) {
|
||||||
self.parent_constructed();
|
self.parent_constructed();
|
||||||
|
|
||||||
|
// Force left-to-right direction for the ports grid to avoid messed up UI when defaulting to right-to-left
|
||||||
|
self.obj().set_direction(gtk::TextDirection::Ltr);
|
||||||
|
|
||||||
// Display a grab cursor when the mouse is over the port so the user knows it can be dragged to another port.
|
// Display a grab cursor when the mouse is over the port so the user knows it can be dragged to another port.
|
||||||
self.obj()
|
self.obj()
|
||||||
.set_cursor(gtk::gdk::Cursor::from_name("grab", None).as_ref());
|
.set_cursor(gtk::gdk::Cursor::from_name("grab", None).as_ref());
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ mod imp {
|
|||||||
menu.append(Some("200%"), Some("win.set-zoom(2.0)"));
|
menu.append(Some("200%"), Some("win.set-zoom(2.0)"));
|
||||||
menu.append(Some("300%"), Some("win.set-zoom(3.0)"));
|
menu.append(Some("300%"), Some("win.set-zoom(3.0)"));
|
||||||
let popover = gtk::PopoverMenu::from_model(Some(&menu));
|
let popover = gtk::PopoverMenu::from_model(Some(&menu));
|
||||||
|
popover.set_position(gtk::PositionType::Top);
|
||||||
|
|
||||||
ZoomEntry {
|
ZoomEntry {
|
||||||
graphview: Default::default(),
|
graphview: Default::default(),
|
||||||
|
|||||||
@@ -1,26 +1,37 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<interface>
|
<interface>
|
||||||
<template class="HelvumZoomEntry" parent="GtkBox">
|
<template class="HelvumZoomEntry" parent="GtkBox">
|
||||||
<child>
|
<property name="spacing">12</property>
|
||||||
<object class="GtkButton" id="zoom_out_button">
|
|
||||||
<property name="icon-name">zoom-out-symbolic</property>
|
|
||||||
<property name="tooltip-text">Zoom out</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkEntry" id="entry">
|
<object class="GtkEntry" id="entry">
|
||||||
<property name="secondary-icon-name">go-down-symbolic</property>
|
<property name="secondary-icon-name">go-down-symbolic</property>
|
||||||
<property name="input-purpose">digits</property>
|
<property name="input-purpose">digits</property>
|
||||||
|
<property name="max-width-chars">5</property>
|
||||||
|
<style>
|
||||||
|
<class name="osd"/>
|
||||||
|
<class name="rounded"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="zoom_out_button">
|
||||||
|
<property name="icon-name">zoom-out-symbolic</property>
|
||||||
|
<property name="tooltip-text">Zoom out</property>
|
||||||
|
<style>
|
||||||
|
<class name="osd"/>
|
||||||
|
<class name="rounded"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="zoom_in_button">
|
<object class="GtkButton" id="zoom_in_button">
|
||||||
<property name="icon-name">zoom-in-symbolic</property>
|
<property name="icon-name">zoom-in-symbolic</property>
|
||||||
<property name="tooltip-text">Zoom in</property>
|
<property name="tooltip-text">Zoom in</property>
|
||||||
|
<style>
|
||||||
|
<class name="osd"/>
|
||||||
|
<class name="rounded"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<style>
|
|
||||||
<class name="linked"/>
|
|
||||||
</style>
|
|
||||||
</template>
|
</template>
|
||||||
</interface>
|
</interface>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk" version="4.0"/>
|
<requires lib="gtk" version="4.0"/>
|
||||||
<requires lib="Adw" version="1.0"/>
|
<requires lib="Adw" version="1.4"/>
|
||||||
<menu id="primary_menu">
|
<menu id="primary_menu">
|
||||||
<section>
|
<section>
|
||||||
<item>
|
<item>
|
||||||
@@ -15,21 +15,10 @@
|
|||||||
<property name="default-height">720</property>
|
<property name="default-height">720</property>
|
||||||
<property name="title">Helvum - Pipewire Patchbay</property>
|
<property name="title">Helvum - Pipewire Patchbay</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="AdwToolbarView">
|
||||||
<property name="orientation">vertical</property>
|
<child type="top">
|
||||||
<child>
|
|
||||||
<object class="AdwHeaderBar" id="header_bar">
|
<object class="AdwHeaderBar" id="header_bar">
|
||||||
<child type="end">
|
<child type="end">
|
||||||
<object class="GtkBox">
|
|
||||||
<style>
|
|
||||||
<class name="toolbar"></class>
|
|
||||||
</style>
|
|
||||||
<child>
|
|
||||||
<object class="HelvumZoomEntry">
|
|
||||||
<property name="zoomed-widget">graph</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuButton">
|
<object class="GtkMenuButton">
|
||||||
<property name="icon-name">open-menu-symbolic</property>
|
<property name="icon-name">open-menu-symbolic</property>
|
||||||
<property name="menu-model">primary_menu</property>
|
<property name="menu-model">primary_menu</property>
|
||||||
@@ -37,14 +26,17 @@
|
|||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
<property name="content">
|
||||||
</child>
|
<object class="GtkBox">
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwBanner" id="connection_banner">
|
<object class="AdwBanner" id="connection_banner">
|
||||||
<property name="title" translatable="yes">Disconnected</property>
|
<property name="title" translatable="yes">Disconnected</property>
|
||||||
<property name="revealed">false</property>
|
<property name="revealed">false</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkOverlay">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScrolledWindow">
|
<object class="GtkScrolledWindow">
|
||||||
<child>
|
<child>
|
||||||
@@ -55,6 +47,19 @@
|
|||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child type="overlay">
|
||||||
|
<object class="HelvumZoomEntry">
|
||||||
|
<property name="zoomed-widget">graph</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<property name="valign">end</property>
|
||||||
|
<property name="margin-end">24</property>
|
||||||
|
<property name="margin-bottom">24</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user