mirror of
https://github.com/nekename/OpenDeck.git
synced 2026-10-28 18:02:23 +00:00
Register plugins and use Tokio for async
This commit is contained in:
1 parent
4e7be5f1d9
commit
913951a096
13 files changed
+902
-165
No files matched your search
Generated
+241
-10
@@ -38,6 +38,12 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
@@ -83,6 +89,12 @@ version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "ascii"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.5.1"
|
||||
@@ -379,6 +391,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "buf_redux"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"safemem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
@@ -504,6 +526,12 @@ dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chunked_transfer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901"
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.24.1"
|
||||
@@ -724,6 +752,22 @@ dependencies = [
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"gzip-header",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.9"
|
||||
@@ -977,9 +1021,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
@@ -1387,6 +1431,15 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gzip-header"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.21"
|
||||
@@ -1591,9 +1644,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
@@ -2035,6 +2088,16 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
@@ -2056,6 +2119,24 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multipart"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182"
|
||||
dependencies = [
|
||||
"buf_redux",
|
||||
"httparse",
|
||||
"log",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"quick-error",
|
||||
"rand 0.8.5",
|
||||
"safemem",
|
||||
"tempfile",
|
||||
"twoway",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
@@ -2210,6 +2291,15 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
@@ -2278,11 +2368,19 @@ dependencies = [
|
||||
name = "opendeck"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
"lazy_static",
|
||||
"os_info",
|
||||
"rouille",
|
||||
"serde",
|
||||
"serde-inline-default",
|
||||
"serde_json",
|
||||
"serialport",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2428,9 +2526,9 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
@@ -2693,6 +2791,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.30.0"
|
||||
@@ -2944,6 +3048,30 @@ dependencies = [
|
||||
"windows 0.37.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rouille"
|
||||
version = "3.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"brotli",
|
||||
"chrono",
|
||||
"deflate",
|
||||
"filetime",
|
||||
"multipart",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha1_smol",
|
||||
"threadpool",
|
||||
"time",
|
||||
"tiny_http",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
@@ -3095,6 +3223,17 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-inline-default"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa824cde50b5f01ff28a955114d8152a07cd62d81f53459dad0f2610136be844"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.190"
|
||||
@@ -3239,6 +3378,12 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
@@ -3830,6 +3975,15 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "threadpool"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
|
||||
dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.30"
|
||||
@@ -3838,6 +3992,8 @@ checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa 1.0.9",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
@@ -3859,6 +4015,18 @@ dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny_http"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"chunked_transfer",
|
||||
"httpdate",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
@@ -3876,20 +4044,34 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.33.0"
|
||||
version = "1.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
|
||||
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2 0.5.5",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
@@ -3900,6 +4082,18 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"tokio",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.10"
|
||||
@@ -4064,6 +4258,34 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twoway"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
@@ -4080,6 +4302,15 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.13"
|
||||
@@ -4109,9 +4340,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
|
||||
+12
-2
@@ -13,10 +13,20 @@ rust-version = "1.60"
|
||||
tauri-build = { version = "1.5.0", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
# Large, fundamental libraries
|
||||
tauri = { version = "1.5.2", features = [ "system-tray", "api-all" ] }
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
serde_json = "1.0"
|
||||
serialport = "4.2.2"
|
||||
tokio = { version = "1.34.0", features = [ "full" ] }
|
||||
tokio-tungstenite = "0.20.1"
|
||||
rouille = "3.6.2"
|
||||
# Smaller utility libraries
|
||||
futures-util = "0.3.29"
|
||||
anyhow = "1.0.75"
|
||||
serde-inline-default = "0.1.1"
|
||||
lazy_static = "1.4.0"
|
||||
os_info = "3.7.0"
|
||||
|
||||
[features]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
@@ -1,146 +0,0 @@
|
||||
use std::thread;
|
||||
|
||||
use std::time::Duration;
|
||||
use serde_json::Value;
|
||||
|
||||
/// Implemented by all supported devices.
|
||||
trait BaseDevice {
|
||||
fn num_keys(&self) -> u8;
|
||||
fn num_dials(&self) -> u8;
|
||||
|
||||
fn key_down(&self, key: u8);
|
||||
fn key_up(&self, key: u8);
|
||||
fn dial_rotate(&self, dial: u8, ticks: i16);
|
||||
}
|
||||
|
||||
/// A representation of a ProntoKey device.
|
||||
struct ProntoKeyDevice {
|
||||
address: String
|
||||
}
|
||||
impl BaseDevice for ProntoKeyDevice {
|
||||
fn num_keys(&self) -> u8 { 9 }
|
||||
fn num_dials(&self) -> u8 { 2 }
|
||||
|
||||
fn key_down(&self, key: u8) {
|
||||
println!("{} down {}", &self.address, key);
|
||||
}
|
||||
fn key_up(&self, key: u8) {
|
||||
println!("{} up {}", &self.address, key);
|
||||
}
|
||||
fn dial_rotate(&self, dial: u8, ticks: i16) {
|
||||
println!("{} rotate {} by {}", &self.address, dial, ticks);
|
||||
}
|
||||
}
|
||||
impl ProntoKeyDevice {
|
||||
/// Attempt to open a serial connection with the device and handle incoming data.
|
||||
fn init(port: String) {
|
||||
thread::spawn(|| {
|
||||
let mut device: Option<ProntoKeyDevice> = Option::None;
|
||||
let mut last_key: u8 = 0;
|
||||
let mut last_sliders: Vec<i16> = vec![0; 2];
|
||||
|
||||
let mut port = match
|
||||
serialport::new(port, 57600)
|
||||
.timeout(Duration::from_millis(10))
|
||||
.open()
|
||||
{
|
||||
Ok(p) => p,
|
||||
Err(e) => panic!("Failed to open serial port: {}", e.description)
|
||||
};
|
||||
let _ = port.write("register".as_bytes());
|
||||
|
||||
let mut serial_buf: Vec<u8> = vec![0; 1024];
|
||||
let mut holding_string: String = "".to_owned();
|
||||
|
||||
loop {
|
||||
match port.read(serial_buf.as_mut_slice()) {
|
||||
Ok(t) => {
|
||||
match std::str::from_utf8(&serial_buf[..t]) {
|
||||
Ok(data) => holding_string += data,
|
||||
Err(_) => break
|
||||
}
|
||||
// Iterate through JSON objects received from device that are being held in the buffer.
|
||||
while holding_string.contains("}") {
|
||||
let index = holding_string.find("}").unwrap_or_default();
|
||||
let chunk = holding_string[..=index].trim();
|
||||
let j: Value = match serde_json::from_str(chunk) {
|
||||
Ok(data) => data,
|
||||
Err(_) => continue
|
||||
};
|
||||
holding_string = holding_string[(index + 1)..].to_owned();
|
||||
|
||||
// If the device is uninitialised, attempt to read its MAC address and initialise.
|
||||
if device.is_none() {
|
||||
match &j["address"] {
|
||||
Value::String(address) => {
|
||||
device = Some(ProntoKeyDevice { address: address.clone() });
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let device = device.as_ref().unwrap();
|
||||
|
||||
// Handle key presses and releases.
|
||||
match &j["key"] {
|
||||
Value::Number(num) => {
|
||||
match num.as_u64().unwrap_or_default() as u8 {
|
||||
0 => device.key_up(last_key),
|
||||
val => {
|
||||
device.key_down(val);
|
||||
last_key = val;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Handle slider value changes.
|
||||
match &j["slider0"] {
|
||||
Value::Number(val) => {
|
||||
let val: i16 = match val.as_i64() {
|
||||
Some(v) => v as i16,
|
||||
_ => last_sliders[0]
|
||||
};
|
||||
device.dial_rotate(0, val - last_sliders[0]);
|
||||
last_sliders[0] = val;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
match &j["slider1"] {
|
||||
Value::Number(val) => {
|
||||
let val: i16 = match val.as_i64() {
|
||||
Some(v) => v as i16,
|
||||
_ => last_sliders[1]
|
||||
};
|
||||
device.dial_rotate(1, val - last_sliders[1]);
|
||||
last_sliders[1] = val;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => (),
|
||||
Err(e) => eprintln!("{:?}", e)
|
||||
}
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to initialise all connected devices.
|
||||
pub fn initialise_devices() {
|
||||
// Iterate through available serial ports and attempt to register them as ProntoKey devices.
|
||||
for port in serialport::available_ports().unwrap_or_default() {
|
||||
match port.port_type {
|
||||
serialport::SerialPortType::UsbPort(info) => {
|
||||
if info.vid == 0x10c4 && info.pid == 0xea60 {
|
||||
ProntoKeyDevice::init(port.port_name);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
mod prontokey;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
/// A trait implemented by all supported devices.
|
||||
pub trait BaseDevice {
|
||||
fn num_dials(&self) -> u8;
|
||||
fn num_rows(&self) -> u8;
|
||||
fn num_columns(&self) -> u8;
|
||||
fn num_keys(&self) -> u8 {
|
||||
self.num_rows() * self.num_columns()
|
||||
}
|
||||
|
||||
fn id(&self) -> String;
|
||||
fn name(&self) -> String;
|
||||
fn r#type(&self) -> u8;
|
||||
|
||||
fn key_down(&self, key: u8);
|
||||
fn key_up(&self, key: u8);
|
||||
fn dial_rotate(&self, dial: u8, ticks: i16);
|
||||
}
|
||||
|
||||
|
||||
/// The dimensions of a device.
|
||||
#[derive(Clone, Serialize)]
|
||||
pub struct DeviceSize {
|
||||
pub rows: u8,
|
||||
pub columns: u8
|
||||
}
|
||||
|
||||
/// Metadata of a device.
|
||||
#[derive(Clone, Serialize)]
|
||||
pub struct DeviceInfo {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub size: DeviceSize,
|
||||
pub r#type: u8
|
||||
}
|
||||
|
||||
impl DeviceInfo {
|
||||
fn new(device: &impl BaseDevice) -> DeviceInfo {
|
||||
DeviceInfo {
|
||||
id: device.id(),
|
||||
name: device.name(),
|
||||
size: DeviceSize {
|
||||
rows: device.num_rows(),
|
||||
columns: device.num_columns()
|
||||
},
|
||||
r#type: device.r#type()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DEVICES: Mutex<Vec<DeviceInfo>> = Mutex::new(vec![]);
|
||||
}
|
||||
|
||||
/// Attempt to initialise all connected devices.
|
||||
pub fn initialise_devices() {
|
||||
// Iterate through available serial ports and attempt to register them as ProntoKey devices.
|
||||
for port in serialport::available_ports().unwrap_or_default() {
|
||||
match port.port_type {
|
||||
serialport::SerialPortType::UsbPort(info) => {
|
||||
if info.vid == 0x10c4 && info.pid == 0xea60 {
|
||||
tokio::spawn(prontokey::ProntoKeyDevice::init(port.port_name));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
use super::BaseDevice;
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
/// A representation of a ProntoKey device.
|
||||
pub struct ProntoKeyDevice {
|
||||
address: String
|
||||
}
|
||||
|
||||
impl BaseDevice for ProntoKeyDevice {
|
||||
fn num_dials(&self) -> u8 { 2 }
|
||||
fn num_rows(&self) -> u8 { 3 }
|
||||
fn num_columns(&self) -> u8 { 3 }
|
||||
|
||||
fn id(&self) -> String {
|
||||
format!("pk-{}", self.address)
|
||||
}
|
||||
fn name(&self) -> String {
|
||||
String::from("ProntoKey")
|
||||
}
|
||||
fn r#type(&self) -> u8 { 7 }
|
||||
|
||||
fn key_down(&self, key: u8) {
|
||||
println!("{} down {}", &self.address, key);
|
||||
}
|
||||
fn key_up(&self, key: u8) {
|
||||
println!("{} up {}", &self.address, key);
|
||||
}
|
||||
fn dial_rotate(&self, dial: u8, ticks: i16) {
|
||||
println!("{} rotate {} by {}", &self.address, dial, ticks);
|
||||
}
|
||||
}
|
||||
|
||||
impl ProntoKeyDevice {
|
||||
/// Attempt to open a serial connection with the device and handle incoming data.
|
||||
pub async fn init(port: String) {
|
||||
let mut device: Option<ProntoKeyDevice> = Option::None;
|
||||
let mut last_key: u8 = 0;
|
||||
let mut last_sliders: Vec<i16> = vec![0; 2];
|
||||
|
||||
let mut port = match
|
||||
serialport::new(port, 57600)
|
||||
.timeout(Duration::from_millis(10))
|
||||
.open()
|
||||
{
|
||||
Ok(p) => p,
|
||||
Err(e) => panic!("Failed to open serial port: {}", e.description)
|
||||
};
|
||||
let _ = port.write("register".as_bytes());
|
||||
|
||||
let mut serial_buf: Vec<u8> = vec![0; 1024];
|
||||
let mut holding_string = String::from("");
|
||||
|
||||
loop {
|
||||
match port.read(serial_buf.as_mut_slice()) {
|
||||
Ok(t) => {
|
||||
match std::str::from_utf8(&serial_buf[..t]) {
|
||||
Ok(data) => holding_string += data,
|
||||
Err(_) => break
|
||||
}
|
||||
// Iterate through JSON objects received from device that are being held in the buffer.
|
||||
while holding_string.contains("}") {
|
||||
let index = holding_string.find("}").unwrap_or_default();
|
||||
let chunk = holding_string[..=index].trim();
|
||||
let j: Value = match serde_json::from_str(chunk) {
|
||||
Ok(data) => data,
|
||||
Err(_) => continue
|
||||
};
|
||||
holding_string = holding_string[(index + 1)..].to_owned();
|
||||
|
||||
// If the device is uninitialised, attempt to read its MAC address and initialise.
|
||||
if device.is_none() {
|
||||
match &j["address"] {
|
||||
Value::String(address) => {
|
||||
device = Some(ProntoKeyDevice { address: address.clone() });
|
||||
match &device {
|
||||
Some(device) => super::DEVICES.lock().unwrap().push(super::DeviceInfo::new(device)),
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let device = device.as_ref().unwrap();
|
||||
|
||||
// Handle key presses and releases.
|
||||
match &j["key"] {
|
||||
Value::Number(num) => {
|
||||
match num.as_u64().unwrap_or_default() as u8 {
|
||||
0 => device.key_up(last_key),
|
||||
val => {
|
||||
device.key_down(val);
|
||||
last_key = val;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Handle slider value changes.
|
||||
match &j["slider0"] {
|
||||
Value::Number(val) => {
|
||||
let val: i16 = match val.as_i64() {
|
||||
Some(v) => v as i16,
|
||||
_ => last_sliders[0]
|
||||
};
|
||||
device.dial_rotate(0, val - last_sliders[0]);
|
||||
last_sliders[0] = val;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
match &j["slider1"] {
|
||||
Value::Number(val) => {
|
||||
let val: i16 = match val.as_i64() {
|
||||
Some(v) => v as i16,
|
||||
_ => last_sliders[1]
|
||||
};
|
||||
device.dial_rotate(1, val - last_sliders[1]);
|
||||
last_sliders[1] = val;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => (),
|
||||
Err(e) => eprintln!("{:?}", e)
|
||||
}
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RegisterEvent {
|
||||
pub uuid: String
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
mod inbound;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::WebSocketStream;
|
||||
use futures_util::StreamExt;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
static ref SOCKETS: Mutex<HashMap<String, WebSocketStream<TcpStream>>> = Mutex::new(HashMap::new());
|
||||
}
|
||||
|
||||
/// Register a plugin to send and receive events with its WebSocket.
|
||||
pub async fn register_plugin(data: inbound::RegisterEvent, stream: WebSocketStream<TcpStream>) {
|
||||
SOCKETS.lock().await.insert(data.uuid.clone(), stream);
|
||||
tokio::spawn(handle_stream(data.uuid));
|
||||
}
|
||||
|
||||
async fn handle_stream(uuid: String) {
|
||||
while let Some(data) = SOCKETS.lock().await.get_mut(&uuid).unwrap().next().await {
|
||||
println!("{}", data.unwrap());
|
||||
}
|
||||
}
|
||||
+13
-7
@@ -1,13 +1,19 @@
|
||||
// Prevents additional console window on Windows in release
|
||||
// Prevents additional console window on Windows in release.
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
mod shared;
|
||||
mod events;
|
||||
mod plugins;
|
||||
mod devices;
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let app = match tauri::Builder::default().build(tauri::generate_context!()) {
|
||||
Ok(app) => app,
|
||||
Err(e) => panic!("Failed to create Tauri application: {}", e.to_string())
|
||||
};
|
||||
devices::initialise_devices();
|
||||
|
||||
match tauri::Builder::default().run(tauri::generate_context!()) {
|
||||
Err(e) => panic!("Error while running Tauri application: {}", e.to_string()),
|
||||
_ => {}
|
||||
}
|
||||
plugins::initialise_plugins(app.handle());
|
||||
|
||||
app.run(|_, _| {});
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
use serde::Serialize;
|
||||
|
||||
// Structs that make up the Info parameter passed to plugins during the registration procedure.
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Serialize)]
|
||||
pub(super) struct ApplicationInfo {
|
||||
pub font: String,
|
||||
pub language: String,
|
||||
pub platform: String,
|
||||
pub platformVersion: String,
|
||||
pub version: String
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(super) struct PluginInfo {
|
||||
pub uuid: String,
|
||||
pub version: String
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Serialize)]
|
||||
pub(super) struct ColoursInfo {
|
||||
pub buttonPressedBackgroundColor: String,
|
||||
pub buttonPressedBorderColor: String,
|
||||
pub buttonPressedTextColor: String,
|
||||
pub disabledColor: String,
|
||||
pub highlightColor: String,
|
||||
pub mouseDownColor: String
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Serialize)]
|
||||
pub(super) struct Info {
|
||||
pub application: ApplicationInfo,
|
||||
pub plugin: PluginInfo,
|
||||
pub devicePixelRatio: u8,
|
||||
pub colors: ColoursInfo,
|
||||
pub devices: Vec<crate::devices::DeviceInfo>
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
use crate::shared::Action;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub(super) struct OS {
|
||||
#[serde(alias = "Platform")]
|
||||
pub platform: String
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize)]
|
||||
pub(super) struct PluginManifest {
|
||||
#[serde(alias = "Version")]
|
||||
pub version: String,
|
||||
|
||||
#[serde(alias = "Actions")]
|
||||
pub actions: Vec<Action>,
|
||||
|
||||
#[serde(alias = "OS")]
|
||||
pub os: Vec<OS>,
|
||||
|
||||
#[serde(alias = "CodePath")]
|
||||
pub code_path: Option<String>,
|
||||
|
||||
#[serde(alias = "CodePathWin")]
|
||||
pub code_path_windows: Option<String>,
|
||||
|
||||
#[serde(alias = "CodePathMac")]
|
||||
pub code_path_macos: Option<String>,
|
||||
|
||||
#[serde(alias = "CodePathLin")]
|
||||
pub code_path_linux: Option<String>
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
mod manifest;
|
||||
mod info_param;
|
||||
|
||||
use std::{fs, path};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use tauri::AppHandle;
|
||||
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use futures_util::StreamExt;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
|
||||
/// Initialise a plugin from a given directory.
|
||||
fn initialise_plugin(path: path::PathBuf, app: &AppHandle) -> anyhow::Result<()> {
|
||||
let plugin_uuid = path.file_name().unwrap().to_str().unwrap();
|
||||
let mut manifest_path = path.clone();
|
||||
manifest_path.push("manifest.json");
|
||||
|
||||
let manifest =
|
||||
fs::read(&manifest_path)
|
||||
.with_context(|| { format!("Failed to read manifest of plugin at {}", manifest_path.display()) })?;
|
||||
let mut manifest: manifest::PluginManifest =
|
||||
serde_json::from_slice(&manifest)
|
||||
.with_context(|| { format!("Failed to parse manifest of plugin at {}", manifest_path.display()) })?;
|
||||
|
||||
for action in &mut manifest.actions {
|
||||
action.plugin = plugin_uuid.to_string();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let platform = "windows";
|
||||
#[cfg(target_os = "macos")]
|
||||
let platform = "mac";
|
||||
#[cfg(target_os = "linux")]
|
||||
let platform = "linux";
|
||||
|
||||
let mut code_path = manifest.code_path;
|
||||
let mut use_browser = false;
|
||||
let mut use_wine = false;
|
||||
let mut supported = false;
|
||||
|
||||
// Determine the method used to run the plugin based on its supported operating systems and the current operating system.
|
||||
for os in manifest.os {
|
||||
if os.platform == platform {
|
||||
#[cfg(target_os = "windows")]
|
||||
if let Some(_) = &manifest.code_path_windows { code_path = manifest.code_path_windows; }
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Some(_) = &manifest.code_path_macos { code_path = manifest.code_path_macos; }
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Some(_) = &manifest.code_path_linux { code_path = manifest.code_path_linux; }
|
||||
|
||||
if let Some(code_path) = &code_path {
|
||||
use_browser = code_path.ends_with(".html");
|
||||
}
|
||||
use_wine = false;
|
||||
|
||||
supported = true;
|
||||
break;
|
||||
} else if os.platform == "windows" {
|
||||
use_wine = true;
|
||||
supported = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !supported || code_path.is_none() {
|
||||
return Err(anyhow!("Failed to load plugin with ID {}: unsupported on platform {}", plugin_uuid, platform));
|
||||
}
|
||||
|
||||
let info = info_param::Info {
|
||||
application: info_param::ApplicationInfo {
|
||||
font: String::from("Rubik"),
|
||||
language: String::from("en"),
|
||||
platform: platform.to_owned(),
|
||||
platformVersion: os_info::get().version().to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_owned()
|
||||
},
|
||||
plugin: info_param::PluginInfo {
|
||||
uuid: plugin_uuid.to_string(),
|
||||
version: manifest.version
|
||||
},
|
||||
devicePixelRatio: 0,
|
||||
colors: info_param::ColoursInfo {
|
||||
buttonPressedBackgroundColor: String::from("#303030FF"),
|
||||
buttonPressedBorderColor: String::from("#646464FF"),
|
||||
buttonPressedTextColor: String::from("#969696FF"),
|
||||
disabledColor: String::from("#F7821B59"),
|
||||
highlightColor: String::from("#F7821BFF"),
|
||||
mouseDownColor: String::from("#CF6304FF")
|
||||
},
|
||||
devices: crate::devices::DEVICES.lock().unwrap().to_vec()
|
||||
};
|
||||
|
||||
let code_path = code_path.unwrap();
|
||||
|
||||
if use_browser {
|
||||
// Create a webview window for the plugin and call its registration function.
|
||||
let url = String::from("http://localhost:57118/") + &path.join(code_path).to_str().unwrap();
|
||||
let window = tauri::WindowBuilder::new(
|
||||
app,
|
||||
plugin_uuid.replace(".", "_"),
|
||||
tauri::WindowUrl::External(url.parse().unwrap())
|
||||
)
|
||||
.visible(false)
|
||||
.build()
|
||||
.with_context(|| { format!("Failed to initialise plugin with ID {}", plugin_uuid) })?;
|
||||
|
||||
window.eval(&format!(
|
||||
"let a = setInterval(() => {{
|
||||
try {{
|
||||
connectElgatoStreamDeckSocket({}, \"{}\", \"{}\", {});
|
||||
clearInterval(a);
|
||||
}} catch {{}}
|
||||
}}, 10);",
|
||||
57116, plugin_uuid, "register", serde_json::to_string(&info).unwrap()
|
||||
))?;
|
||||
} else if use_wine {
|
||||
// Start Wine with the appropriate arguments.
|
||||
Command::new("wine")
|
||||
.current_dir(&path)
|
||||
.args([
|
||||
code_path,
|
||||
String::from("-port"), 57116.to_string(),
|
||||
String::from("-pluginUUID"), plugin_uuid.to_string(),
|
||||
String::from("-registerEvent"), String::from("register"),
|
||||
String::from("-info"), serde_json::to_string(&info).unwrap()
|
||||
])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.with_context(|| { format!("Failed to initialise plugin with ID {}", plugin_uuid) })?;
|
||||
} else {
|
||||
// Run the plugin's executable natively.
|
||||
Command::new(code_path)
|
||||
.current_dir(&path)
|
||||
.args([
|
||||
String::from("-port"), 57116.to_string(),
|
||||
String::from("-pluginUUID"), plugin_uuid.to_string(),
|
||||
String::from("-registerEvent"), String::from("register"),
|
||||
String::from("-info"), serde_json::to_string(&info).unwrap()
|
||||
])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.with_context(|| { format!("Failed to initialise plugin with ID {}", path.file_name().unwrap().to_str().unwrap()) })?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialise plugins from the plugins directory.
|
||||
pub fn initialise_plugins(app: AppHandle) {
|
||||
tokio::spawn(init_websocket_server());
|
||||
tokio::spawn(init_browser_server());
|
||||
|
||||
let mut plugin_dir = app.path_resolver().app_config_dir().unwrap();
|
||||
plugin_dir.push("plugins/");
|
||||
let _ = fs::create_dir_all(&plugin_dir);
|
||||
|
||||
let entries = match fs::read_dir(&plugin_dir) {
|
||||
Ok(p) => p,
|
||||
Err(e) => panic!("Failed to read plugins directory at {}: {}", plugin_dir.display(), e.to_string())
|
||||
};
|
||||
|
||||
// Iterate through all directory entries in the plugins folder and initialise them as plugins if appropriate
|
||||
for entry in entries {
|
||||
if let Ok(entry) = entry {
|
||||
if entry.metadata().unwrap().is_dir() {
|
||||
if let Err(e) = initialise_plugin(entry.path(), &app) {
|
||||
eprintln!("{}\n\tCaused by: {}", e.to_string(), e.root_cause());
|
||||
}
|
||||
} else {
|
||||
eprintln!("Failed to initialise plugin at {}: is a file", entry.path().display());
|
||||
}
|
||||
} else if let Err(e) = entry {
|
||||
eprintln!("Failed to read plugin directory: {}", e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Start the WebSocket server that plugins communicate with.
|
||||
async fn init_websocket_server() {
|
||||
let listener = match TcpListener::bind("localhost:57116").await {
|
||||
Ok(listener) => listener,
|
||||
Err(error) => {
|
||||
eprintln!("Failed to bind to socket: {}", error.to_string());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
while let Ok((stream, _)) = listener.accept().await {
|
||||
accept_connection(stream).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle incoming data from a WebSocket connection.
|
||||
async fn accept_connection(stream: TcpStream) {
|
||||
let mut socket = match tokio_tungstenite::accept_async(stream).await {
|
||||
Ok(socket) => socket,
|
||||
Err(error) => {
|
||||
eprintln!("Failed to complete WebSocket handshake: {}", error.to_string());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
while let Some(data) = socket.next().await {
|
||||
crate::events::register_plugin(serde_json::from_str(&data.unwrap().into_text().unwrap()).unwrap(), socket).await;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a simple webserver to serve files of plugins that run in a browser environment.
|
||||
async fn init_browser_server() {
|
||||
rouille::start_server("localhost:57118", move |request| {
|
||||
rouille::Response::html(std::fs::read_to_string(request.url()).unwrap())
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde_inline_default::serde_inline_default;
|
||||
|
||||
/// A state of an action.
|
||||
#[serde_inline_default]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ActionState {
|
||||
#[serde(alias = "Image")]
|
||||
pub image: String,
|
||||
|
||||
#[serde_inline_default(String::from(""))]
|
||||
#[serde(alias = "Name")]
|
||||
pub name: String,
|
||||
|
||||
#[serde_inline_default(String::from(""))]
|
||||
#[serde(alias = "Title")]
|
||||
pub text: String,
|
||||
|
||||
#[serde_inline_default(true)]
|
||||
#[serde(alias = "ShowTitle")]
|
||||
pub show: bool,
|
||||
|
||||
#[serde_inline_default(String::from("#f2f2f2"))]
|
||||
#[serde(alias = "TitleColor")]
|
||||
pub colour: String,
|
||||
|
||||
#[serde_inline_default(String::from("middle"))]
|
||||
#[serde(alias = "TitleAlignment")]
|
||||
pub alignment: String,
|
||||
|
||||
#[serde_inline_default(String::from("Regular"))]
|
||||
#[serde(alias = "FontStyle")]
|
||||
pub style: String,
|
||||
|
||||
#[serde_inline_default(String::from("16"))]
|
||||
#[serde(alias = "FontSize")]
|
||||
pub size: String,
|
||||
|
||||
#[serde_inline_default(false)]
|
||||
#[serde(alias = "FontUnderline")]
|
||||
pub underline: bool
|
||||
}
|
||||
|
||||
/// An action, deserialised from the plugin manifest.
|
||||
#[serde_inline_default]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Action {
|
||||
#[serde(alias = "Name")]
|
||||
pub name: String,
|
||||
|
||||
#[serde(alias = "UUID")]
|
||||
pub uuid: String,
|
||||
|
||||
#[serde_inline_default(String::from(""))]
|
||||
pub plugin: String,
|
||||
|
||||
#[serde(alias = "Tooltip")]
|
||||
pub tooltip: String,
|
||||
|
||||
#[serde(alias = "Icon")]
|
||||
pub icon: String,
|
||||
|
||||
#[serde_inline_default(true)]
|
||||
#[serde(alias = "VisibleInActionsList")]
|
||||
pub visible_in_action_list: bool,
|
||||
|
||||
#[serde_inline_default(String::from(""))]
|
||||
#[serde(alias = "PropertyInspectorPath")]
|
||||
pub property_inspector: String,
|
||||
|
||||
#[serde_inline_default(vec![String::from("Keypad")])]
|
||||
#[serde(alias = "Controllers")]
|
||||
pub controllers: Vec<String>,
|
||||
|
||||
#[serde(alias = "States")]
|
||||
pub states: Vec<ActionState>
|
||||
}
|
||||
|
||||
/// Information about the slot an instance is located in.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ActionContext {
|
||||
pub device: String,
|
||||
pub profile: String,
|
||||
pub controller: String,
|
||||
pub position: u8,
|
||||
pub index: u16
|
||||
}
|
||||
|
||||
impl ToString for ActionContext {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}.{}.{}.{}.{}", self.device, self.profile, self.controller, self.position, self.index)
|
||||
}
|
||||
}
|
||||
|
||||
/// An instance of an action.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ActionInstance {
|
||||
pub action: Action,
|
||||
pub context: ActionContext,
|
||||
pub states: Vec<ActionState>,
|
||||
pub current_state: u16,
|
||||
pub settings: String
|
||||
}
|
||||
File renamed without changes.
Reference in new issue
Block a user