feat: working version
This commit is contained in:
521
Cargo.lock
generated
521
Cargo.lock
generated
@@ -2,6 +2,527 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libpulse-binding"
|
||||
version = "2.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed3557a2dfc380c8f061189a01c6ae7348354e0c9886038dc6c171219c08eaff"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"libpulse-sys",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libpulse-sys"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc19e110fbf42c17260d30f6d3dc545f58491c7830d38ecb9aaca96e26067a9b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pkg-config",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mixrs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"libpulse-binding",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.38.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.5",
|
||||
"windows_aarch64_msvc 0.52.5",
|
||||
"windows_i686_gnu 0.52.5",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.5",
|
||||
"windows_x86_64_gnu 0.52.5",
|
||||
"windows_x86_64_gnullvm 0.52.5",
|
||||
"windows_x86_64_msvc 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
@@ -4,3 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
pulse = { version = "2.0", package = "libpulse-binding" }
|
||||
tokio = { version = "1.38.0", features = ["full"] }
|
||||
|
||||
25
src/instructions.rs
Normal file
25
src/instructions.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
#[repr(u8)]
|
||||
pub enum MixerInstruction {
|
||||
SelectNext,
|
||||
SelectPrevious,
|
||||
ToggleMuteCurrent,
|
||||
IncreaseCurrent,
|
||||
DecreaseCurrent,
|
||||
GetCurrent,
|
||||
PlayPauseCurrent,
|
||||
}
|
||||
|
||||
impl MixerInstruction {
|
||||
pub fn from_u8(byte: u8) -> Self {
|
||||
match byte {
|
||||
0 => MixerInstruction::SelectNext,
|
||||
1 => MixerInstruction::SelectPrevious,
|
||||
2 => MixerInstruction::ToggleMuteCurrent,
|
||||
3 => MixerInstruction::IncreaseCurrent,
|
||||
4 => MixerInstruction::DecreaseCurrent,
|
||||
5 => MixerInstruction::GetCurrent,
|
||||
6 => MixerInstruction::PlayPauseCurrent,
|
||||
_ => panic!("Could not parse '{byte}' to MixerInstruction"),
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/main.rs
51
src/main.rs
@@ -1,3 +1,50 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
mod instructions;
|
||||
pub mod mixer;
|
||||
pub mod pulseaudio;
|
||||
pub mod utils;
|
||||
|
||||
use anyhow::Result;
|
||||
use mixer::Mixer;
|
||||
use pulseaudio::PulseInstruction;
|
||||
use std::{process::Command, sync::mpsc::channel};
|
||||
|
||||
const NOTIFY_SEND_REPLACE_ID: u32 = 1448531;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let mainloop = pulse::mainloop::standard::Mainloop::new().expect("Error getting main loop");
|
||||
|
||||
let (pulse_ix_tx, pulse_ix_rx) = channel::<PulseInstruction>();
|
||||
|
||||
let mut mixer = Mixer::new(mainloop, pulse_ix_tx);
|
||||
|
||||
mixer.run(pulse_ix_rx);
|
||||
}
|
||||
|
||||
pub fn send_notification(message: &str) -> Result<()> {
|
||||
Command::new("notify-send")
|
||||
.args(vec![
|
||||
"Mixrs",
|
||||
message,
|
||||
"-r",
|
||||
&NOTIFY_SEND_REPLACE_ID.to_string(),
|
||||
])
|
||||
.env("DBUS_SESSION_BUS_ADDRESS", "unix:path=/run/user/1000/bus")
|
||||
.spawn()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn playerctl_toggle(target: &str) -> Result<()> {
|
||||
let get_players = Command::new("playerctl").arg("-l").output()?;
|
||||
let get_players_output = String::from_utf8(get_players.stdout)?;
|
||||
let players: Vec<&str> = get_players_output.split("\n").collect();
|
||||
|
||||
match players.iter().find(|p| p.to_lowercase().contains(&target.to_lowercase())) {
|
||||
Some(player) => {
|
||||
Command::new("playerctl").args(vec!["-p", player, "play-pause"]).spawn()?;
|
||||
},
|
||||
None => {},
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
465
src/mixer.rs
Normal file
465
src/mixer.rs
Normal file
@@ -0,0 +1,465 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use std::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
collections::HashMap,
|
||||
fs,
|
||||
io::Read,
|
||||
os::unix::net::UnixListener,
|
||||
path::Path,
|
||||
sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Arc, Mutex,
|
||||
},
|
||||
thread,
|
||||
time::Duration,
|
||||
usize,
|
||||
};
|
||||
|
||||
use pulse::{
|
||||
callbacks::ListResult,
|
||||
context::{
|
||||
subscribe::{Facility, InterestMaskSet, Operation},
|
||||
FlagSet,
|
||||
},
|
||||
mainloop::standard::{IterateResult, Mainloop},
|
||||
volume::ChannelVolumes,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
instructions::MixerInstruction,
|
||||
playerctl_toggle,
|
||||
pulseaudio::{PulseInstruction, SinkInputMixerData},
|
||||
send_notification,
|
||||
utils::{get_sink_input_name, percentage_to_total_volume},
|
||||
};
|
||||
|
||||
pub struct Mixer {
|
||||
sink_inputs: HashMap<u32, SinkInputMixerData>,
|
||||
selected_index: Arc<Mutex<Option<usize>>>,
|
||||
mainloop: Mainloop,
|
||||
context: pulse::context::Context,
|
||||
}
|
||||
|
||||
impl Mixer {
|
||||
pub fn new(mut mainloop: Mainloop, pulse_ix_tx: Sender<PulseInstruction>) -> Self {
|
||||
let mut context =
|
||||
pulse::context::Context::new(&mainloop, "Mixrs").expect("Error creating pulse context");
|
||||
|
||||
context
|
||||
.borrow_mut()
|
||||
.connect(None, FlagSet::NOFLAGS, None)
|
||||
.expect("Error connecting pulse context");
|
||||
|
||||
loop {
|
||||
match mainloop.borrow_mut().iterate(false) {
|
||||
IterateResult::Quit(_) | IterateResult::Err(_) => {
|
||||
panic!("Iterate state was not success, quitting...");
|
||||
}
|
||||
IterateResult::Success(_) => {}
|
||||
}
|
||||
match context.borrow().get_state() {
|
||||
pulse::context::State::Ready => {
|
||||
break;
|
||||
}
|
||||
pulse::context::State::Failed | pulse::context::State::Terminated => {
|
||||
panic!("Context state failed/terminated, quitting...");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let sink_inputs: HashMap<u32, SinkInputMixerData> = HashMap::new();
|
||||
|
||||
let selected_index: Arc<Mutex<Option<usize>>> = Arc::new(Mutex::new(None));
|
||||
|
||||
context.subscribe(InterestMaskSet::SINK_INPUT, |_| {});
|
||||
|
||||
context.set_subscribe_callback(Some(Box::new(move |facility, operation, index| {
|
||||
let Some(facility) = facility else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(operation) = operation else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Facility::SinkInput = facility else {
|
||||
return;
|
||||
};
|
||||
|
||||
pulse_ix_tx
|
||||
.send(match operation {
|
||||
Operation::New => PulseInstruction::AddSinkInput(index),
|
||||
Operation::Changed => PulseInstruction::UpdateSinkInput(index),
|
||||
Operation::Removed => PulseInstruction::RemoveSinkInput(index),
|
||||
})
|
||||
.unwrap();
|
||||
})));
|
||||
|
||||
Self {
|
||||
sink_inputs,
|
||||
selected_index,
|
||||
mainloop,
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_socket_listener(&self) -> Result<UnixListener> {
|
||||
let socket_path = Path::new("/tmp/mixrs");
|
||||
|
||||
if socket_path.exists() {
|
||||
fs::remove_file(socket_path)?;
|
||||
}
|
||||
|
||||
let listener = UnixListener::bind(socket_path)?;
|
||||
|
||||
Ok(listener)
|
||||
}
|
||||
|
||||
pub fn run(&mut self, pulse_ix_rx: Receiver<PulseInstruction>) -> ! {
|
||||
let listener = self
|
||||
.create_socket_listener()
|
||||
.expect("Error creating unix socket listener");
|
||||
|
||||
let (mixer_tx, mixer_rx) = channel::<MixerInstruction>();
|
||||
|
||||
thread::spawn(move || {
|
||||
for client in listener.incoming() {
|
||||
match client {
|
||||
Ok(mut stream) => {
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(1);
|
||||
stream.read_to_end(&mut buf).expect("Error reading stream");
|
||||
|
||||
mixer_tx.send(MixerInstruction::from_u8(buf[0])).unwrap();
|
||||
}
|
||||
Err(_) => println!("Stream error"),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let initial_sink_inputs: Arc<Mutex<HashMap<u32, SinkInputMixerData>>> =
|
||||
Arc::new(Mutex::new(HashMap::new()));
|
||||
let callback_initial_sink_inputs = initial_sink_inputs.clone();
|
||||
|
||||
let initial_sink_inputs_operation = self
|
||||
.context
|
||||
.borrow_mut()
|
||||
.introspect()
|
||||
.borrow_mut()
|
||||
.get_sink_input_info_list(move |r| {
|
||||
let ListResult::Item(sink_input) = r else {
|
||||
return;
|
||||
};
|
||||
|
||||
callback_initial_sink_inputs.lock().unwrap().insert(
|
||||
sink_input.index,
|
||||
SinkInputMixerData {
|
||||
name: get_sink_input_name(&sink_input).unwrap(),
|
||||
volume: sink_input.volume.avg().0,
|
||||
channels: sink_input.volume.len(),
|
||||
muted: sink_input.mute,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
while initial_sink_inputs_operation.get_state() == pulse::operation::State::Running {
|
||||
iterate_mainloop(&mut self.mainloop);
|
||||
}
|
||||
|
||||
self.sink_inputs = initial_sink_inputs.lock().unwrap().clone();
|
||||
|
||||
*self.selected_index.lock().unwrap() = match self.sink_inputs.keys().nth(0) {
|
||||
Some(_) => Some(0),
|
||||
None => None,
|
||||
};
|
||||
|
||||
loop {
|
||||
match mixer_rx.try_recv() {
|
||||
Ok(ix) => match ix {
|
||||
MixerInstruction::SelectNext => self.select_next(),
|
||||
MixerInstruction::SelectPrevious => self.select_previous(),
|
||||
MixerInstruction::ToggleMuteCurrent => self.toggle_mute_current(),
|
||||
MixerInstruction::IncreaseCurrent => self.increase_volume_current(),
|
||||
MixerInstruction::DecreaseCurrent => self.decrease_volume_current(),
|
||||
MixerInstruction::GetCurrent => self.get_current(),
|
||||
MixerInstruction::PlayPauseCurrent => self.play_pause_current(),
|
||||
},
|
||||
Err(_) => (),
|
||||
}
|
||||
|
||||
if let Some(ix) = pulse_ix_rx.try_recv().ok() {
|
||||
match ix {
|
||||
PulseInstruction::AddSinkInput(sink_index) => {
|
||||
let result: Arc<Mutex<Option<SinkInputMixerData>>> =
|
||||
Arc::new(Mutex::new(None));
|
||||
let operation_result = result.clone();
|
||||
|
||||
let operation = self
|
||||
.context
|
||||
.borrow_mut()
|
||||
.introspect()
|
||||
.borrow_mut()
|
||||
.get_sink_input_info(sink_index, move |r| {
|
||||
if let ListResult::Item(sink_input) = r {
|
||||
*operation_result.lock().unwrap() = Some(SinkInputMixerData {
|
||||
name: get_sink_input_name(sink_input).unwrap(),
|
||||
volume: sink_input.volume.avg().0,
|
||||
channels: sink_input.volume.len(),
|
||||
muted: sink_input.mute,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
while operation.get_state() == pulse::operation::State::Running {
|
||||
iterate_mainloop(&mut self.mainloop);
|
||||
}
|
||||
|
||||
let sink_input = result.lock().unwrap().take();
|
||||
if let Some(sink_input) = sink_input {
|
||||
self.sink_inputs.insert(sink_index, sink_input);
|
||||
}
|
||||
}
|
||||
PulseInstruction::RemoveSinkInput(sink_index) => {
|
||||
let selected_index_lock = self.selected_index.lock().unwrap();
|
||||
|
||||
match *selected_index_lock {
|
||||
Some(current_index) => {
|
||||
drop(selected_index_lock);
|
||||
|
||||
let removed_sink_input_index = self
|
||||
.sink_inputs
|
||||
.keys()
|
||||
.position(|k| *k == sink_index)
|
||||
.unwrap();
|
||||
|
||||
let current_key =
|
||||
*self.sink_inputs.keys().nth(current_index).unwrap();
|
||||
|
||||
if self.sink_inputs.remove(&sink_index).is_some() {
|
||||
if sink_index == current_key
|
||||
|| removed_sink_input_index > current_index
|
||||
{
|
||||
self.select_previous();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
PulseInstruction::UpdateSinkInput(sink_index) => {
|
||||
match self.sink_inputs.get_mut(&sink_index) {
|
||||
Some(sink_input_mixer_data) => {
|
||||
let new_sink_input: Arc<Mutex<Option<SinkInputMixerData>>> =
|
||||
Arc::new(Mutex::new(None));
|
||||
let callback_new_sink_input = new_sink_input.clone();
|
||||
|
||||
let operation = self
|
||||
.context
|
||||
.borrow_mut()
|
||||
.introspect()
|
||||
.borrow_mut()
|
||||
.get_sink_input_info(sink_index, move |r| {
|
||||
let ListResult::Item(sink_input) = r else {
|
||||
return;
|
||||
};
|
||||
|
||||
*callback_new_sink_input.lock().unwrap() =
|
||||
Some(SinkInputMixerData {
|
||||
name: get_sink_input_name(&sink_input).unwrap(),
|
||||
volume: sink_input.volume.avg().0,
|
||||
channels: sink_input.volume.len(),
|
||||
muted: sink_input.mute,
|
||||
});
|
||||
});
|
||||
|
||||
while operation.get_state() == pulse::operation::State::Running {
|
||||
iterate_mainloop(&mut self.mainloop);
|
||||
}
|
||||
|
||||
let mut sink_input_lock = new_sink_input.lock().unwrap();
|
||||
if let Some(new_sink_input) = sink_input_lock.take() {
|
||||
*sink_input_mixer_data = new_sink_input;
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterate_mainloop(&mut self.mainloop);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_next(&mut self) {
|
||||
let mut index_lock = self.selected_index.lock().unwrap();
|
||||
|
||||
match *index_lock {
|
||||
Some(current_index) => {
|
||||
let new_index: usize =
|
||||
(current_index.overflowing_add(1).0 % self.sink_inputs.len()).max(0);
|
||||
|
||||
if current_index != new_index {
|
||||
*index_lock = Some(new_index);
|
||||
}
|
||||
|
||||
drop(index_lock);
|
||||
self.get_current();
|
||||
}
|
||||
None => {
|
||||
*index_lock = if self.sink_inputs.len() > 0 {
|
||||
Some(0)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_previous(&mut self) {
|
||||
let mut index_lock = self.selected_index.lock().unwrap();
|
||||
|
||||
match *index_lock {
|
||||
Some(current_index) => {
|
||||
let new_index: usize = match current_index.overflowing_sub(1) {
|
||||
(_, true) => self.sink_inputs.len() - 1,
|
||||
(new_value, false) => new_value,
|
||||
};
|
||||
|
||||
if current_index != new_index {
|
||||
*index_lock = Some(new_index);
|
||||
}
|
||||
|
||||
drop(index_lock);
|
||||
self.get_current();
|
||||
}
|
||||
None => {
|
||||
*index_lock = if self.sink_inputs.len() > 0 {
|
||||
Some(0)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_mute_current(&mut self) {
|
||||
let index_lock = self.selected_index.lock().unwrap();
|
||||
|
||||
let Some(index) = *index_lock else {
|
||||
return;
|
||||
};
|
||||
|
||||
drop(index_lock);
|
||||
|
||||
let sink_index = *self.sink_inputs.keys().nth(index).unwrap();
|
||||
|
||||
self.context
|
||||
.borrow_mut()
|
||||
.introspect()
|
||||
.borrow_mut()
|
||||
.set_sink_input_mute(
|
||||
sink_index,
|
||||
!self.sink_inputs.get(&sink_index).unwrap().muted,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn increase_volume_current(&mut self) {
|
||||
let index_lock = self.selected_index.lock().unwrap();
|
||||
|
||||
let Some(index) = *index_lock else {
|
||||
return;
|
||||
};
|
||||
|
||||
drop(index_lock);
|
||||
|
||||
let sink_index = *self.sink_inputs.keys().nth(index).unwrap();
|
||||
|
||||
let sink_input = self.sink_inputs.get(&sink_index).unwrap();
|
||||
|
||||
let mut volume = ChannelVolumes::default();
|
||||
volume.set(
|
||||
sink_input.channels,
|
||||
pulse::volume::Volume(sink_input.volume),
|
||||
);
|
||||
|
||||
volume.increase(pulse::volume::Volume(percentage_to_total_volume(5)));
|
||||
|
||||
self.context
|
||||
.borrow_mut()
|
||||
.introspect()
|
||||
.borrow_mut()
|
||||
.set_sink_input_volume(sink_index, &volume, None);
|
||||
}
|
||||
|
||||
pub fn decrease_volume_current(&mut self) {
|
||||
let index_lock = self.selected_index.lock().unwrap();
|
||||
|
||||
let Some(index) = *index_lock else {
|
||||
return;
|
||||
};
|
||||
|
||||
drop(index_lock);
|
||||
|
||||
let sink_index = *self.sink_inputs.keys().nth(index).unwrap();
|
||||
|
||||
let sink_input = self.sink_inputs.get(&sink_index).unwrap();
|
||||
|
||||
let mut volume = ChannelVolumes::default();
|
||||
volume.set(
|
||||
sink_input.channels,
|
||||
pulse::volume::Volume(sink_input.volume),
|
||||
);
|
||||
|
||||
volume.decrease(pulse::volume::Volume(percentage_to_total_volume(5)));
|
||||
|
||||
self.context
|
||||
.borrow_mut()
|
||||
.introspect()
|
||||
.borrow_mut()
|
||||
.set_sink_input_volume(sink_index, &volume, None);
|
||||
}
|
||||
|
||||
pub fn get_current(&self) {
|
||||
let index_lock = self.selected_index.lock().unwrap();
|
||||
|
||||
let Some(index) = *index_lock else {
|
||||
return;
|
||||
};
|
||||
|
||||
drop(index_lock);
|
||||
|
||||
let sink_index = *self.sink_inputs.keys().nth(index).unwrap();
|
||||
|
||||
let current_name = &self.sink_inputs.get(&sink_index).unwrap().name;
|
||||
let _ = send_notification(&format!("Selected: {current_name}"));
|
||||
}
|
||||
|
||||
pub fn play_pause_current(&self) {
|
||||
let index_lock = self.selected_index.lock().unwrap();
|
||||
|
||||
let Some(index) = *index_lock else {
|
||||
return;
|
||||
};
|
||||
|
||||
drop(index_lock);
|
||||
|
||||
let sink_index = *self.sink_inputs.keys().nth(index).unwrap();
|
||||
|
||||
let current_name = &self.sink_inputs.get(&sink_index).unwrap().name;
|
||||
match playerctl_toggle(current_name) {
|
||||
Ok(_) => {
|
||||
let _ = send_notification(&format!("Toggled {current_name}"));
|
||||
}
|
||||
Err(_) => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iterate_mainloop(mainloop: &mut pulse::mainloop::standard::Mainloop) {
|
||||
mainloop.borrow_mut().iterate(false);
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
32
src/pulseaudio/mod.rs
Normal file
32
src/pulseaudio/mod.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use std::u32;
|
||||
|
||||
use crate::utils::total_volume_to_percentage;
|
||||
|
||||
pub enum PulseInstruction {
|
||||
AddSinkInput(u32),
|
||||
RemoveSinkInput(u32),
|
||||
UpdateSinkInput(u32),
|
||||
}
|
||||
|
||||
pub enum PulseResponse {
|
||||
Ok,
|
||||
Error,
|
||||
SinkInput(Option<SinkInputMixerData>),
|
||||
SinkInputs(Vec<SinkInputMixerData>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SinkInputMixerData {
|
||||
/// The input sink's `application.name`
|
||||
pub name: String,
|
||||
/// The input sink's volume
|
||||
pub volume: u32,
|
||||
pub muted: bool,
|
||||
pub channels: u8,
|
||||
}
|
||||
|
||||
impl SinkInputMixerData {
|
||||
pub fn get_volume_percent(&self) -> u8 {
|
||||
total_volume_to_percentage(self.volume)
|
||||
}
|
||||
}
|
||||
28
src/utils.rs
Normal file
28
src/utils.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use anyhow::anyhow;
|
||||
use pulse::{context::introspect::SinkInputInfo, volume};
|
||||
|
||||
const FULL_VOLUME: u32 = 1 << 16;
|
||||
|
||||
pub fn volume_to_percentage(volume: volume::ChannelVolumes) -> u8 {
|
||||
let average = volume.avg().0;
|
||||
|
||||
total_volume_to_percentage(average)
|
||||
}
|
||||
|
||||
pub fn total_volume_to_percentage(volume: u32) -> u8 {
|
||||
((volume as f32 / FULL_VOLUME as f32) * 100.0).round() as u8
|
||||
}
|
||||
|
||||
pub fn percentage_to_total_volume(percentage: u8) -> u32 {
|
||||
((FULL_VOLUME as f32 / 100.0) * percentage as f32).round() as u32
|
||||
}
|
||||
|
||||
pub fn get_sink_input_name(sink_input: &SinkInputInfo) -> anyhow::Result<String> {
|
||||
let Some(name_bytes) = sink_input.proplist.get("application.name") else {
|
||||
return Err(anyhow!("Invalid sink input name"));
|
||||
};
|
||||
|
||||
Ok(String::from_utf8(
|
||||
name_bytes[..name_bytes.len() - 1].to_vec(),
|
||||
)?)
|
||||
}
|
||||
Reference in New Issue
Block a user