diff --git a/src/backend/openvr/mod.rs b/src/backend/openvr/mod.rs index 50ecc6e..859ed38 100644 --- a/src/backend/openvr/mod.rs +++ b/src/backend/openvr/mod.rs @@ -124,10 +124,6 @@ pub fn openvr_run(running: Arc, show_by_default: bool) -> Result<(), let mut playspace = playspace::PlayspaceMover::new(); playspace.playspace_changed(&mut compositor_mgr, &mut chaperone_mgr); - #[cfg(feature = "osc")] - let mut osc_sender = - crate::backend::osc::OscSender::new(state.session.config.osc_out_port).ok(); - set_action_manifest(&mut input_mgr)?; let mut input_source = OpenVrInputSource::new(&mut input_mgr)?; @@ -333,8 +329,8 @@ pub fn openvr_run(running: Arc, show_by_default: bool) -> Result<(), } #[cfg(feature = "osc")] - if let Some(ref mut sender) = osc_sender { - let _ = sender.send_params(&overlays, &state); + if let Some(ref mut sender) = state.osc_sender { + let _ = sender.send_params(&overlays, &state.input_state.devices); }; #[cfg(feature = "wayvr")] diff --git a/src/backend/openxr/mod.rs b/src/backend/openxr/mod.rs index bd1acc7..cb06a7c 100644 --- a/src/backend/openxr/mod.rs +++ b/src/backend/openxr/mod.rs @@ -109,10 +109,6 @@ pub fn openxr_run(running: Arc, show_by_default: bool) -> Result<(), .ok() }); - #[cfg(feature = "osc")] - let mut osc_sender = - crate::backend::osc::OscSender::new(app_state.session.config.osc_out_port).ok(); - let (session, mut frame_wait, mut frame_stream) = unsafe { let raw_session = helpers::create_overlay_session( &xr_instance, @@ -315,8 +311,8 @@ pub fn openxr_run(running: Arc, show_by_default: bool) -> Result<(), } #[cfg(feature = "osc")] - if let Some(ref mut sender) = osc_sender { - let _ = sender.send_params(&overlays, &app_state); + if let Some(ref mut sender) = app_state.osc_sender { + let _ = sender.send_params(&overlays, &app_state.input_state.devices); }; let (_, views) = xr_state.session.locate_views( diff --git a/src/backend/osc.rs b/src/backend/osc.rs index 69089e2..5e046e0 100644 --- a/src/backend/osc.rs +++ b/src/backend/osc.rs @@ -8,12 +8,9 @@ use rosc::{OscMessage, OscPacket, OscType}; use crate::overlays::{keyboard::KEYBOARD_NAME, watch::WATCH_NAME}; -use crate::{ - backend::input::TrackedDeviceRole, - state::AppState, -}; +use crate::backend::input::TrackedDeviceRole; -use super::common::OverlayContainer; +use super::{common::OverlayContainer, input::TrackedDevice}; pub struct OscSender { last_sent_overlay: Instant, @@ -53,7 +50,11 @@ impl OscSender { Ok(()) } - pub fn send_params(&mut self, overlays: &OverlayContainer, app: &AppState) -> anyhow::Result<()> + pub fn send_params( + &mut self, + overlays: &OverlayContainer, + devices: &Vec, + ) -> anyhow::Result<()> where D: Default, { @@ -96,7 +97,6 @@ impl OscSender { "/avatar/parameters/openOverlayCount".into(), vec![OscType::Int(num_overlays)], )?; - } // send battery levels every 10 seconds @@ -108,25 +108,25 @@ impl OscSender { let mut tracker_total_bat = 0.0; let mut controller_total_bat = 0.0; - for device in &app.input_state.devices { + for device in devices { let tracker_param; // soc is the battery level (set to device status.charge) let level = device.soc.unwrap_or(-1.0); let parameter = match device.role { - TrackedDeviceRole::None => {continue} - TrackedDeviceRole::Hmd => { + TrackedDeviceRole::None => continue, + TrackedDeviceRole::Hmd => { // legacy OVR Toolkit style (int) // as of 20 Nov 2024 OVR Toolkit uses int 0-100, but this may change in a future update. //TODO: update this once their implementation matches their docs self.send_message( "/avatar/parameters/hmdBattery".into(), - vec![OscType::Int((level * 100.0f32).round() as i32)], + vec![OscType::Int((level * 100.0f32).round() as i32)], )?; "headset" } - TrackedDeviceRole::LeftHand => { + TrackedDeviceRole::LeftHand => { controller_count += 1; controller_total_bat += level; "leftController" @@ -136,7 +136,7 @@ impl OscSender { controller_total_bat += level; "rightController" } - TrackedDeviceRole::Tracker => { + TrackedDeviceRole::Tracker => { tracker_count += 1; tracker_total_bat += level; tracker_param = format!("tracker{tracker_count}"); @@ -147,25 +147,37 @@ impl OscSender { // send device battery parameters self.send_message( format!("/avatar/parameters/{parameter}Battery").into(), - vec![OscType::Float(level)], + vec![OscType::Float(level)], )?; self.send_message( format!("/avatar/parameters/{parameter}Charging").into(), - vec![OscType::Bool(device.charging)], + vec![OscType::Bool(device.charging)], )?; } // send average controller and tracker battery parameters self.send_message( format!("/avatar/parameters/averageControllerBattery").into(), - vec![OscType::Float(controller_total_bat / controller_count as f32)], + vec![OscType::Float( + controller_total_bat / controller_count as f32, + )], )?; self.send_message( format!("/avatar/parameters/averageTrackerBattery").into(), - vec![OscType::Float(tracker_total_bat / tracker_count as f32)], + vec![OscType::Float(tracker_total_bat / tracker_count as f32)], )?; } Ok(()) } + + pub fn send_single_param( + &mut self, + parameter: String, + values: Vec, + ) -> anyhow::Result<()> { + self.send_message(parameter, values)?; + + Ok(()) + } } diff --git a/src/gui/modular/button.rs b/src/gui/modular/button.rs index 45e6bc8..4066913 100644 --- a/src/gui/modular/button.rs +++ b/src/gui/modular/button.rs @@ -25,9 +25,12 @@ use crate::{ state::AppState, }; -#[cfg(not(feature = "wayvr"))] +#[cfg(any(not(feature = "wayvr"), not(feature = "osc")))] use crate::overlays::toast::error_toast_str; +#[cfg(feature = "osc")] +use rosc::OscType; + use super::{ExecArgs, ModularControl, ModularData}; #[derive(Deserialize, Clone)] @@ -180,6 +183,26 @@ pub enum ButtonAction { System { action: SystemAction, }, + SendOscValue { + parameter: Arc, + values: Option>, + }, +} + +#[derive(Deserialize, Clone)] +#[serde(tag = "type")] +#[cfg(feature = "osc")] +pub enum OscValue { + Int { value: i32 }, + Float { value: f32 }, + String { value: String }, + Bool { value: bool }, +} +#[derive(Deserialize, Clone)] +#[serde(tag = "type")] +#[cfg(not(feature = "osc"))] +pub enum OscValue { + None, } pub(super) struct PressData { @@ -391,6 +414,34 @@ fn handle_action(action: &ButtonAction, press: &mut PressData, app: &mut AppStat ButtonAction::DragMultiplier { delta } => { app.session.config.space_drag_multiplier += delta; } + ButtonAction::SendOscValue { parameter, values } => { + #[cfg(feature = "osc")] + if let Some(ref mut sender) = app.osc_sender { + // convert OscValue to OscType + let mut converted: Vec = Vec::new(); + + for value in values.as_ref().unwrap() { + let converted_value = match value { + OscValue::Bool { value } => OscType::Bool(*value), + OscValue::Int { value } => OscType::Int(*value), + OscValue::Float { value } => OscType::Float(*value), + OscValue::String { value } => OscType::String(value.to_string()), + }; + + converted.push(converted_value); + } + + let _ = sender.send_single_param(parameter.to_string(), converted); + audio_thump(app); // play sound for feedback + }; + + #[cfg(not(feature = "osc"))] + { + let _ = ¶meter; + let _ = &values; + error_toast_str(app, "OSC feature is not enabled"); + } + } } } diff --git a/src/state.rs b/src/state.rs index 71ebe8a..ff80ca9 100644 --- a/src/state.rs +++ b/src/state.rs @@ -14,6 +14,9 @@ use { std::{cell::RefCell, rc::Rc}, }; +#[cfg(feature = "osc")] +use crate::backend::osc::OscSender; + use crate::{ backend::{input::InputState, overlay::OverlayID, task::TaskContainer}, config::{AStrMap, GeneralConfig}, @@ -49,6 +52,9 @@ pub struct AppState { pub sprites: AStrMap>, pub keyboard_focus: KeyboardFocus, + #[cfg(feature = "osc")] + pub osc_sender: Option, + #[cfg(feature = "wayvr")] pub wayvr: Option>>, // Dynamically created if requested } @@ -102,6 +108,9 @@ impl AppState { .wayvr_config .post_load(&session.config, &mut tasks)?; + #[cfg(feature = "osc")] + let osc_sender = crate::backend::osc::OscSender::new(session.config.osc_out_port).ok(); + Ok(AppState { fc: FontCache::new(session.config.primary_font.clone())?, session, @@ -115,6 +124,9 @@ impl AppState { sprites: AStrMap::new(), keyboard_focus: KeyboardFocus::PhysicalScreen, + #[cfg(feature = "osc")] + osc_sender, + #[cfg(feature = "wayvr")] wayvr, })