diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 28f010cd..04400fa2 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -5433,6 +5433,8 @@ pub fn run() {
if let Err(error) = initialize_app_log(app.handle()) {
std::eprintln!("[internal][tauri][warn] {}", error.message);
}
+ #[cfg(target_os = "linux")]
+ linux_media::mirror_volume(app.handle());
// Always built, so toggling the setting takes effect without a restart.
if let Err(error) = build_tray(app.handle()) {
std::eprintln!("[internal][tauri][warn] tray unavailable: {error}");
diff --git a/src-tauri/src/linux_media.rs b/src-tauri/src/linux_media.rs
index 4ac4622d..dc051e0a 100644
--- a/src-tauri/src/linux_media.rs
+++ b/src-tauri/src/linux_media.rs
@@ -4,9 +4,17 @@ use souvlaki::{
};
use std::sync::Mutex;
use std::time::Duration;
-use tauri::{AppHandle, Emitter, State};
+use tauri::{AppHandle, Emitter, Listener, Manager, State};
const MEDIA_CONTROL_EVENT: &str = "linux-media-control";
+/// Emitted by the main window on every volume or mute change; the mini player follows it too.
+const VOLUME_SYNC_EVENT: &str = "player-volume-sync";
+
+#[derive(Deserialize)]
+struct VolumeSync {
+ muted: bool,
+ volume: f64,
+}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -31,6 +39,7 @@ struct CachedMetadata {
pub struct LinuxMediaSession {
controls: Mutex