Commit Graph

47 Commits

Author SHA1 Message Date
Tarasov Aleksandr 0f8abbc443 refactor(daemon): Refactor src/utils/pipewire.rs to flatten deep conditionals and consolidate logic. (#124)
Moved redundant struct initialization into `AudioDevice::new` and unified port mapping assignments in an `add_port` method. This removes nesting using early returns and eliminates an unnecessary clone on the hashmap conversion step.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-06-01 22:51:58 +03:00
Tarasov Aleksandr 5e47e7d6fb feat(gui): support for soundpad:// uri (#123)
* feat(gui): support for soundpad:// uri

* fix: flatpak

* do not open gui when downloading file
2026-05-28 00:58:03 +03:00
dependabot[bot] 84a4a01282 chore(deps): bump tokio from 1.52.1 to 1.52.3 (#115)
* chore(deps): bump tokio from 1.52.1 to 1.52.3

Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.52.1 to 1.52.3.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.52.1...tokio-1.52.3)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.52.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* fix: replace std::sync::mpsc::sync_channel with tokio::sync::oneshot::channel to avoid deadlocks

* deps: update cargo-sources.json

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: arabian <a.tevg@ya.ru>
2026-05-22 00:28:46 +03:00
Tarasov Aleksandr fe655be59a fix: insecure fallback directory and secure file creation (#111)
* 🔒 Fix insecure fallback directory and secure file creation

The daemon's fallback runtime directory `get_runtime_dir()` was hardcoded to `/run/pwsp`, creating a risk of shared, insecure access in multi-user systems.
This commit secures the fallback logic by:
1. Creating a user-specific temporary directory (`/tmp/pwsp-$UID`).
2. Ensuring directory creation happens atomically with `0o700` permissions using `std::fs::DirBuilder`.
3. Validating the fallback directory strictly (checking UID, 0o700 permissions, and symlink status) if it already exists to mitigate symlink attacks.
4. Using `libc::geteuid()` for robust cross-platform UID extraction.
5. Fixing `is_daemon_running` and locking logic to use `fs::OpenOptions` instead of `fs::File::create` to prevent accidental file truncation on active lock files.

Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com>

* 🔒 Fix insecure fallback directory and secure file creation

The daemon's fallback runtime directory `get_runtime_dir()` was hardcoded to `/run/pwsp`, creating a risk of shared, insecure access in multi-user systems.
This commit secures the fallback logic by:
1. Creating a user-specific temporary directory (`/tmp/pwsp-$UID`).
2. Ensuring directory creation happens atomically with `0o700` permissions using `std::fs::DirBuilder`.
3. Validating the fallback directory strictly (checking UID, 0o700 permissions, and symlink status) if it already exists to mitigate symlink attacks.
4. Using safe `rustix::process::geteuid()` for robust cross-platform UID extraction, avoiding `unsafe` blocks.
5. Fixing `is_daemon_running` and locking logic to use `fs::OpenOptions` instead of `fs::File::create` to prevent accidental file truncation on active lock files.

Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com>

* small refactor

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-05-17 17:43:51 +03:00
Tarasov Aleksandr dc1ecc81ea refactor: replace all rust Result with anyhow::Result (#103) 2026-05-15 19:32:26 +03:00
RiDDiX a6d93ff528 fix clippy lints under rust 1.95 (#90) 2026-04-28 14:13:11 +03:00
Tarasov Aleksandr fc2cd5e2da fix(daemon): Remove .expect() panics from PipeWire initialization (#89)
This commit addresses a security and stability vulnerability where failures in PipeWire context setup, connection, or registry acquisition would crash the entire thread (or daemon) via `.expect()` panics.

We now gracefully capture and propagate initialization errors up the call stack. A `sync_channel(0)` is used to signal the success or failure of the initial pipewire setup back to the calling functions (`get_all_devices`, `create_virtual_mic`, `create_link`). This prevents unexpected crashes and improves error resilience.

Also removed unneeded `pw_sender` panics on channel termination by simply dropping/ignoring the result.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-27 22:54:17 +03:00
Tarasov Aleksandr 10f9937dc3 tests: parse_command set_volume edge cases (#73)
Add unit tests for parse_command in src/utils/commands.rs to ensure
robust handling of set_volume edge cases including missing or
invalid volume and id arguments.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-25 14:37:55 +03:00
Tarasov Aleksandr 7f8b7194b6 refactor: move PipeWire initialization into a reusable helper function (#69)
Extract duplicated `pipewire::init()`, `MainLoopRc::new()`, and `ContextRc::new()` setup code from `pw_get_global_objects_thread`, `create_virtual_mic`, and `create_link` into a shared `setup_pipewire_context` helper in `src/utils/pipewire.rs`. Also ran codebase-wide linters to improve code quality.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-20 19:22:50 +03:00
Tarasov Aleksandr 302f153b91 refactor: break down handle_input into smaller methods in src/gui/input.rs (#67)
Extract sections from the long `handle_input` function into smaller,
context-specific helper methods such as `handle_hotkey_assignment`,
`handle_toggles`, `handle_playback_and_focus`, `handle_file_playback`,
`handle_navigation`, and `handle_hotkey_triggers`. This significantly
improves the maintainability and readability of `src/gui/input.rs`
while preserving original functionality.

In addition, ran `cargo clippy --fix` on the project to resolve a few
other minor health issues, like collapsing nested `if` statements and
reducing unnecessary allocations.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-20 19:21:59 +03:00
Tarasov Aleksandr f87dcb1564 refactor: remove unnecessary string cloning when finding hotkey conflicts (#68)
Changed `HotkeyConfig::find_conflicts` to return a `Vec<(&str, &str)>` rather than allocating owned `Strings`. In `src/gui/draw.rs`, the code now builds a `HashSet<&str>` directly from the borrowed strings using array-based flat-mapping, avoiding intermediate `Vec` allocations and redundant clones.

Benchmarked to be approximately 3.5x faster in scenarios involving many configured slots.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-20 19:20:54 +03:00
Tarasov Aleksandr 2a8fcca06b Fix virtual mic audio linking (#62)
* Fix virtual mic audio linking by managing it in AudioPlayer lifecycle

- Moved `link_player_to_virtual_mic` to `src/utils/pipewire.rs` and updated it to return a termination sender.
- Added `player_link_sender` to `AudioPlayer` to manage the PipeWire link between the daemon and the virtual mic.
- Integrated linking logic into `AudioPlayer::play` and `AudioPlayer::update` to ensure the link is established when audio starts playing.
- Ensured the link is terminated in `AudioPlayer::drop_stream` when the audio sink is closed.
- Removed redundant and potentially failing startup linking loop from the daemon.
- Fixed log spam by ensuring `link_player` is only attempted when necessary and errors are handled gracefully.
- Maintained compatibility with stable Rust by avoiding unstable features.

Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com>

* small refactor

* refactor

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-17 14:24:58 +03:00
Tarasov Aleksandr 70c7e3789b 🔒 Fix potential memory exhaustion in socket reads (#59)
Addresses a security vulnerability where the daemon or client could be
forced to allocate up to 10MB of memory per malformed socket message,
potentially leading to Out-Of-Memory (OOM) crashes.

Changes:
- Introduced a central `MAX_MESSAGE_SIZE` constant of 128KB in `src/types/socket.rs`.
- Enforced the 128KB limit on incoming requests in `src/bin/daemon.rs`.
- Enforced the 128KB limit on incoming responses in `src/utils/daemon.rs`.
- Preserved detailed `eprintln!` logging when messages are rejected.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-17 13:56:29 +03:00
Tarasov Aleksandr 42c0170044 fix: hotkeys setting from pwsp-gui (#56)
* refactor: do not overwrite incorrect hotkeys config

* fix: hotkeys not saved via pwsp-gui
2026-04-12 17:05:10 +03:00
RiDDiX a156df346b feat: add hotkey system (#48)
* feat: add hotkey system for playing individual sounds

Slot-based hotkey mappings stored in ~/.config/pwsp/hotkeys.json.
Daemon serves hotkey IPC commands for CLI/compositor bindings.
GUI supports focused hotkey triggers, a dedicated Hotkeys panel
with search and conflict detection, file badges, and a key chord
capture dialog. CLI gains play-hotkey, get hotkeys, set hotkey,
set hotkey-key, and clear-hotkey subcommands.

* feat: add global hotkey support via evdev

Listen for keyboard events directly from /dev/input using evdev,
enabling hotkeys to work system-wide regardless of window focus
or display server (X11, GNOME, KDE Plasma, Hyprland).

The daemon spawns async listeners for each keyboard device at
startup, tracks modifier state, and triggers playback when a
configured chord matches. Requires the user to be in the 'input'
group; logs a warning and continues without global hotkeys if
devices are inaccessible.

* various changes

* refactor: route hotkey mutations through daemon IPC

GUI no longer writes hotkey config directly to disk. Instead, all
mutations (set slot, set key chord, clear chord, remove slot) are
sent to the daemon via IPC, which persists the changes. The state
thread periodically syncs the hotkey config back from the daemon,
so CLI-made changes are reflected in the GUI.

New IPC commands: set_hotkey_action (arbitrary action per slot),
clear_hotkey_key (remove key chord without removing the slot).

Also removes unreachable capture overlay from draw_hotkeys().

* small refactor

---------

Co-authored-by: arabian <a.tevg@ya.ru>
2026-04-06 21:43:41 +03:00
Tarasov Aleksandr 6114b9a7f8 🔒 [security] Set restricted permissions on socket and runtime directory (#40)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-03-21 21:03:48 +03:00
Tarasov Aleksandr b8baeb6226 refactor: replace contains_key/unwrap with if let Some in pipewire.rs (#39)
This commit replaces two instances of `contains_key` followed by
`get_mut().unwrap()` with the more idiomatic `if let Some(...)` pattern
in `src/utils/pipewire.rs`. This reduces redundant hash map lookups and
eliminates potential panics from `unwrap()`.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-03-21 21:03:30 +03:00
Tarasov Aleksandr 3add499bd7 Optimize get_device with iterator chaining (#38)
Replaces manual vector extension and linear search with an iterator
chain. This avoids an unnecessary allocation and potential reallocation
of the `input_devices` vector and allows for short-circuiting if the
device is found early.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-03-08 02:40:25 +03:00
Tarasov Aleksandr 261f83efd4 🧹 Code Health: Handle AudioPlayer initialization errors safely (#35)
* 🧹 Refactor: Replace unsafe unwrap in get_audio_player

Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com>

* 🧹 Refactor: Replace unsafe unwrap in get_audio_player

Resolved GitHub CI failure where a syntax error was introduced due to a bad automated merge with main. Rebased cleanly to ensure only the get_audio_player code health changes are included.

Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com>

* Delete tests/perf_play.rs

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-03-08 01:36:25 +03:00
Tarasov Aleksandr d6effc972e 🧹 refactor: replace unsafe unwraps in pipewire port parsing with safe destructuring (#34)
Replaced sequential unwraps on pipewire properties ("node.id", "port.id", "port.name")
with an `if let` and `and_then()` pattern in `src/utils/pipewire.rs`. This provides
safety against daemon crashes when properties are missing or malformed by silently
returning instead of panicking.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-03-08 00:41:06 +03:00
Tarasov Aleksandr 077518019f perf: cache and sort microphone inputs once instead of every frame (#27)
In `draw_footer`, the `all_inputs` HashMap was being collected into a Vec
and sorted on every single UI frame (~60 FPS). This caused unnecessary
overhead and allocations.

This commit introduces a cached `all_inputs_sorted` vector in the
`AudioPlayerState` which is populated only when the inputs map changes
during the state sync. `draw_footer` now loops over this pre-sorted
vector directly.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-03-08 00:28:45 +03:00
Tarasov Aleksandr 4b50645c93 refactor(gui): replace unsafe unwrap on gui thread lock (#23)
Replaces `audio_player_state_shared.lock().unwrap()` with `.unwrap_or_else(|e| e.into_inner())` in `src/utils/gui.rs` to allow safe recovery from poisoned locks and avoid application panics.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-03-08 00:25:42 +03:00
arabianq 92a576de37 fix(pwsp-daemon): added retries to link_player_to_virtual_mic()
https://github.com/arabianq/pipewire-soundpad/issues/15
2026-03-06 15:19:06 +03:00
arabianq ce948ce678 feat: you can now get volume for all sound individually, not only via fullstate 2026-02-25 00:34:05 +03:00
arabianq 58e5f039be feat(cli, flatpak): implemented kill action for pwsp-cli.
use it instead of pkill in the flatpak wrapper
2026-02-23 13:40:41 +03:00
arabianq b816d2aa88 feat: get daemon's version using pwsp-cli
pwsp-cli get daemon-version
2026-02-14 15:43:17 +03:00
arabianq ce5910b9a6 fix: improve device lookup in get_device function and update daemon device name 2026-01-28 22:30:33 +03:00
arabianq 5ea9b3b0ba feat(daemon): implementet get full-state command 2026-01-28 02:41:33 +03:00
arabianq d385e5356e refactor: simplify device retrieval in link_player_to_virtual_mic function 2026-01-28 01:30:03 +03:00
arabianq bae10edc99 feat: replace synchronous requests with asynchronous counterparts for improved performance 2026-01-25 00:14:14 +03:00
arabianq 4d54443593 feat: add master volume slider 2026-01-24 23:45:33 +03:00
arabianq 5afe3dd45b refactor: remove unused requests and variables from start_app_state_thread 2026-01-24 22:36:26 +03:00
arabianq bd75ac6190 refactor: remove unused fields from AudioPlayerState 2026-01-24 22:34:25 +03:00
arabianq 3e6a8b6e79 feat: first attemp to support playing multiple tracks in parallel 2026-01-24 22:18:42 +03:00
arabianq 16e94e71d3 fix: update input device properties to output for capture_MONO 2026-01-16 16:16:27 +03:00
arabianq 6cf4a9744d replace println! with eprintln! for errors 2026-01-08 02:46:44 +03:00
arabianq 6c06da7b0d feat: implemented toggle-loop 2026-01-02 04:39:48 +03:00
arabianq a7af1caace fix: highly improved GUI performance 2026-01-02 03:59:05 +03:00
arabianq ac667f1273 feat: make_request_sync now uses tokio instead of futures 2026-01-02 03:33:20 +03:00
arabianq 531a49dbdf feat: implemented loop support in pwsp-gui 2026-01-02 03:18:51 +03:00
arabianq 2a13c25230 feat: implemented loop support in pwsp-cli 2026-01-02 02:57:34 +03:00
arabianq 546b6fd13f add toggle-pause command to pwsp-cli 2025-12-16 21:08:47 +03:00
arabianq fa217c3b25 fix not working with mono input devices 2025-12-16 21:08:47 +03:00
arabianq 3981833a3b call pipewire::init in every pipewire thread 2025-12-16 21:08:47 +03:00
arabianq 874c8063aa use device name instead of node id to get audio device 2025-12-16 21:08:47 +03:00
arabianq 23f5ee0d22 change code to work with pipewire 0.9.2 2025-12-16 21:08:47 +03:00
arabianq dee908a347 1.0.0 rewrite 2025-12-16 21:08:47 +03:00