diff --git a/PenguinWave/src-tauri/src/audio/mod.rs b/PenguinWave/src-tauri/src/audio/mod.rs index 0635645..9944866 100644 --- a/PenguinWave/src-tauri/src/audio/mod.rs +++ b/PenguinWave/src-tauri/src/audio/mod.rs @@ -1 +1,13 @@ +use crate::event::{CHAT_SINK, GAME_SINK}; + pub mod equalizer; + +pub struct SinkConfig { + pub(crate) name: &'static str, + pub(crate) display_name: &'static str, +} + +pub const SINK_CONFIGS: &[SinkConfig] = &[ + SinkConfig { name: GAME_SINK, display_name: GAME_SINK }, + SinkConfig { name: CHAT_SINK, display_name: CHAT_SINK }, +]; \ No newline at end of file diff --git a/PenguinWave/src-tauri/src/lib.rs b/PenguinWave/src-tauri/src/lib.rs index 7068814..c5637a9 100644 --- a/PenguinWave/src-tauri/src/lib.rs +++ b/PenguinWave/src-tauri/src/lib.rs @@ -4,13 +4,11 @@ mod headsets; mod system; mod utils; use crate::event::chatmix_listener::{init_chatmix_monitor, ChatMixMonitoringState}; -use crate::system::pipewire::PipeWireManager; +use crate::system::pipewire::{init_virtual_channels, PipeWireManager}; use crate::utils::hardware_utils::*; use crate::utils::state::AppStateManager; use serde::{Deserialize, Serialize}; use tauri::{Manager}; -use crate::event::{CHAT_SINK, GAME_SINK}; -use crate::headsets::steelseries; #[tauri::command] fn greet(name: &str) -> String { @@ -36,23 +34,6 @@ fn greet(name: &str) -> String { name, device_count ); - // Verify if game/chat sinks exist, if not create them and link with the devices - - if let Ok(sinks) = PipeWireManager::list_sinks() { - if !sinks.contains(&GAME_SINK.to_string()) && !sinks.contains(&CHAT_SINK.to_string()) - { - let game_sink_id = PipeWireManager::create_virtual_sink(&GAME_SINK.to_string(), &GAME_SINK.to_string()); - let chat_sink_id = PipeWireManager::create_virtual_sink(&CHAT_SINK.to_string(), &CHAT_SINK.to_string()); - - if let (Ok(_), Ok(_)) = (game_sink_id, chat_sink_id) { - let _ = PipeWireManager::link_ports(&format!("{}:monitor_FL", CHAT_SINK), &format!("{}:playback_FL", steelseries::SINK_NAME)); - let _ = PipeWireManager::link_ports(&format!("{}:monitor_FL", CHAT_SINK), &format!("{}:playback_FR", steelseries::SINK_NAME)); - let _ = PipeWireManager::link_ports(&format!("{}:monitor_FL", GAME_SINK), &format!("{}:playback_FL", steelseries::SINK_NAME)); - let _ = PipeWireManager::link_ports(&format!("{}:monitor_FL", GAME_SINK), &format!("{}:playback_FR", steelseries::SINK_NAME)); - } - }; - } - for (i, device_list) in devices.iter().enumerate() { let battery = device_list.device.request_battery(); let chatmix = device_list.device.request_chatmix(); @@ -153,6 +134,7 @@ pub fn run() { builder .manage(ChatMixMonitoringState::default()) .setup(|app| { + init_virtual_channels(app.handle()); init_chatmix_monitor(app.handle()); Ok(()) }) diff --git a/PenguinWave/src-tauri/src/system/pipewire.rs b/PenguinWave/src-tauri/src/system/pipewire.rs index ba328f7..cd3c476 100644 --- a/PenguinWave/src-tauri/src/system/pipewire.rs +++ b/PenguinWave/src-tauri/src/system/pipewire.rs @@ -1,6 +1,8 @@ use std::process::Command; use serde::{Serialize, Deserialize}; use anyhow::{Result, anyhow}; +use tauri::AppHandle; +use crate::audio::SINK_CONFIGS; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct PipeWireNode { @@ -311,4 +313,84 @@ impl PipeWireManager { Ok(()) } + pub fn get_default_sink() -> Result { + let output = Command::new("pactl") + .args(&["get-default-sink"]) + .output()?; + + if !output.status.success() { + return Err(anyhow!("Failed to get default sink: {}", + String::from_utf8_lossy(&output.stderr))); + } + + let sink_name = String::from_utf8_lossy(&output.stdout) + .trim() + .to_string(); + + if sink_name.is_empty() { + return Err(anyhow!("No default sink found")); + } + + Ok(sink_name) + } +} + +pub fn init_virtual_channels(app_handle: &AppHandle) { + match try_init_virtual_channels() { + Ok(()) => println!("Virtual channels initialized successfully"), + Err(e) => eprintln!("Failed to initialize virtual channels: {}", e), + } +} + +fn try_init_virtual_channels() -> Result<(), Box> { + let existing_sinks = PipeWireManager::list_sinks() + .map_err(|e| format!("Failed to list existing sinks: {}", e))?; + + let sinks_to_create: Vec<_> = SINK_CONFIGS + .iter() + .filter(|config| !existing_sinks.contains(&config.name.to_string())) + .collect(); + + if sinks_to_create.is_empty() { + println!("All required sinks already exist"); + return Ok(()); + } + + let mut successfully_created = Vec::new(); + + for config in sinks_to_create { + match PipeWireManager::create_virtual_sink(&config.name.to_string(), &config.display_name.to_string()) { + Ok(_) => { + successfully_created.push(config.name.to_string()); + println!("Created virtual sink: {}", config.name); + } + Err(e) => { + eprintln!("Failed to create sink {}: {}", config.name, e); + } + } + } + + for sink_name in &successfully_created { + link_sink_to_default(sink_name)?; + } + + Ok(()) +} + +fn link_sink_to_default(sink_name: &str) -> Result<(), Box> { + // Get the current default sink + let default_sink = PipeWireManager::get_default_sink() + .map_err(|e| format!("Failed to get default sink: {}", e))?; + + let connections = [ + (format!("{}:monitor_FL", sink_name), format!("{}:playback_FL", default_sink)), + (format!("{}:monitor_FR", sink_name), format!("{}:playback_FR", default_sink)), + ]; + + for (source, target) in connections { + PipeWireManager::link_ports(&source, &target) + .map_err(|e| format!("Failed to link {} to {}: {}", source, target, e))?; + } + + Ok(()) } \ No newline at end of file