use std::{ net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, time::Instant, }; use anyhow::bail; use rosc::{OscMessage, OscPacket, OscType}; use crate::overlays::{keyboard::KEYBOARD_NAME, watch::WATCH_NAME}; use super::common::OverlayContainer; pub struct OscSender { last_sent: Instant, upstream: UdpSocket, } impl OscSender { pub fn new(send_port: u16) -> anyhow::Result { let ip = IpAddr::V4(Ipv4Addr::LOCALHOST); let Ok(upstream) = UdpSocket::bind("0.0.0.0:0") else { bail!("Failed to bind UDP socket - OSC will not function."); }; let Ok(_) = upstream.connect(SocketAddr::new(ip, send_port)) else { bail!("Failed to connect UDP socket - OSC will not function."); }; Ok(Self { upstream, last_sent: Instant::now(), }) } pub fn send_message(&self, addr: String, args: Vec) -> anyhow::Result<()> { let packet = OscPacket::Message(OscMessage { addr, args }); let Ok(bytes) = rosc::encoder::encode(&packet) else { bail!("Could not encode OSC packet."); }; let Ok(_) = self.upstream.send(&bytes) else { bail!("Could not send OSC packet."); }; Ok(()) } pub fn send_params(&mut self, overlays: &OverlayContainer) -> anyhow::Result<()> where D: Default, { if self.last_sent.elapsed().as_millis() < 100 { return Ok(()); } let mut num_overlays = 0; let mut has_keyboard = false; let mut has_wrist = false; for o in overlays.iter() { if !o.state.want_visible { continue; } match o.state.name.as_ref() { WATCH_NAME => has_wrist = true, KEYBOARD_NAME => has_keyboard = true, _ => { if o.state.interactable { num_overlays += 1 } } } } self.send_message( "/avatar/parameters/isOverlayOpen".into(), vec![OscType::Bool(num_overlays > 0)], )?; self.send_message( "/avatar/parameters/isKeyboardOpen".into(), vec![OscType::Bool(has_keyboard)], )?; self.send_message( "/avatar/parameters/isWristVisible".into(), vec![OscType::Bool(has_wrist)], )?; self.send_message( "/avatar/parameters/openOverlayCount".into(), vec![OscType::Int(num_overlays)], )?; Ok(()) } }