It's a start

This commit is contained in:
Aode (Lion) 2021-10-02 11:11:42 -05:00
commit 94539a08c4
8 changed files with 1431 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

1077
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

14
Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[package]
name = "streamdeck-dbus"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1"
tokio = { version = "1", features = ["full"] }
tokio-serial = "5.4.1"
tokio-actors = { version = "0.1", git = "https://git.asonix.dog/asonix/tokio-actors", branch = "main" }
uuid = { version = "0.8.2", features = ["serde", "v4"]}
zbus = "2.0.0-beta.7"

45
src/connector.rs Normal file
View file

@ -0,0 +1,45 @@
use crate::deck::Deck;
use crate::ManagerMessage;
use std::path::PathBuf;
use tokio_actors::{BoxFuture, Context, SendHandle};
use uuid::Uuid;
use zbus::Connection;
#[derive(Clone, Debug)]
pub(crate) struct Connector {
connection: Connection,
parent: SendHandle<ManagerMessage>,
}
impl Connector {
pub(crate) fn new(connection: Connection, parent: SendHandle<ManagerMessage>) -> Self {
println!("new connector");
Connector { connection, parent }
}
pub(crate) fn turn<'a>(
&'a mut self,
path: PathBuf,
ctx: &'a mut Context<PathBuf>,
) -> BoxFuture<'a> {
println!("Connector");
Box::pin(async move {
ctx.stop();
// TODO: Run connection logic
let deck = Deck::build(
self.connection.clone(),
// TODO: look up device name
"".to_owned(),
// TODO: look up device id
Uuid::new_v4().as_bytes().to_vec(),
path,
self.parent.clone(),
)
.await?;
self.parent.send(ManagerMessage::Connected(deck)).await?;
Ok(())
})
}
}

94
src/deck.rs Normal file
View file

@ -0,0 +1,94 @@
use crate::{DeckMessage, ManagerMessage};
use std::{
path::{Path, PathBuf},
sync::Arc,
};
use tokio::sync::RwLock;
use tokio_actors::{BoxFuture, Context, SendHandle};
use zbus::Connection;
#[derive(Clone, Debug)]
struct Id(Vec<u8>);
#[derive(Clone, Debug)]
struct Inner {
name: String,
}
#[derive(Clone, Debug)]
pub(crate) struct Deck {
connection: Connection,
id: Id,
path: PathBuf,
parent: SendHandle<ManagerMessage>,
inner: Arc<RwLock<Inner>>,
}
#[zbus::dbus_interface(name = "dog.asonix.DeckManager.Deck1")]
impl Deck {
#[dbus_interface(property)]
fn id(&self) -> Vec<u8> {
self.id.0.clone()
}
#[dbus_interface(property)]
async fn name(&self) -> String {
self.inner.read().await.name.clone()
}
#[dbus_interface(property)]
async fn set_name(&mut self, name: String) {
self.inner.write().await.name = name;
}
}
impl Deck {
pub(crate) async fn build(
connection: Connection,
name: String,
id: Vec<u8>,
path: PathBuf,
parent: SendHandle<ManagerMessage>,
) -> anyhow::Result<Self> {
let deck = Deck {
connection: connection.clone(),
id: Id(id),
path,
parent,
inner: Arc::new(RwLock::new(Inner { name })),
};
connection
.object_server_mut()
.await
.at(deck.object_name(), deck.clone())?;
println!("new deck");
Ok(deck)
}
pub(crate) fn object_name(&self) -> String {
format!("/dog/asonix/DeckManager/{}", self.id)
}
pub(crate) fn turn<'a>(
&'a mut self,
msg: DeckMessage,
_: &'a mut Context<DeckMessage>,
) -> BoxFuture<'a> {
match msg {}
}
pub(crate) fn path(&self) -> &Path {
&self.path
}
}
impl std::fmt::Display for Id {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for byte in &self.0 {
write!(f, "{:02X}", byte)?;
}
Ok(())
}
}

37
src/main.rs Normal file
View file

@ -0,0 +1,37 @@
use std::{path::PathBuf, time::Duration};
mod connector;
mod deck;
mod manager;
mod searcher;
use deck::Deck;
use manager::Manager;
use searcher::Searcher;
#[derive(Clone, Debug)]
enum DeckMessage {}
#[derive(Clone, Debug)]
enum ManagerMessage {
Found(PathBuf),
Connected(Deck),
Ignored(PathBuf),
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut root = tokio_actors::root();
let manager = Manager::build().await?;
let manager = root.spawn_child(manager, Manager::turn).await?;
let searcher = Searcher::new(manager);
let searcher = root.spawn_child(searcher, Searcher::turn).await?;
searcher.every(Duration::from_secs(1), || ());
tokio::signal::ctrl_c().await?;
root.close().await;
Ok(())
}

