It's a start
This commit is contained in:
commit
94539a08c4
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
1077
Cargo.lock
generated
Normal file
1077
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
14
Cargo.toml
Normal file
14
Cargo.toml
Normal 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
45
src/connector.rs
Normal 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
94
src/deck.rs
Normal 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
37
src/main.rs
Normal 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
135
src/manager.rs
Normal 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
28
src/searcher.rs
Normal 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(())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue