Compare commits

..
6 Commits
Author SHA1 Message Date
arabianq 6d7abdbf53 change version to 1.13.0 2026-07-24 05:52:27 +03:00
arabianq 41bb035b2c cargo update 2026-07-24 05:43:52 +03:00
arabianq 3873bf4270 deps: update rodio 2026-07-24 05:43:02 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2b60b47cce chore(deps): bump clap from 4.6.3 to 4.6.4 in the clap-stack group (#176)
* chore(deps): bump clap from 4.6.3 to 4.6.4 in the clap-stack group

Bumps the clap-stack group with 1 update: [clap](https://github.com/clap-rs/clap).


Updates `clap` from 4.6.3 to 4.6.4
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.3...clap_complete-v4.6.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.6.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: clap-stack
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(flatpak): automatically update cargo-sources.json

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 05:42:53 +03:00
Tarasov AleksandrandGitHub 65ae8589db feat(lib, daemon, cli, gui): add volume multiplier (#175)
* initial implementation

* rename DaemonConfig.volume_multiplier to default_volume_multiplier

* add volume_multiplier to the FullState

* add pwsp-gui integration
2026-07-24 05:33:51 +03:00
Tarasov AleksandrandGitHub a33d023950 feat(daemon, cli): global DaemonConfig and an ability to get/save it using cli (#174)
* use one global Arc<DaemonConfig>

* implement commands

* replace Arc<DaemonConfig> with Arc<Mutex<DaemonConfig>> and fix pwsp-gui

* add pwsp-cli support

* replace serde_json::to_string_pretty with to_string

* refactor

* implement UpdateDaemonConfigCommand and use it in pwsp-gui so it now uses real in-memory config, not the saved one

* implement utils::gui get_daemon_config and update_daemon_config to simplify code
2026-07-24 04:49:51 +03:00
22 changed files with 574 additions and 187 deletions
Generated
+105 -55
View File
@@ -294,7 +294,7 @@ checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.2", "syn 3.0.3",
] ]
[[package]] [[package]]
@@ -303,6 +303,43 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "audio-core"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ebbf82d06013f4c41fe71303feb980cddd78496d904d06be627972de51a24"
[[package]]
name = "audioadapter"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91f87b70b051c5866680ad79f6743a42ccab264c009d1a71f4d33a3872ae60c8"
dependencies = [
"audio-core",
"num-traits",
]
[[package]]
name = "audioadapter-buffers"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9097d67933fb083d382ce980430afdb758aada60846010aee6be068c06cef0ca"
dependencies = [
"audioadapter",
"audioadapter-sample",
"num-traits",
]
[[package]]
name = "audioadapter-sample"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ab94f2bc04a14e1f49ee5f222f66460e8a1b51627bdfedf34eed394d747938"
dependencies = [
"audio-core",
"num-traits",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.5.1" version = "1.5.1"
@@ -575,9 +612,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.6.3" version = "4.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fb99565819980999fb7b4a1796046a5c949e6d4ff132cf5fadf5a641e20d776" checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -596,14 +633,14 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.6.3" version = "4.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f2392eae7f16557a3d727ef3a12e57b2b2ca6f98566a5f4fb41ffe305df077" checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 3.0.3",
] ]
[[package]] [[package]]
@@ -1370,13 +1407,13 @@ dependencies = [
[[package]] [[package]]
name = "foreign-types-macros" name = "foreign-types-macros"
version = "0.2.3" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 3.0.3",
] ]
[[package]] [[package]]
@@ -1533,9 +1570,9 @@ dependencies = [
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.3" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
[[package]] [[package]]
name = "globset" name = "globset"
@@ -2128,9 +2165,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.186" version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]] [[package]]
name = "libloading" name = "libloading"
@@ -2435,16 +2472,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "num-bigint"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-derive" name = "num-derive"
version = "0.4.2" version = "0.4.2"
@@ -2465,17 +2492,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -3200,7 +3216,7 @@ checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5"
[[package]] [[package]]
name = "pwsp-cli" name = "pwsp-cli"
version = "1.12.5" version = "1.13.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -3211,7 +3227,7 @@ dependencies = [
[[package]] [[package]]
name = "pwsp-daemon" name = "pwsp-daemon"
version = "1.12.5" version = "1.13.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -3223,7 +3239,7 @@ dependencies = [
[[package]] [[package]]
name = "pwsp-gui" name = "pwsp-gui"
version = "1.12.5" version = "1.13.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"eframe", "eframe",
@@ -3247,7 +3263,7 @@ dependencies = [
[[package]] [[package]]
name = "pwsp-lib" name = "pwsp-lib"
version = "1.12.5" version = "1.13.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -3278,9 +3294,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.39.4" version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@@ -3496,11 +3512,11 @@ dependencies = [
[[package]] [[package]]
name = "rodio" name = "rodio"
version = "0.22.2" version = "0.22.2"
source = "git+https://github.com/arabianq/rodio.git?rev=4746803c2e5f67004264ac946708d47a9faf510c#4746803c2e5f67004264ac946708d47a9faf510c" source = "git+https://github.com/arabianq/rodio.git?rev=610955cf524467690cc9abe1a6277e8bffcadb71#610955cf524467690cc9abe1a6277e8bffcadb71"
dependencies = [ dependencies = [
"cpal", "cpal",
"dasp_sample", "dasp_sample",
"num-rational", "rubato",
"symphonia", "symphonia",
"symphonia-adapter-libopus", "symphonia-adapter-libopus",
"thiserror 2.0.19", "thiserror 2.0.19",
@@ -3512,6 +3528,20 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "rubato"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4be9c88e3d722d3d36939e41941f6b6f52810c1235bf19998a7d4535b402892"
dependencies = [
"audioadapter",
"audioadapter-buffers",
"num-integer",
"num-traits",
"visibility",
"windowfunctions",
]
[[package]] [[package]]
name = "rust-i18n" name = "rust-i18n"
version = "4.2.1" version = "4.2.1"
@@ -3608,9 +3638,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.15.0" version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
dependencies = [ dependencies = [
"zeroize", "zeroize",
] ]
@@ -3732,7 +3762,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.2", "syn 3.0.3",
] ]
[[package]] [[package]]
@@ -3756,7 +3786,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.2", "syn 3.0.3",
] ]
[[package]] [[package]]
@@ -4183,9 +4213,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "3.0.2" version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a207d6d6a2b7fc470b80443726053f18a2481b7e1eee970597051596567987a3" checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -4307,7 +4337,7 @@ checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.2", "syn 3.0.3",
] ]
[[package]] [[package]]
@@ -4708,6 +4738,17 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "visibility"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.5.0" version = "2.5.0"
@@ -4799,9 +4840,9 @@ dependencies = [
[[package]] [[package]]
name = "wayland-backend" name = "wayland-backend"
version = "0.3.15" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8"
dependencies = [ dependencies = [
"cc", "cc",
"downcast-rs", "downcast-rs",
@@ -4813,9 +4854,9 @@ dependencies = [
[[package]] [[package]]
name = "wayland-client" name = "wayland-client"
version = "0.31.14" version = "0.31.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073"
dependencies = [ dependencies = [
"bitflags 2.13.1", "bitflags 2.13.1",
"rustix 1.1.4", "rustix 1.1.4",
@@ -4911,9 +4952,9 @@ dependencies = [
[[package]] [[package]]
name = "wayland-scanner" name = "wayland-scanner"
version = "0.31.10" version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quick-xml", "quick-xml",
@@ -5096,6 +5137,15 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "windowfunctions"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90628d739333b7c5d2ee0b70210b97b8cddc38440c682c96fd9e2c24c2db5f3a"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.62.2" version = "0.62.2"
+2 -2
View File
@@ -8,7 +8,7 @@ members = [
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
version = "1.12.5" version = "1.13.0"
edition = "2024" edition = "2024"
authors = ["arabian"] authors = ["arabian"]
homepage = "https://pwsp.arabianq.ru" homepage = "https://pwsp.arabianq.ru"
@@ -52,7 +52,7 @@ rustix = { version = "1.1.4", features = ["process"] }
rust-i18n = "4.0.0" rust-i18n = "4.0.0"
sys-locale = "0.3.2" sys-locale = "0.3.2"
rodio = { git = "https://github.com/arabianq/rodio.git", rev = "4746803c2e5f67004264ac946708d47a9faf510c", default-features = false, features = [ rodio = { git = "https://github.com/arabianq/rodio.git", rev = "610955cf524467690cc9abe1a6277e8bffcadb71", default-features = false, features = [
"symphonia-all", "symphonia-all",
"symphonia-libopus", "symphonia-libopus",
"playback", "playback",
+4 -4
View File
@@ -1,6 +1,6 @@
pkgbase = pwsp-bin pkgbase = pwsp-bin
pkgdesc = Lets you play audio files through your microphone (Pre-built binaries) pkgdesc = Lets you play audio files through your microphone (Pre-built binaries)
pkgver = 1.12.5 pkgver = 1.13.0
pkgrel = 1 pkgrel = 1
url = https://github.com/arabianq/pipewire-soundpad url = https://github.com/arabianq/pipewire-soundpad
arch = x86_64 arch = x86_64
@@ -10,11 +10,11 @@ pkgbase = pwsp-bin
depends = alsa-lib depends = alsa-lib
provides = pwsp provides = pwsp
conflicts = pwsp conflicts = pwsp
source = pipewire-soundpad-1.12.5.tar.gz :: https://github.com/arabianq/pipewire-soundpad/archive/refs/tags/v1.12.5.tar.gz source = pipewire-soundpad-1.13.0.tar.gz :: https://github.com/arabianq/pipewire-soundpad/archive/refs/tags/v1.13.0.tar.gz
sha256sums = SKIP sha256sums = SKIP
source_x86_64 = pwsp-1.12.5-x86_64.zip :: https://github.com/arabianq/pipewire-soundpad/releases/download/v1.12.5/pwsp-v1.12.5-linux-x64.zip source_x86_64 = pwsp-1.13.0-x86_64.zip :: https://github.com/arabianq/pipewire-soundpad/releases/download/v1.13.0/pwsp-v1.13.0-linux-x64.zip
sha256sums_x86_64 = SKIP sha256sums_x86_64 = SKIP
source_aarch64 = pwsp-1.12.5-aarch64.zip :: https://github.com/arabianq/pipewire-soundpad/releases/download/v1.12.5/pwsp-v1.12.5-linux-arm64.zip source_aarch64 = pwsp-1.13.0-aarch64.zip :: https://github.com/arabianq/pipewire-soundpad/releases/download/v1.13.0/pwsp-v1.13.0-linux-arm64.zip
sha256sums_aarch64 = SKIP sha256sums_aarch64 = SKIP
pkgname = pwsp-bin pkgname = pwsp-bin
+1 -1
View File
@@ -1,7 +1,7 @@
# Maintainer: Alexander Tarasov <a.tevg@ya.ru> # Maintainer: Alexander Tarasov <a.tevg@ya.ru>
pkgname=pwsp-bin pkgname=pwsp-bin
_pkgname=pipewire-soundpad _pkgname=pipewire-soundpad
pkgver=1.12.5 pkgver=1.13.0
pkgrel=1 pkgrel=1
pkgdesc="Lets you play audio files through your microphone (Pre-built binaries)" pkgdesc="Lets you play audio files through your microphone (Pre-built binaries)"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
+2 -2
View File
@@ -1,6 +1,6 @@
pkgbase = pwsp pkgbase = pwsp
pkgdesc = Lets you play audio files through your microphone pkgdesc = Lets you play audio files through your microphone
pkgver = 1.12.5 pkgver = 1.13.0
pkgrel = 1 pkgrel = 1
url = https://github.com/arabianq/pipewire-soundpad url = https://github.com/arabianq/pipewire-soundpad
arch = x86_64 arch = x86_64
@@ -12,7 +12,7 @@ pkgbase = pwsp
makedepends = cmake makedepends = cmake
makedepends = pipewire makedepends = pipewire
makedepends = alsa-lib makedepends = alsa-lib
source = https://github.com/arabianq/pipewire-soundpad/archive/refs/tags/v1.12.5.tar.gz source = https://github.com/arabianq/pipewire-soundpad/archive/refs/tags/v1.13.0.tar.gz
sha256sums = SKIP sha256sums = SKIP
pkgname = pwsp pkgname = pwsp
+1 -1
View File
@@ -1,7 +1,7 @@
# Maintainer: Alexander Tarasov <a.tevg@ya.ru> # Maintainer: Alexander Tarasov <a.tevg@ya.ru>
pkgsubn=pwsp pkgsubn=pwsp
pkgname=pwsp pkgname=pwsp
pkgver=1.12.5 pkgver=1.13.0
pkgrel=1 pkgrel=1
pkgdesc="Lets you play audio files through your microphone" pkgdesc="Lets you play audio files through your microphone"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
File diff suppressed because one or more lines are too long
@@ -25,7 +25,7 @@
<name>arabian</name> <name>arabian</name>
</developer> </developer>
<releases> <releases>
<release version="1.12.5" date="2026-07-20" /> <release version="1.13.0" date="2026-07-24" />
</releases> </releases>
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
</component> </component>
+1 -1
View File
@@ -13,7 +13,7 @@
Name: pwsp Name: pwsp
Version: 1.12.5 Version: 1.13.0
Release: %autorelease Release: %autorelease
Summary: Lets you play audio files through your microphone Summary: Lets you play audio files through your microphone
+12
View File
@@ -69,6 +69,8 @@ enum Actions {
#[clap(short, long)] #[clap(short, long)]
id: Option<u32>, id: Option<u32>,
}, },
/// Daemon configuration
SaveDaemonConfig,
/// Play a sound by hotkey slot name /// Play a sound by hotkey slot name
PlayHotkey { slot: String }, PlayHotkey { slot: String },
/// Remove the hotkey slot /// Remove the hotkey slot
@@ -86,6 +88,8 @@ enum GetCommands {
#[clap(short, long)] #[clap(short, long)]
id: Option<u32>, id: Option<u32>,
}, },
/// Volume multiplier for all tracks
VolumeMultiplier,
/// Playback position (in seconds) /// Playback position (in seconds)
Position { Position {
#[clap(short, long)] #[clap(short, long)]
@@ -106,6 +110,8 @@ enum GetCommands {
Inputs, Inputs,
/// Version of the daemon /// Version of the daemon
DaemonVersion, DaemonVersion,
/// Daemon configuration
DaemonConfig,
/// Full player state /// Full player state
FullState, FullState,
/// All hotkey slots /// All hotkey slots
@@ -120,6 +126,8 @@ enum SetCommands {
#[clap(short, long)] #[clap(short, long)]
id: Option<u32>, id: Option<u32>,
}, },
/// Volume multiplier for all tracks
VolumeMultiplier { volume: f32 },
/// Playback position (in seconds) /// Playback position (in seconds)
Position { Position {
position: f32, position: f32,
@@ -165,6 +173,7 @@ async fn main() -> Result<()> {
concurrent, concurrent,
} => Request::play(&file_path.to_string_lossy(), concurrent), } => Request::play(&file_path.to_string_lossy(), concurrent),
Actions::ToggleLoop { id } => Request::toggle_loop(id), Actions::ToggleLoop { id } => Request::toggle_loop(id),
Actions::SaveDaemonConfig => Request::save_daemon_config(),
Actions::PlayHotkey { slot } => Request::play_hotkey(&slot), Actions::PlayHotkey { slot } => Request::play_hotkey(&slot),
Actions::ClearHotkey { slot } => Request::clear_hotkey(&slot), Actions::ClearHotkey { slot } => Request::clear_hotkey(&slot),
Actions::ClearHotkeyKey { slot } => Request::clear_hotkey_key(&slot), Actions::ClearHotkeyKey { slot } => Request::clear_hotkey_key(&slot),
@@ -172,6 +181,7 @@ async fn main() -> Result<()> {
Commands::Get { parameter } => match parameter { Commands::Get { parameter } => match parameter {
GetCommands::IsPaused => Request::get_is_paused(), GetCommands::IsPaused => Request::get_is_paused(),
GetCommands::Volume { id } => Request::get_volume(id), GetCommands::Volume { id } => Request::get_volume(id),
GetCommands::VolumeMultiplier => Request::get_volume_multiplier(),
GetCommands::Position { id } => Request::get_position(id), GetCommands::Position { id } => Request::get_position(id),
GetCommands::Duration { id } => Request::get_duration(id), GetCommands::Duration { id } => Request::get_duration(id),
GetCommands::State => Request::get_state(), GetCommands::State => Request::get_state(),
@@ -179,11 +189,13 @@ async fn main() -> Result<()> {
GetCommands::Input => Request::get_input(), GetCommands::Input => Request::get_input(),
GetCommands::Inputs => Request::get_inputs(), GetCommands::Inputs => Request::get_inputs(),
GetCommands::DaemonVersion => Request::get_daemon_version(), GetCommands::DaemonVersion => Request::get_daemon_version(),
GetCommands::DaemonConfig => Request::get_daemon_config(),
GetCommands::FullState => Request::get_full_state(), GetCommands::FullState => Request::get_full_state(),
GetCommands::Hotkeys => Request::get_hotkeys(), GetCommands::Hotkeys => Request::get_hotkeys(),
}, },
Commands::Set { parameter } => match parameter { Commands::Set { parameter } => match parameter {
SetCommands::Volume { volume, id } => Request::set_volume(volume, id), SetCommands::Volume { volume, id } => Request::set_volume(volume, id),
SetCommands::VolumeMultiplier { volume } => Request::set_volume_multiplier(volume),
SetCommands::Position { position, id } => Request::seek(position, id), SetCommands::Position { position, id } => Request::seek(position, id),
SetCommands::Input { name } => Request::set_input(&name), SetCommands::Input { name } => Request::set_input(&name),
SetCommands::Loop { enabled, id } => Request::set_loop(&enabled, id), SetCommands::Loop { enabled, id } => Request::set_loop(&enabled, id),
+22
View File
@@ -217,6 +217,28 @@ kz = "Әрқашан дыбыс деңгейін есте сақтау"
he = "זכור תמיד עוצמת קול" he = "זכור תמיד עוצמת קול"
pt-BR = "Lembrar volume" pt-BR = "Lembrar volume"
[gui.settings.remember_volume_multiplier]
en = "Always remember volume multiplier"
ru = "Всегда запоминать множитель громкости"
es = "Recordar siempre el multiplicador de volumen"
fr = "Toujours se souvenir du multiplicateur de volume"
zh = "始终记住音量乘数"
ar = "تذكر ضرب الصوت دائمًا"
kz = "Әрқашан дыбыс деңгейін есте сақтау"
he = "זכור תמיד עוצמת קול"
pt-BR = "Lembrar volume"
[gui.settings.volume_multiplier]
en = "Volume multiplier"
ru = "Множитель громкости"
es = "Multiplicador de volumen"
fr = "Multiplicateur de volume"
zh = "音量乘数"
ar = "ضرب الصوت"
kz = "Дыбыс деңгейі"
he = "עוצמת קול"
pt-BR = "Volume"
[gui.settings.remember_mic] [gui.settings.remember_mic]
en = "Always remember microphone" en = "Always remember microphone"
ru = "Всегда запоминать микрофон" ru = "Всегда запоминать микрофон"
+8 -8
View File
@@ -9,14 +9,13 @@ use itertools::Itertools;
use pwsp_lib::{ use pwsp_lib::{
types::{ types::{
audio_player::PlayerState, audio_player::PlayerState,
config::GuiConfig, config::{GuiConfig, HotkeyConfig},
config::HotkeyConfig,
gui::{AppState, AudioPlayerState}, gui::{AppState, AudioPlayerState},
socket::Request, socket::Request,
}, },
utils::{ utils::gui::{
daemon::get_daemon_config, get_daemon_config, get_gui_config, make_request_async, make_request_sync,
gui::{get_gui_config, make_request_async, make_request_sync, start_app_state_thread}, start_app_state_thread, update_daemon_config,
}, },
}; };
use rfd::FileDialog; use rfd::FileDialog;
@@ -130,10 +129,11 @@ impl SoundpadGui {
pub fn set_input(&mut self, name: String) { pub fn set_input(&mut self, name: String) {
make_request_async(Request::set_input(&name)); make_request_async(Request::set_input(&name));
if self.config.save_input { if self.config.save_input
let mut daemon_config = get_daemon_config(); && let Ok(mut daemon_config) = get_daemon_config()
{
daemon_config.default_input_name = Some(name); daemon_config.default_input_name = Some(name);
daemon_config.save_to_file().ok(); update_daemon_config(&daemon_config).ok();
} }
} }
+23 -4
View File
@@ -3,7 +3,7 @@ use eframe::{App, Frame as EFrame};
use egui::{CentralPanel, Context, ThemePreference}; use egui::{CentralPanel, Context, ThemePreference};
use pwsp_lib::{ use pwsp_lib::{
types::{config::PreferredTheme, socket::Request}, types::{config::PreferredTheme, socket::Request},
utils::{daemon::get_daemon_config, gui::make_request_async}, utils::gui::{get_daemon_config, make_request_async, update_daemon_config},
}; };
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@@ -85,10 +85,29 @@ impl App for SoundpadGui {
self.app_state.ignore_volume_update_until = self.app_state.ignore_volume_update_until =
Some(Instant::now() + Duration::from_millis(300)); Some(Instant::now() + Duration::from_millis(300));
if self.config.save_volume { if self.config.save_volume
let mut daemon_config = get_daemon_config(); && let Ok(mut daemon_config) = get_daemon_config()
{
daemon_config.default_volume = Some(self.app_state.volume_slider_value); daemon_config.default_volume = Some(self.app_state.volume_slider_value);
daemon_config.save_to_file().ok(); update_daemon_config(&daemon_config).ok();
}
}
if self.app_state.volume_multiplier_dragged {
make_request_async(Request::set_volume_multiplier(
self.app_state.volume_multiplier_slider_value,
));
self.app_state.volume_multiplier_dragged = false;
self.app_state.ignore_volume_multiplier_update_until =
Some(Instant::now() + Duration::from_millis(300));
if self.config.save_volume_multiplier
&& let Ok(mut daemon_config) = get_daemon_config()
{
daemon_config.default_volume_multiplier =
Some(self.app_state.volume_multiplier_slider_value);
update_daemon_config(&daemon_config).ok();
} }
} }
+39 -1
View File
@@ -1,5 +1,5 @@
use crate::gui::SoundpadGui; use crate::gui::SoundpadGui;
use egui::{Align, Button, Color32, ComboBox, Layout, RichText, Ui}; use egui::{Align, Button, Color32, ComboBox, Layout, RichText, Slider, Ui};
use egui_material_icons::icons::ICON_ARROW_BACK; use egui_material_icons::icons::ICON_ARROW_BACK;
use pwsp_lib::types::config::PreferredTheme; use pwsp_lib::types::config::PreferredTheme;
use rust_i18n::t; use rust_i18n::t;
@@ -34,6 +34,10 @@ impl SoundpadGui {
&mut self.config.save_volume, &mut self.config.save_volume,
t!("gui.settings.remember_volume"), t!("gui.settings.remember_volume"),
); );
let save_volume_multiplier_response = ui.checkbox(
&mut self.config.save_volume_multiplier,
t!("gui.settings.remember_volume_multiplier"),
);
let save_input_response = let save_input_response =
ui.checkbox(&mut self.config.save_input, t!("gui.settings.remember_mic")); ui.checkbox(&mut self.config.save_input, t!("gui.settings.remember_mic"));
let save_scale_response = ui.checkbox( let save_scale_response = ui.checkbox(
@@ -46,6 +50,7 @@ impl SoundpadGui {
); );
if save_volume_response.changed() if save_volume_response.changed()
|| save_volume_multiplier_response.changed()
|| save_input_response.changed() || save_input_response.changed()
|| save_scale_response.changed() || save_scale_response.changed()
|| pause_on_exit_response.changed() || pause_on_exit_response.changed()
@@ -88,6 +93,39 @@ impl SoundpadGui {
} }
// -------------------------------- // --------------------------------
ui.separator();
// ----------- Sliders ------------
// Volume multiplier
let should_update_multiplier = !self.app_state.volume_multiplier_dragged
&& self
.app_state
.ignore_volume_multiplier_update_until
.map(|t| std::time::Instant::now() > t)
.unwrap_or(true);
if should_update_multiplier {
self.app_state.volume_multiplier_slider_value =
self.audio_player_state.volume_multiplier;
}
ui.horizontal(|ui| {
let slider = Slider::new(
&mut self.app_state.volume_multiplier_slider_value,
0.01..=3.0,
);
let response = ui.add(slider);
ui.label(t!("gui.settings.volume_multiplier"));
if response.changed() {
// This condition is required to avoid spamming requests while dragging, but to allow changing the value via TextEdit
if !response.dragged() || (response.dragged() && response.drag_stopped()) {
self.app_state.volume_multiplier_dragged = true;
}
}
});
// --------------------------------
ui.with_layout(Layout::bottom_up(Align::Min), |ui| { ui.with_layout(Layout::bottom_up(Align::Min), |ui| {
ui.label(t!( ui.label(t!(
"gui.settings.version", "gui.settings.version",
+20 -7
View File
@@ -1,7 +1,7 @@
use crate::{ use crate::{
types::pipewire::DeviceType, types::pipewire::DeviceType,
utils::{ utils::{
daemon::get_daemon_config, daemon::with_daemon_config,
pipewire::{PwTerminator, create_link, get_device, link_player_to_virtual_mic}, pipewire::{PwTerminator, create_link, get_device, link_player_to_virtual_mic},
}, },
}; };
@@ -40,6 +40,7 @@ pub struct FullState {
pub state: PlayerState, pub state: PlayerState,
pub tracks: Vec<TrackInfo>, pub tracks: Vec<TrackInfo>,
pub volume: f32, pub volume: f32,
pub volume_multiplier: f32,
pub current_input: String, pub current_input: String,
pub all_inputs: HashMap<String, String>, pub all_inputs: HashMap<String, String>,
} }
@@ -63,12 +64,19 @@ pub struct AudioPlayer {
pub input_device_name: Option<String>, pub input_device_name: Option<String>,
pub volume: f32, // Master volume pub volume: f32, // Master volume
pub volume_multiplier: f32,
} }
impl AudioPlayer { impl AudioPlayer {
pub async fn new() -> Result<Self> { pub async fn new() -> Result<Self> {
let daemon_config = get_daemon_config(); let (default_input_name, default_volume, default_volume_multiplier) =
let default_volume = daemon_config.default_volume.unwrap_or(1.0); with_daemon_config(|c| {
(
c.default_input_name.clone(),
c.default_volume.unwrap_or(1.0),
c.default_volume_multiplier.unwrap_or(1.0),
)
});
let mut audio_player = AudioPlayer { let mut audio_player = AudioPlayer {
stream_handle: None, stream_handle: None,
@@ -77,9 +85,10 @@ impl AudioPlayer {
input_link_sender: None, input_link_sender: None,
player_link_sender: None, player_link_sender: None,
input_device_name: daemon_config.default_input_name.clone(), input_device_name: default_input_name,
volume: default_volume, volume: default_volume,
volume_multiplier: default_volume_multiplier,
}; };
if audio_player.input_device_name.is_some() { if audio_player.input_device_name.is_some() {
@@ -262,12 +271,16 @@ impl AudioPlayer {
if let Some(id) = id { if let Some(id) = id {
if let Some(sound) = self.tracks.get_mut(&id) { if let Some(sound) = self.tracks.get_mut(&id) {
sound.volume = volume; sound.volume = volume;
sound.sink.set_volume(self.volume * volume); sound
.sink
.set_volume(self.volume * sound.volume * self.volume_multiplier);
} }
} else { } else {
self.volume = volume; self.volume = volume;
for sound in self.tracks.values_mut() { for sound in self.tracks.values_mut() {
sound.sink.set_volume(self.volume * sound.volume); sound
.sink
.set_volume(self.volume * sound.volume * self.volume_multiplier);
} }
} }
} }
@@ -347,7 +360,7 @@ impl AudioPlayer {
.ok_or_else(|| anyhow::anyhow!("stream_handle is unexpectedly missing"))? .ok_or_else(|| anyhow::anyhow!("stream_handle is unexpectedly missing"))?
.mixer(); .mixer();
let sink = Player::connect_new(mixer); let sink = Player::connect_new(mixer);
sink.set_volume(self.volume); // Default volume is 1.0 * master sink.set_volume(self.volume * self.volume_multiplier); // Default volume is 1.0 * master
sink.append(source); sink.append(source);
sink.play(); sink.play();
+82 -2
View File
@@ -1,12 +1,12 @@
use crate::{ use crate::{
types::{ types::{
audio_player::{FullState, PlayerState}, audio_player::{FullState, PlayerState},
config::HotkeyConfig, config::{DaemonConfig, HotkeyConfig},
socket::{Request, Response}, socket::{Request, Response},
}, },
utils::{ utils::{
commands::parse_command, commands::parse_command,
daemon::get_audio_player, daemon::{get_audio_player, with_daemon_config},
pipewire::{get_all_devices, get_device}, pipewire::{get_all_devices, get_device},
}, },
}; };
@@ -46,11 +46,17 @@ pub struct GetVolumeCommand {
pub id: Option<u32>, pub id: Option<u32>,
} }
pub struct GetVolumeMultiplierCommand {}
pub struct SetVolumeCommand { pub struct SetVolumeCommand {
pub volume: Option<f32>, pub volume: Option<f32>,
pub id: Option<u32>, pub id: Option<u32>,
} }
pub struct SetVolumeMultiplierCommand {
pub volume_multiplier: Option<f32>,
}
pub struct GetPositionCommand { pub struct GetPositionCommand {
pub id: Option<u32>, pub id: Option<u32>,
} }
@@ -127,6 +133,14 @@ pub struct ClearHotkeyKeyCommand {
pub slot: Option<String>, pub slot: Option<String>,
} }
pub struct GetDaemonConfigCommand {}
pub struct SaveDaemonConfigCommand {}
pub struct UpdateDaemonConfigCommand {
pub new_config: DaemonConfig,
}
#[async_trait] #[async_trait]
impl Executable for PingCommand { impl Executable for PingCommand {
async fn execute(&self) -> Response { async fn execute(&self) -> Response {
@@ -262,6 +276,18 @@ impl Executable for GetVolumeCommand {
} }
} }
#[async_trait]
impl Executable for GetVolumeMultiplierCommand {
async fn execute(&self) -> Response {
let audio_player = match get_audio_player().await {
Ok(player) => player.lock().await,
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
};
Response::new(true, audio_player.volume_multiplier.to_string())
}
}
#[async_trait] #[async_trait]
impl Executable for SetVolumeCommand { impl Executable for SetVolumeCommand {
async fn execute(&self) -> Response { async fn execute(&self) -> Response {
@@ -278,6 +304,26 @@ impl Executable for SetVolumeCommand {
} }
} }
#[async_trait]
impl Executable for SetVolumeMultiplierCommand {
async fn execute(&self) -> Response {
if let Some(volume_multiplier) = self.volume_multiplier {
let mut audio_player = match get_audio_player().await {
Ok(player) => player.lock().await,
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
};
audio_player.volume_multiplier = volume_multiplier;
audio_player.set_volume(volume_multiplier, None); // Reset current volume for all tracks to apply multiplier
Response::new(
true,
format!("Audio volume multiplier was set to {}", volume_multiplier),
)
} else {
Response::new(false, "Invalid volume multiplier value")
}
}
}
#[async_trait] #[async_trait]
impl Executable for GetPositionCommand { impl Executable for GetPositionCommand {
async fn execute(&self) -> Response { async fn execute(&self) -> Response {
@@ -508,6 +554,7 @@ impl Executable for GetFullStateCommand {
state: audio_player.get_state(), state: audio_player.get_state(),
tracks: audio_player.get_tracks(), tracks: audio_player.get_tracks(),
volume: audio_player.volume, volume: audio_player.volume,
volume_multiplier: audio_player.volume_multiplier,
current_input: current_input_nick, current_input: current_input_nick,
all_inputs, all_inputs,
}; };
@@ -723,3 +770,36 @@ impl Executable for ClearHotkeyKeyCommand {
} }
} }
} }
#[async_trait]
impl Executable for GetDaemonConfigCommand {
async fn execute(&self) -> Response {
let serialized = with_daemon_config(|c| serde_json::to_string(&c));
match serialized {
Ok(s) => Response::new(true, s),
Err(err) => Response::new(false, format!("Failed to serialize daemon config: {}", err)),
}
}
}
#[async_trait]
impl Executable for SaveDaemonConfigCommand {
async fn execute(&self) -> Response {
match with_daemon_config(|c| c.save_to_file()) {
Ok(_) => Response::new(true, "Daemon config saved successfully"),
Err(err) => Response::new(false, format!("Failed to save daemon config: {}", err)),
}
}
}
#[async_trait]
impl Executable for UpdateDaemonConfigCommand {
async fn execute(&self) -> Response {
with_daemon_config(|c| {
c.clone_from(&self.new_config);
});
Response::new(true, "Daemon config updated successfully")
}
}
+3
View File
@@ -17,6 +17,7 @@ use std::{
pub struct DaemonConfig { pub struct DaemonConfig {
pub default_input_name: Option<String>, pub default_input_name: Option<String>,
pub default_volume: Option<f32>, pub default_volume: Option<f32>,
pub default_volume_multiplier: Option<f32>,
} }
impl DaemonConfig { impl DaemonConfig {
@@ -73,6 +74,7 @@ pub struct GuiConfig {
pub left_panel_width: f32, pub left_panel_width: f32,
pub save_volume: bool, pub save_volume: bool,
pub save_volume_multiplier: bool,
pub save_input: bool, pub save_input: bool,
pub save_scale_factor: bool, pub save_scale_factor: bool,
pub pause_on_exit: bool, pub pause_on_exit: bool,
@@ -117,6 +119,7 @@ impl Default for GuiConfig {
left_panel_width: 280.0, left_panel_width: 280.0,
save_volume: false, save_volume: false,
save_volume_multiplier: false,
save_input: false, save_input: false,
save_scale_factor: false, save_scale_factor: false,
pause_on_exit: false, pause_on_exit: false,
+4
View File
@@ -31,13 +31,16 @@ pub struct AppState {
pub show_settings: bool, pub show_settings: bool,
pub volume_dragged: bool, pub volume_dragged: bool,
pub volume_multiplier_dragged: bool,
pub force_focus_search: bool, pub force_focus_search: bool,
pub volume_slider_value: f32, pub volume_slider_value: f32,
pub volume_multiplier_slider_value: f32,
pub search_field_id: Option<Id>, pub search_field_id: Option<Id>,
pub ignore_volume_update_until: Option<Instant>, pub ignore_volume_update_until: Option<Instant>,
pub ignore_volume_multiplier_update_until: Option<Instant>,
pub current_dir: Option<PathBuf>, pub current_dir: Option<PathBuf>,
pub dirs: Vec<PathBuf>, pub dirs: Vec<PathBuf>,
@@ -65,6 +68,7 @@ pub struct AudioPlayerState {
pub tracks: Vec<TrackInfo>, pub tracks: Vec<TrackInfo>,
pub volume: f32, // Master volume pub volume: f32, // Master volume
pub volume_multiplier: f32,
pub current_input: String, pub current_input: String,
pub all_inputs: HashMap<String, String>, pub all_inputs: HashMap<String, String>,
+30
View File
@@ -1,3 +1,4 @@
use crate::types::config::DaemonConfig;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
@@ -94,6 +95,10 @@ impl Request {
Request::new("get_volume", args) Request::new("get_volume", args)
} }
pub fn get_volume_multiplier() -> Self {
Request::new("get_volume_multiplier", vec![])
}
pub fn get_position(id: Option<u32>) -> Self { pub fn get_position(id: Option<u32>) -> Self {
let mut args = vec![]; let mut args = vec![];
let id_str; let id_str;
@@ -138,6 +143,13 @@ impl Request {
Request::new("set_volume".to_string(), args) Request::new("set_volume".to_string(), args)
} }
pub fn set_volume_multiplier(volume: f32) -> Self {
Request::new(
"set_volume_multiplier",
vec![("volume_multiplier", &volume.to_string())],
)
}
pub fn seek(position: f32, id: Option<u32>) -> Self { pub fn seek(position: f32, id: Option<u32>) -> Self {
let mut args = vec![("position".to_string(), position.to_string())]; let mut args = vec![("position".to_string(), position.to_string())];
if let Some(id) = id { if let Some(id) = id {
@@ -172,6 +184,24 @@ impl Request {
Request::new("get_daemon_version", vec![]) Request::new("get_daemon_version", vec![])
} }
pub fn get_daemon_config() -> Self {
Request::new("get_daemon_config", vec![])
}
pub fn save_daemon_config() -> Self {
Request::new("save_daemon_config", vec![])
}
pub fn update_daemon_config(new_config: &DaemonConfig) -> Self {
Request::new(
"update_daemon_config",
vec![(
"new_config",
&serde_json::to_string(&new_config).unwrap_or_default(),
)],
)
}
pub fn get_full_state() -> Self { pub fn get_full_state() -> Self {
Request::new("get_full_state", vec![]) Request::new("get_full_state", vec![])
} }
+22 -1
View File
@@ -1,4 +1,4 @@
use crate::types::{commands::*, socket::Request}; use crate::types::{commands::*, config::DaemonConfig, socket::Request};
use std::path::PathBuf; use std::path::PathBuf;
@@ -15,6 +15,7 @@ pub fn parse_command(request: &Request) -> Option<Box<dyn Executable + Send>> {
"is_paused" => Some(Box::new(IsPausedCommand {})), "is_paused" => Some(Box::new(IsPausedCommand {})),
"get_state" => Some(Box::new(GetStateCommand {})), "get_state" => Some(Box::new(GetStateCommand {})),
"get_volume" => Some(Box::new(GetVolumeCommand { id })), "get_volume" => Some(Box::new(GetVolumeCommand { id })),
"get_volume_multiplier" => Some(Box::new(GetVolumeMultiplierCommand {})),
"set_volume" => { "set_volume" => {
let volume = request let volume = request
.args .args
@@ -24,6 +25,15 @@ pub fn parse_command(request: &Request) -> Option<Box<dyn Executable + Send>> {
.ok(); .ok();
Some(Box::new(SetVolumeCommand { volume, id })) Some(Box::new(SetVolumeCommand { volume, id }))
} }
"set_volume_multiplier" => {
let volume_multiplier = request
.args
.get("volume_multiplier")
.unwrap_or(&String::new())
.parse::<f32>()
.ok();
Some(Box::new(SetVolumeMultiplierCommand { volume_multiplier }))
}
"get_position" => Some(Box::new(GetPositionCommand { id })), "get_position" => Some(Box::new(GetPositionCommand { id })),
"seek" => { "seek" => {
let position = request let position = request
@@ -119,6 +129,17 @@ pub fn parse_command(request: &Request) -> Option<Box<dyn Executable + Send>> {
key_chord, key_chord,
})) }))
} }
"get_daemon_config" => Some(Box::new(GetDaemonConfigCommand {})),
"save_daemon_config" => Some(Box::new(SaveDaemonConfigCommand {})),
"update_daemon_config" => {
let new_config = request
.args
.get("new_config")
.and_then(|nc| serde_json::from_str::<DaemonConfig>(nc).ok())
.unwrap_or_default();
Some(Box::new(UpdateDaemonConfigCommand { new_config }))
}
_ => None, _ => None,
} }
} }
+23 -7
View File
@@ -7,36 +7,52 @@ use crate::types::{
use anyhow::Result; use anyhow::Result;
use std::os::unix::fs::{DirBuilderExt, MetadataExt, PermissionsExt}; use std::os::unix::fs::{DirBuilderExt, MetadataExt, PermissionsExt};
use std::path::PathBuf; use std::path::PathBuf;
use std::{env, error::Error, fs}; use std::{
env,
error::Error,
fs,
sync::{Arc, Mutex, OnceLock},
};
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
net::UnixStream, net::UnixStream,
sync::{Mutex, OnceCell}, sync::{Mutex as AsyncMutex, OnceCell},
time::{Duration, sleep}, time::{Duration, sleep},
}; };
static AUDIO_PLAYER: OnceCell<Mutex<AudioPlayer>> = OnceCell::const_new(); static AUDIO_PLAYER: OnceCell<AsyncMutex<AudioPlayer>> = OnceCell::const_new();
static DAEMON_CONFIG: OnceLock<Arc<Mutex<DaemonConfig>>> = OnceLock::new();
pub async fn get_audio_player() -> Result<&'static Mutex<AudioPlayer>, String> { pub async fn get_audio_player() -> Result<&'static AsyncMutex<AudioPlayer>, String> {
AUDIO_PLAYER AUDIO_PLAYER
.get_or_try_init(|| async { .get_or_try_init(|| async {
println!("Initializing audio player"); println!("Initializing audio player");
match AudioPlayer::new().await { match AudioPlayer::new().await {
Ok(player) => Ok(Mutex::new(player)), Ok(player) => Ok(AsyncMutex::new(player)),
Err(err) => Err(err.to_string()), Err(err) => Err(err.to_string()),
} }
}) })
.await .await
} }
pub fn get_daemon_config() -> DaemonConfig { pub fn get_daemon_config() -> &'static Arc<Mutex<DaemonConfig>> {
DaemonConfig::load_from_file().unwrap_or_else(|_| { DAEMON_CONFIG.get_or_init(|| {
Arc::new(Mutex::new(DaemonConfig::load_from_file().unwrap_or_else(
|_| {
let config = DaemonConfig::default(); let config = DaemonConfig::default();
config.save_to_file().ok(); config.save_to_file().ok();
config config
},
)))
}) })
} }
pub fn with_daemon_config<R>(f: impl FnOnce(&mut DaemonConfig) -> R) -> R {
let config = get_daemon_config().clone();
let mut guard = config.lock().unwrap();
f(&mut guard)
}
fn get_current_uid() -> u32 { fn get_current_uid() -> u32 {
rustix::process::geteuid().as_raw() rustix::process::geteuid().as_raw()
} }
+15 -1
View File
@@ -1,7 +1,7 @@
use crate::{ use crate::{
types::{ types::{
audio_player::FullState, audio_player::FullState,
config::{GuiConfig, HotkeyConfig}, config::{DaemonConfig, GuiConfig, HotkeyConfig},
gui::AudioPlayerState, gui::AudioPlayerState,
socket::{Request, Response}, socket::{Request, Response},
}, },
@@ -24,6 +24,19 @@ pub fn get_gui_config() -> GuiConfig {
}) })
} }
pub fn update_daemon_config(new_config: &DaemonConfig) -> Result<()> {
make_request_sync(Request::update_daemon_config(new_config))?;
make_request_async(Request::save_daemon_config());
Ok(())
}
pub fn get_daemon_config() -> Result<DaemonConfig> {
let response = make_request_sync(Request::get_daemon_config())?;
let config: DaemonConfig = serde_json::from_str(&response.message)?;
Ok(config)
}
pub fn make_request_sync(request: Request) -> Result<Response> { pub fn make_request_sync(request: Request) -> Result<Response> {
tokio::task::block_in_place(|| { tokio::task::block_in_place(|| {
tokio::runtime::Handle::current() tokio::runtime::Handle::current()
@@ -103,6 +116,7 @@ pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerSt
}; };
guard.tracks = full_state.tracks; guard.tracks = full_state.tracks;
guard.volume = full_state.volume; guard.volume = full_state.volume;
guard.volume_multiplier = full_state.volume_multiplier;
guard.current_input = full_state guard.current_input = full_state
.current_input .current_input
.split(" - ") .split(" - ")