135
src/manager.rs Normal file
View file

@ -0,0 +1,135 @@
use crate::{connector::Connector, deck::Deck, ManagerMessage};
use std::{
collections::{HashMap, HashSet},
path::PathBuf,
sync::Arc,
};
use tokio::sync::RwLock;
use tokio_actors::{BoxFuture, Context};
use zbus::SignalContext;
#[derive(Debug, Default)]
struct Inner {
pending_ports: HashSet<PathBuf>,
connected_decks: HashMap<PathBuf, String>,
ignored_ports: HashSet<PathBuf>,
}
#[derive(Clone, Debug)]
pub(crate) struct Manager {
connection: zbus::Connection,
inner: Arc<RwLock<Inner>>,
}
#[zbus::dbus_interface(name = "dog.asonix.DeckManager1")]
impl Manager {
#[dbus_interface(property, name = "ConnectedDecks")]
async fn connected_decks(&self) -> Vec<String> {
let mut vec: Vec<String> = self
.inner
.read()
.await
.connected_decks
.values()
.cloned()
.collect();
vec.sort();
vec
}
}
impl Manager {
pub(crate) async fn build() -> anyhow::Result<Self> {
let connection = zbus::Connection::session().await?;
connection.request_name("dog.asonix.DeckManager").await?;
let manager = Manager {
connection: connection.clone(),
inner: Arc::new(RwLock::new(Inner::default())),
};
connection
.object_server_mut()
.await
.at(manager.object_name(), manager.clone())?;
println!("new manager");
Ok(manager)
}
pub(crate) fn turn<'a>(
&'a mut self,
msg: ManagerMessage,
ctx: &'a mut Context<ManagerMessage>,
) -> BoxFuture<'a> {
match msg {
ManagerMessage::Found(path) => Box::pin(self.found(path, ctx)),
ManagerMessage::Connected(deck) => Box::pin(self.connected(deck, ctx)),
ManagerMessage::Ignored(path) => Box::pin(self.ignored(path, ctx)),
}
}
fn object_name(&self) -> String {
"/dog/asonix/DeckManager".to_owned()
}
async fn ignored(
&mut self,
path: PathBuf,
_: &'_ mut Context<ManagerMessage>,
) -> anyhow::Result<()> {
println!("ignored");
self.inner.write().await.ignored_ports.insert(path);
Ok(())
}
async fn connected(
&mut self,
deck: Deck,
ctx: &'_ mut Context<ManagerMessage>,
) -> anyhow::Result<()> {
println!("connected");
self.inner
.write()
.await
.connected_decks
.insert(deck.path().to_owned(), deck.object_name());
ctx.spawn_child(deck, Deck::turn);
// auto generated
let signal = SignalContext::new(&self.connection, self.object_name())?;
self.connected_decks_changed(&signal).await?;
Ok(())
}
async fn found(
&mut self,
path: PathBuf,
ctx: &'_ mut Context<ManagerMessage>,
) -> anyhow::Result<()> {
println!("found");
let inner = self.inner.read().await;
for set in [&inner.pending_ports, &inner.ignored_ports] {
if set.contains(&path) {
return Ok(());
}
}
if inner.connected_decks.contains_key(&path) {
return Ok(());
}
drop(inner);
let connector = Connector::new(self.connection.clone(), ctx.handle());
let mut handle = ctx.spawn_child(connector, Connector::turn);
if let Err(e) = handle.send(path.clone()).await {
handle.stop();
return Err(e.into());
}
self.inner.write().await.pending_ports.insert(path);
Ok(())
}
}

28
src/searcher.rs Normal file
View file

@ -0,0 +1,28 @@
use crate::ManagerMessage;
use std::path::PathBuf;
use tokio_actors::{BoxFuture, Context, SendHandle};
#[derive(Clone, Debug)]
pub(crate) struct Searcher {
manager: SendHandle<ManagerMessage>,
}
impl Searcher {
pub(crate) fn new(manager: SendHandle<ManagerMessage>) -> Self {
println!("new searcher");
Searcher { manager }
}
pub(crate) fn turn<'a>(&'a mut self, _: (), _: &'a mut Context<()>) -> BoxFuture<'a> {
Box::pin(self.search())
}
async fn search(&mut self) -> anyhow::Result<()> {
println!("Search");
// TODO: do searching
self.manager
.send(ManagerMessage::Found(PathBuf::new()))
.await?;
Ok(())
}
}