streamdeck-workspace/streamdeck-daemon/src/dbus.rs

168 lines
5.2 KiB
Rust

use crate::{
message::{DeckMessage, InputMessage, ManagerMessage, ObsMessage},
store::Store,
};
use std::collections::BTreeSet;
use streamdeck_common::Input;
use tokio::sync::{
broadcast::Receiver,
mpsc::{self, Sender},
};
use zbus::SignalContext;
mod button;
mod deck;
mod obs;
pub(crate) const fn daemon_path() -> &'static str {
"/dog/asonix/git/asonix/StreamdeckDaemon"
}
pub(crate) const fn obs_path() -> &'static str {
"/dog/asonix/git/asonix/StreamdeckDaemon/obs"
}
pub(crate) fn deck_path(serial_number: &str) -> String {
String::new() + daemon_path() + "/" + serial_number
}
pub(crate) fn button_path(serial_number: &str, input: &Input) -> String {
deck_path(serial_number) + "/" + input.to_string().as_str()
}
struct Daemon {
manager: Sender<ManagerMessage>,
decks: BTreeSet<String>,
}
fn fail(e: impl std::fmt::Display) -> zbus::fdo::Error {
zbus::fdo::Error::Failed(e.to_string())
}
#[zbus::dbus_interface(name = "dog.asonix.git.asonix.StreamdeckDaemon")]
impl Daemon {
async fn enable_discovery(&self) -> zbus::fdo::Result<()> {
tracing::debug!("enable_discovery");
self.manager
.send(ManagerMessage::EnableDiscovery)
.await
.map_err(fail)
}
async fn disable_discovery(&self) -> zbus::fdo::Result<()> {
tracing::debug!("disable_discovery");
self.manager
.send(ManagerMessage::DisableDiscovery)
.await
.map_err(fail)
}
async fn get_decks(&self) -> Vec<String> {
tracing::debug!("get_decks");
self.decks.iter().cloned().collect()
}
#[dbus_interface(signal)]
async fn deck_added(ctx: &SignalContext<'_>, serial_number: &str) -> zbus::Result<()>;
#[dbus_interface(signal)]
async fn deck_removed(ctx: &SignalContext<'_>, serial_number: &str) -> zbus::Result<()>;
}
#[tracing::instrument(skip_all)]
pub(crate) async fn spawn(
store: Store,
mut input: Receiver<InputMessage>,
mut deck_rx: mpsc::Receiver<DeckMessage>,
obs: Sender<ObsMessage>,
manager: Sender<ManagerMessage>,
) -> anyhow::Result<zbus::Connection> {
let state = Daemon {
manager: manager.clone(),
decks: BTreeSet::new(),
};
let conn = zbus::ConnectionBuilder::session()?
.name("dog.asonix.git.asonix.StreamdeckDaemon")?
.serve_at(daemon_path(), state)?
.build()
.await?;
obs::Obs::hydrate(conn.clone(), obs).await?;
let conn2 = conn.clone();
tokio::spawn(async move {
let conn = conn2;
while let Some(msg) = deck_rx.recv().await {
match msg {
DeckMessage::Open(config) => {
if let Ok(daemon_ref) = conn
.object_server()
.interface::<_, Daemon>(daemon_path())
.await
{
let mut daemon = daemon_ref.get_mut().await;
if let Err(e) = deck::Deck::hydrate(
conn.clone(),
config.serial_number.clone(),
config.product_name,
config.port_name,
store.clone(),
)
.await
{
tracing::warn!("Error hydrating deck: {}", e);
continue;
}
daemon.decks.insert(config.serial_number.clone());
let ctx = daemon_ref.signal_context();
let _ = Daemon::deck_added(ctx, &config.serial_number).await;
}
}
DeckMessage::Close(serial_number) => {
let _ = conn
.object_server()
.remove::<deck::Deck, _>(deck_path(&serial_number))
.await;
if let Ok(daemon_ref) = conn
.object_server()
.interface::<_, Daemon>(daemon_path())
.await
{
let mut daemon = daemon_ref.get_mut().await;
daemon.decks.remove(&serial_number);
let ctx = daemon_ref.signal_context();
let _ = Daemon::deck_removed(ctx, &serial_number).await;
}
}
}
}
});
let conn2 = conn.clone();
tokio::spawn(async move {
let conn = conn2;
while let Ok((serial_number, input)) = input.recv().await {
if let Ok(deck_ref) = conn
.object_server()
.interface::<_, deck::Deck>(deck_path(&serial_number))
.await
{
let _ = deck::Deck::button_pushed(deck_ref.signal_context(), input.clone()).await;
}
if let Ok(button_ref) = conn
.object_server()
.interface::<_, button::Button>(button_path(&serial_number, &input))
.await
{
let _ = button::Button::pushed(button_ref.signal_context()).await;
}
}
});
Ok(conn)
}