ooo it really do be workin
This commit is contained in:
parent
00de0a88b5
commit
6c628552bb
779
Cargo.lock
generated
779
Cargo.lock
generated
|
@ -53,12 +53,33 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.0.1"
|
||||
|
@ -83,6 +104,140 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpuid-bool"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbus"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f597e08dfa79b593f23bbfc7840b23b2c5aa2e3a98d8e68b67b5b9ff800dc0db"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"libc",
|
||||
"libdbus-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbus-crossroads"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a816e8ae3382c7b1bccfa6f2778346ee5b13f80e0eccf80cf8f2912af73995a"
|
||||
dependencies = [
|
||||
"dbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbus-tokio"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b4083ad3ad374032aaacf18c4cce2c65c3ba5d4e576a037339a8b6cd0b4509c"
|
||||
dependencies = [
|
||||
"dbus",
|
||||
"libc",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.3"
|
||||
|
@ -96,6 +251,126 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
|
@ -105,12 +380,55 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1ce40d6fc9764887c2fdc7305c3dcc429ba11ff981c1509416afd5697e4437"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "input_buffer"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.9"
|
||||
|
@ -120,12 +438,33 @@ dependencies = [
|
|||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||
|
||||
[[package]]
|
||||
name = "libdbus-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libudev"
|
||||
version = "0.2.0"
|
||||
|
@ -182,12 +521,27 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.7.11"
|
||||
|
@ -232,6 +586,25 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
|
@ -242,12 +615,39 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "obws"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags",
|
||||
"chrono",
|
||||
"either",
|
||||
"futures-util",
|
||||
"log",
|
||||
"rgb",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
|
@ -273,18 +673,77 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.26"
|
||||
|
@ -303,6 +762,46 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.6"
|
||||
|
@ -329,12 +828,103 @@ version = "0.6.23"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fddb3b23626145d1776addfc307e1a1851f60ef6ca64f376bcb889697144cf0"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||
dependencies = [
|
||||
"pest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b0b98f61935da47683bf5c46b965ce1642ef1db78860b8a1defb68bf1b5b43"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48b35457e9d855d3dc05ef32a73e0df1e2c0fd72c38796a4ee909160c8eeec2"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serialport"
|
||||
version = "4.0.1"
|
||||
|
@ -352,6 +942,32 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if 1.0.0",
|
||||
"cpuid-bool",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if 1.0.0",
|
||||
"cpuid-bool",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.3.0"
|
||||
|
@ -361,6 +977,28 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
||||
|
||||
[[package]]
|
||||
name = "sled"
|
||||
version = "0.34.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d0132f3e393bcb7390c60bb45769498cf4550bcb7a21d7f95c02b69f6362cdc"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"fs2",
|
||||
"fxhash",
|
||||
"libc",
|
||||
"log",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.6.1"
|
||||
|
@ -372,12 +1010,25 @@ name = "streamdeck"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dbus",
|
||||
"dbus-crossroads",
|
||||
"dbus-tokio",
|
||||
"env_logger",
|
||||
"log",
|
||||
"obws",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serialport",
|
||||
"sled",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.69"
|
||||
|
@ -398,6 +1049,41 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.5.0"
|
||||
|
@ -429,18 +1115,111 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e96bb520beab540ab664bd5a9cfeaa1fcd846fa68c830b42e2c8963071251d2"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"pin-project",
|
||||
"tokio",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"http",
|
||||
"httparse",
|
||||
"input_buffer",
|
||||
"log",
|
||||
"rand",
|
||||
"sha-1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
|
||||
dependencies = [
|
||||
"matches",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -6,9 +6,20 @@ edition = "2018"
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["ipc-dbus"]
|
||||
ipc-dbus = ["dbus", "dbus-tokio", "dbus-crossroads"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
log = "0.4.0"
|
||||
env_logger = "0.8.0"
|
||||
obws = { version = "0.7.0", path = "../obws" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serialport = "4.0.1"
|
||||
sled = "0.34.6"
|
||||
tokio = { version = "1.5", features = ["full"] }
|
||||
dbus = { version = "0.9", optional = true }
|
||||
dbus-tokio = { version = "0.7", optional = true }
|
||||
dbus-crossroads = { version = "0.3.0", optional = true }
|
||||
|
|
346
src/dbus.rs
Normal file
346
src/dbus.rs
Normal file
|
@ -0,0 +1,346 @@
|
|||
use crate::{
|
||||
message::{Command, InputMessage, ManagerMessage, ObsMessage, Query},
|
||||
store::Store,
|
||||
};
|
||||
use dbus::{channel::MatchingReceiver, message::MatchRule, nonblock::SyncConnection};
|
||||
use dbus_crossroads::{Crossroads, MethodErr};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc::Sender, Notify};
|
||||
|
||||
pub(crate) struct Dbus {
|
||||
connection: Arc<SyncConnection>,
|
||||
store: Store,
|
||||
input: Sender<InputMessage>,
|
||||
obs: Sender<ObsMessage>,
|
||||
manager: Sender<ManagerMessage>,
|
||||
}
|
||||
|
||||
struct DbusState {
|
||||
store: Store,
|
||||
input: Sender<InputMessage>,
|
||||
obs: Sender<ObsMessage>,
|
||||
manager: Sender<ManagerMessage>,
|
||||
}
|
||||
|
||||
impl Dbus {
|
||||
pub(crate) async fn build(
|
||||
shutdown: Arc<Notify>,
|
||||
store: Store,
|
||||
input: Sender<InputMessage>,
|
||||
obs: Sender<ObsMessage>,
|
||||
manager: Sender<ManagerMessage>,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let (resource, connection) =
|
||||
tokio::task::spawn_blocking(move || dbus_tokio::connection::new_session_sync())
|
||||
.await??;
|
||||
|
||||
tokio::spawn(async move {
|
||||
resource.await;
|
||||
shutdown.notify_one();
|
||||
});
|
||||
|
||||
connection
|
||||
.request_name("dog.asonix.git.asonix.Streamdeck", false, true, false)
|
||||
.await?;
|
||||
|
||||
Ok(Dbus {
|
||||
connection,
|
||||
store,
|
||||
input,
|
||||
obs,
|
||||
manager,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn run(self) {
|
||||
let Dbus {
|
||||
connection,
|
||||
store,
|
||||
input,
|
||||
obs,
|
||||
manager,
|
||||
} = self;
|
||||
|
||||
let state = DbusState {
|
||||
store,
|
||||
input,
|
||||
obs,
|
||||
manager,
|
||||
};
|
||||
|
||||
let mut cr = Crossroads::new();
|
||||
|
||||
cr.set_async_support(Some((
|
||||
connection.clone(),
|
||||
Box::new(|x| {
|
||||
tokio::spawn(x);
|
||||
}),
|
||||
)));
|
||||
|
||||
let iface_token = cr.register("dog.asonix.git.asonix.Streamdeck", |b| {
|
||||
b.method_with_cr_async("GetScenes", (), ("scenes",), |mut ctx, cr, ()| {
|
||||
log::info!("GetScenes");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let obs = state.obs.clone();
|
||||
|
||||
async move {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
if obs
|
||||
.send(ObsMessage::Query(Query::GetScenes(tx)))
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
if let Ok(scenes) = rx.await {
|
||||
return ctx.reply(Ok((scenes,)));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to get scenes")))
|
||||
}
|
||||
});
|
||||
|
||||
b.method_with_cr_async("EnableDiscovery", (), (), |mut ctx, cr, ()| {
|
||||
log::info!("EnableDiscovery");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let manager = state.manager.clone();
|
||||
|
||||
async move {
|
||||
if manager.send(ManagerMessage::EnableDiscovery).await.is_ok() {
|
||||
return ctx.reply(Ok(()));
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to enable discovery")))
|
||||
}
|
||||
});
|
||||
|
||||
b.method_with_cr_async("DisableDiscovery", (), (), |mut ctx, cr, ()| {
|
||||
log::info!("DisableDiscovery");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let manager = state.manager.clone();
|
||||
|
||||
async move {
|
||||
if manager.send(ManagerMessage::DisableDiscovery).await.is_ok() {
|
||||
return ctx.reply(Ok(()));
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to disable discovery")))
|
||||
}
|
||||
});
|
||||
|
||||
b.method_with_cr_async("GetDecks", (), ("decks",), |mut ctx, cr, ()| {
|
||||
log::info!("GetDecks");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let manager = state.manager.clone();
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
async move {
|
||||
if manager.send(ManagerMessage::List(tx)).await.is_ok() {
|
||||
if let Ok(decks) = rx.await {
|
||||
return ctx.reply(Ok((decks
|
||||
.into_iter()
|
||||
.map(|d| (d.serial_number, d.product_name, d.port_name))
|
||||
.collect::<Vec<_>>(),)));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to fetch decks")))
|
||||
}
|
||||
});
|
||||
|
||||
b.method_with_cr_async(
|
||||
"Connect",
|
||||
("host", "port"),
|
||||
("state",),
|
||||
|mut ctx, cr, (host, port): (String, u16)| {
|
||||
log::info!("Connect");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let obs = state.obs.clone();
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
async move {
|
||||
if obs.send(ObsMessage::Connect(host, port)).await.is_ok() {
|
||||
if obs.send(ObsMessage::State(tx)).await.is_ok() {
|
||||
if let Ok(state) = rx.await {
|
||||
return ctx.reply(Ok((state.to_string(),)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to start connection")))
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
b.method_with_cr_async("Disconnect", (), ("state",), |mut ctx, cr, ()| {
|
||||
log::info!("Disconnect");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let obs = state.obs.clone();
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
async move {
|
||||
if obs.send(ObsMessage::Disconnect).await.is_ok() {
|
||||
if obs.send(ObsMessage::State(tx)).await.is_ok() {
|
||||
if let Ok(state) = rx.await {
|
||||
return ctx.reply(Ok((state.to_string(),)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to start disconnection")))
|
||||
}
|
||||
});
|
||||
|
||||
b.method_with_cr_async("GetState", (), ("state",), |mut ctx, cr, ()| {
|
||||
log::info!("GetState");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let obs = state.obs.clone();
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
async move {
|
||||
if obs.send(ObsMessage::State(tx)).await.is_ok() {
|
||||
if let Ok(state) = rx.await {
|
||||
return ctx.reply(Ok((state.to_string(),)));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to get state")))
|
||||
}
|
||||
});
|
||||
|
||||
b.method_with_cr_async(
|
||||
"Login",
|
||||
("password",),
|
||||
("state",),
|
||||
|mut ctx, cr, (password,): (String,)| {
|
||||
log::info!("Login");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let obs = state.obs.clone();
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
async move {
|
||||
if obs.send(ObsMessage::Authenticate(password)).await.is_ok() {
|
||||
if obs.send(ObsMessage::State(tx)).await.is_ok() {
|
||||
if let Ok(state) = rx.await {
|
||||
return ctx.reply(Ok((state.to_string(),)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to start login")))
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
b.method_with_cr_async(
|
||||
"GetCommands",
|
||||
("serial_number",),
|
||||
("commands",),
|
||||
|mut ctx, cr, (serial_number,): (String,)| {
|
||||
log::info!("GetCommands");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let store = state.store.clone();
|
||||
|
||||
async move {
|
||||
if let Ok(vec) = store.get_commands(&&serial_number).await {
|
||||
let vec: Vec<_> = vec
|
||||
.into_iter()
|
||||
.filter_map(|(key, cmd)| {
|
||||
Some((key, serde_json::to_string(&cmd).ok()?))
|
||||
})
|
||||
.collect();
|
||||
|
||||
return ctx.reply(Ok((vec,)));
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to get commands")))
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
b.method_with_cr_async("ReadInput", (), ("input",), |mut ctx, cr, ()| {
|
||||
log::info!("ReadInput");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let input = state.input.clone();
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
async move {
|
||||
if input.send(InputMessage::ReadInput(tx)).await.is_ok() {
|
||||
if let Ok((serial_number, key)) = rx.await {
|
||||
return ctx.reply(Ok((vec![(key, serial_number)],)));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to fetch key")))
|
||||
}
|
||||
});
|
||||
|
||||
b.method_with_cr_async(
|
||||
"SetInput",
|
||||
("serial_number", "key", "command"),
|
||||
(),
|
||||
|mut ctx, cr, (serial_number, key, command): (String, u8, String)| {
|
||||
log::info!("SetInput");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let store = state.store.clone();
|
||||
|
||||
async move {
|
||||
if let Ok(command) = serde_json::from_str::<Command>(&command) {
|
||||
if store.store(&serial_number, key, &command).await.is_ok() {
|
||||
return ctx.reply(Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to set mapping")))
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
b.method_with_cr_async(
|
||||
"UnsetInput",
|
||||
("serial_number", "key"),
|
||||
(),
|
||||
|mut ctx, cr, (serial_number, key): (String, u8)| {
|
||||
log::info!("UnsetInput");
|
||||
let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap();
|
||||
|
||||
let store = state.store.clone();
|
||||
|
||||
async move {
|
||||
if store.unset(&serial_number, key).await.is_ok() {
|
||||
return ctx.reply(Ok(()));
|
||||
}
|
||||
|
||||
ctx.reply(Err(MethodErr::failed("Failed to set mapping")))
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
cr.insert("/dog/asonix/git/asonix/Streamdeck", &[iface_token], state);
|
||||
|
||||
connection.start_receive(
|
||||
MatchRule::new_method_call(),
|
||||
Box::new(move |msg, conn| {
|
||||
cr.handle_message(msg, conn).unwrap();
|
||||
true
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
136
src/deck.rs
136
src/deck.rs
|
@ -1,95 +1,67 @@
|
|||
use crate::{ManagerMessage, Port};
|
||||
use crate::{
|
||||
message::{DeckConfig, InputMessage, ManagerMessage},
|
||||
port::Port,
|
||||
};
|
||||
use std::{future::Future, pin::Pin};
|
||||
use tokio::{sync::mpsc::Sender, time::Duration};
|
||||
|
||||
pub(crate) async fn task(manager: Sender<ManagerMessage>, name: String, port: Port) {
|
||||
let _ = do_task(port).await;
|
||||
|
||||
log::info!("{} disconnected", name);
|
||||
|
||||
while manager
|
||||
.send(ManagerMessage::Closed(name.clone()))
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
if manager.is_closed() {
|
||||
return;
|
||||
}
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Deck {
|
||||
pub(crate) manager: Sender<ManagerMessage>,
|
||||
pub(crate) input: Sender<InputMessage>,
|
||||
pub(crate) port: Port,
|
||||
pub(crate) config: DeckConfig,
|
||||
}
|
||||
|
||||
async fn do_task(port: Port) -> Result<(), anyhow::Error> {
|
||||
let mut read_port = port.try_clone()?.lines();
|
||||
let mut write_port = port;
|
||||
impl Deck {
|
||||
pub(crate) async fn run(self) {
|
||||
let res = self.read_task();
|
||||
if let Ok(fut) = res {
|
||||
let _ = tokio::spawn(fut).await;
|
||||
}
|
||||
|
||||
let (read_tx, mut read_rx) = tokio::sync::mpsc::channel(16);
|
||||
let (write_tx, mut write_rx) = tokio::sync::mpsc::channel(16);
|
||||
log::info!("{} disconnected", self.config.product_name);
|
||||
|
||||
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel();
|
||||
while self
|
||||
.manager
|
||||
.send(ManagerMessage::Closed(self.config.port_name.clone()))
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
if self.manager.is_closed() {
|
||||
return;
|
||||
}
|
||||
|
||||
let read_handle = tokio::spawn(async move {
|
||||
loop {
|
||||
log::trace!("read loop");
|
||||
match read_port.read_line().await {
|
||||
Ok(line) => {
|
||||
let _ = read_tx.send(line).await;
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!("Error reading: {}", e);
|
||||
if let Ok(e) = e.downcast::<std::io::Error>() {
|
||||
if matches!(e.kind(), std::io::ErrorKind::BrokenPipe) {
|
||||
break;
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn read_task(
|
||||
&self,
|
||||
) -> Result<Pin<Box<dyn Future<Output = ()> + Send + 'static>>, anyhow::Error> {
|
||||
let mut read_port = self.port.try_clone()?.bytes();
|
||||
let input = self.input.clone();
|
||||
let serial_number = self.config.serial_number.clone();
|
||||
|
||||
Ok(Box::pin(async move {
|
||||
loop {
|
||||
log::trace!("read loop");
|
||||
match read_port.read_byte().await {
|
||||
Ok(byte) => {
|
||||
let _ = input
|
||||
.send(InputMessage::Press(serial_number.clone(), byte))
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!("Error reading: {}", e);
|
||||
if let Ok(e) = e.downcast::<std::io::Error>() {
|
||||
if matches!(e.kind(), std::io::ErrorKind::BrokenPipe) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = shutdown_tx.send(());
|
||||
});
|
||||
let write_handle = tokio::spawn(async move {
|
||||
while let Some(bytes) = write_rx.recv().await {
|
||||
log::trace!("write loop");
|
||||
if let Err(e) = write_port.write(bytes).await {
|
||||
if let Ok(e) = e.downcast::<std::io::Error>() {
|
||||
if matches!(e.kind(), std::io::ErrorKind::BrokenPipe) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = write_port.bytes_to_write().await {
|
||||
if let Ok(e) = e.downcast::<serialport::Error>() {
|
||||
if matches!(e.kind, serialport::ErrorKind::NoDevice) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tokio::pin!(shutdown_rx);
|
||||
|
||||
loop {
|
||||
log::trace!("loop");
|
||||
tokio::select! {
|
||||
Some(line) = read_rx.recv() => {
|
||||
log::info!("{}", line.trim());
|
||||
}
|
||||
_ = &mut shutdown_rx => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
log::debug!("Shutting down");
|
||||
|
||||
read_handle.abort();
|
||||
write_handle.abort();
|
||||
|
||||
let _ = read_handle.await;
|
||||
let _ = write_handle.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
29
src/input.rs
Normal file
29
src/input.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use crate::{
|
||||
message::{InputMessage, ObsMessage},
|
||||
store::Store,
|
||||
};
|
||||
use tokio::sync::{
|
||||
mpsc::{Receiver, Sender},
|
||||
oneshot,
|
||||
};
|
||||
|
||||
pub(crate) async fn task(store: Store, mut rx: Receiver<InputMessage>, obs_tx: Sender<ObsMessage>) {
|
||||
let mut sender_opt: Option<oneshot::Sender<(String, u8)>> = None;
|
||||
|
||||
while let Some(msg) = rx.recv().await {
|
||||
match msg {
|
||||
InputMessage::Press(serial_number, key) => {
|
||||
if let Some(sender) = sender_opt.take() {
|
||||
let _ = sender.send((serial_number, key));
|
||||
} else {
|
||||
if let Ok(Some(command)) = store.pressed(&serial_number, key).await {
|
||||
let _ = obs_tx.send(ObsMessage::Command(command)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
InputMessage::ReadInput(sender) => {
|
||||
sender_opt = Some(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
219
src/main.rs
219
src/main.rs
|
@ -1,147 +1,17 @@
|
|||
use serialport::{SerialPortType, UsbPortInfo};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tokio::{
|
||||
sync::mpsc::{Receiver, Sender},
|
||||
task::JoinHandle,
|
||||
time::Duration,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Notify;
|
||||
|
||||
#[cfg(feature = "ipc-dbus")]
|
||||
mod dbus;
|
||||
mod deck;
|
||||
mod input;
|
||||
mod manager;
|
||||
mod message;
|
||||
mod obs;
|
||||
mod port;
|
||||
mod store;
|
||||
|
||||
use port::Port;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ManagerMessage {
|
||||
Tick,
|
||||
Found(HashSet<String>),
|
||||
Opened(String, Port),
|
||||
Closed(String),
|
||||
}
|
||||
|
||||
struct Manager {
|
||||
tx: Sender<ManagerMessage>,
|
||||
ports: HashMap<String, JoinHandle<()>>,
|
||||
opening: HashMap<String, JoinHandle<()>>,
|
||||
finding: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
fn new(tx: Sender<ManagerMessage>) -> Self {
|
||||
Manager {
|
||||
tx,
|
||||
finding: None,
|
||||
ports: HashMap::default(),
|
||||
opening: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn turn<'a>(&'a mut self, msg: ManagerMessage) -> Turn<'a> {
|
||||
Turn(self, Some(msg))
|
||||
}
|
||||
|
||||
fn do_turn(&mut self, msg: ManagerMessage, cx: &mut Context<'_>) {
|
||||
log::debug!("msg: {:?}", msg);
|
||||
log::debug!("state: {:?}", self);
|
||||
match msg {
|
||||
ManagerMessage::Tick => {
|
||||
if let Some(mut handle) = self.finding.take() {
|
||||
if matches!(Pin::new(&mut handle).poll(cx), Poll::Pending) {
|
||||
self.finding = Some(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let handle = tokio::spawn(find_task(self.tx.clone()));
|
||||
self.finding = Some(handle);
|
||||
}
|
||||
ManagerMessage::Found(found_decks) => {
|
||||
let known = self.ports.keys().cloned().collect::<HashSet<_>>();
|
||||
|
||||
for deck in found_decks.difference(&known) {
|
||||
log::info!("New deck: {}", deck);
|
||||
if let Some(mut handle) = self.opening.remove(deck) {
|
||||
if matches!(Pin::new(&mut handle).poll(cx), Poll::Pending) {
|
||||
self.opening.insert(deck.clone(), handle);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let handle = tokio::spawn(open_task(deck.clone(), self.tx.clone()));
|
||||
self.opening.insert(deck.clone(), handle);
|
||||
}
|
||||
|
||||
for deck in known.difference(&found_decks) {
|
||||
log::info!("Removed deck: {}", deck);
|
||||
if let Some(handle) = self.ports.remove(deck) {
|
||||
handle.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
ManagerMessage::Opened(deck, port) => {
|
||||
let handle = tokio::spawn(crate::deck::task(self.tx.clone(), deck.clone(), port));
|
||||
self.ports.insert(deck, handle);
|
||||
}
|
||||
ManagerMessage::Closed(deck) => {
|
||||
if let Some(handle) = self.ports.remove(&deck) {
|
||||
handle.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn state_task(tx: Sender<ManagerMessage>, mut rx: Receiver<ManagerMessage>) {
|
||||
let mut state = Manager::new(tx);
|
||||
|
||||
while let Some(msg) = rx.recv().await {
|
||||
state.turn(msg).await;
|
||||
}
|
||||
}
|
||||
|
||||
struct Turn<'a>(&'a mut Manager, Option<ManagerMessage>);
|
||||
|
||||
impl<'a> Future for Turn<'a> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let msg = self.1.take().unwrap();
|
||||
self.0.do_turn(msg, cx);
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn tick_task(tx: Sender<ManagerMessage>) {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(5));
|
||||
|
||||
loop {
|
||||
let _ = tx.send(ManagerMessage::Tick).await;
|
||||
interval.tick().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_task(tx: Sender<ManagerMessage>) {
|
||||
match find_streamdecks().await {
|
||||
Ok(streamdecks) => {
|
||||
let _ = tx.send(ManagerMessage::Found(streamdecks)).await;
|
||||
}
|
||||
Err(e) => log::error!("Error finding streamdecks: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn open_task(name: String, tx: Sender<ManagerMessage>) {
|
||||
match Port::open(name.clone()).await {
|
||||
Ok(port) => {
|
||||
let _ = tx.send(ManagerMessage::Opened(name, port)).await;
|
||||
}
|
||||
Err(e) => log::error!("Error opening streamdeck {}: {}", name, e),
|
||||
}
|
||||
}
|
||||
use store::Store;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), anyhow::Error> {
|
||||
|
@ -150,13 +20,43 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
}
|
||||
env_logger::init();
|
||||
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(16);
|
||||
let (manager_tx, manager_rx) = tokio::sync::mpsc::channel(16);
|
||||
let (input_tx, input_rx) = tokio::sync::mpsc::channel(16);
|
||||
let (obs_tx, obs_rx) = tokio::sync::mpsc::channel(16);
|
||||
|
||||
let db = sled::Config::new().temporary(true).open()?;
|
||||
let store = Store::build(db).await?;
|
||||
|
||||
let shutdown = Arc::new(Notify::new());
|
||||
|
||||
let mut handles = vec![];
|
||||
|
||||
handles.push(tokio::spawn(tick_task(tx.clone())));
|
||||
handles.push(tokio::spawn(state_task(tx, rx)));
|
||||
#[cfg(feature = "ipc-dbus")]
|
||||
{
|
||||
dbus::Dbus::build(
|
||||
shutdown.clone(),
|
||||
store.clone(),
|
||||
input_tx.clone(),
|
||||
obs_tx.clone(),
|
||||
manager_tx.clone(),
|
||||
)
|
||||
.await?
|
||||
.run()
|
||||
.await;
|
||||
}
|
||||
|
||||
tokio::signal::ctrl_c().await?;
|
||||
handles.push(tokio::spawn(input::task(store, input_rx, obs_tx)));
|
||||
handles.push(tokio::spawn(obs::task(obs_rx)));
|
||||
handles.push(tokio::spawn(manager::task(
|
||||
input_tx, manager_tx, manager_rx,
|
||||
)));
|
||||
|
||||
tokio::select! {
|
||||
_ = shutdown.notified() => {},
|
||||
_ = tokio::signal::ctrl_c() => {},
|
||||
};
|
||||
|
||||
log::info!("Application closing");
|
||||
|
||||
for hdnl in &handles {
|
||||
hdnl.abort();
|
||||
|
@ -168,34 +68,3 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn find_streamdecks() -> Result<HashSet<String>, anyhow::Error> {
|
||||
tokio::task::spawn_blocking(|| {
|
||||
Ok(serialport::available_ports()?
|
||||
.into_iter()
|
||||
.filter_map(|port| {
|
||||
if let SerialPortType::UsbPort(info) = port.port_type {
|
||||
if is_valid_port(info) {
|
||||
return Some(port.port_name);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.collect())
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
fn is_valid_port(_: UsbPortInfo) -> bool {
|
||||
// TODO: Check for BasedOtt pid/vid
|
||||
true
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Manager {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let ports = self.ports.keys().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||
|
||||
f.debug_struct("Manager").field("ports", &ports).finish()
|
||||
}
|
||||
}
|
||||
|
|
218
src/manager.rs
Normal file
218
src/manager.rs
Normal file
|
@ -0,0 +1,218 @@
|
|||
use crate::{
|
||||
deck::Deck,
|
||||
message::{DeckConfig, InputMessage, ManagerMessage},
|
||||
port::Port,
|
||||
};
|
||||
use serialport::{SerialPortType, UsbPortInfo};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tokio::{
|
||||
sync::mpsc::{Receiver, Sender},
|
||||
task::JoinHandle,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
struct Manager {
|
||||
input_tx: Sender<InputMessage>,
|
||||
tx: Sender<ManagerMessage>,
|
||||
decks: HashMap<String, DeckConfig>,
|
||||
ports: HashMap<String, JoinHandle<()>>,
|
||||
opening: HashMap<String, JoinHandle<()>>,
|
||||
finding: Option<JoinHandle<()>>,
|
||||
discovery: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
fn new(input_tx: Sender<InputMessage>, tx: Sender<ManagerMessage>) -> Self {
|
||||
let discovery = tokio::spawn(tick_task(tx.clone()));
|
||||
Manager {
|
||||
input_tx,
|
||||
tx,
|
||||
decks: HashMap::default(),
|
||||
ports: HashMap::default(),
|
||||
opening: HashMap::default(),
|
||||
finding: None,
|
||||
discovery: Some(discovery),
|
||||
}
|
||||
}
|
||||
|
||||
fn turn<'a>(&'a mut self, msg: ManagerMessage) -> Turn<'a> {
|
||||
Turn(self, Some(msg))
|
||||
}
|
||||
|
||||
fn do_turn(&mut self, msg: ManagerMessage, cx: &mut Context<'_>) {
|
||||
log::debug!("msg: {:?}", msg);
|
||||
log::debug!("state: {:?}", self);
|
||||
match msg {
|
||||
ManagerMessage::EnableDiscovery => {
|
||||
if let Some(mut handle) = self.discovery.take() {
|
||||
if matches!(Pin::new(&mut handle).poll(cx), Poll::Pending) {
|
||||
self.discovery = Some(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let handle = tokio::spawn(tick_task(self.tx.clone()));
|
||||
self.discovery = Some(handle);
|
||||
}
|
||||
ManagerMessage::DisableDiscovery => {
|
||||
if let Some(handle) = self.discovery.take() {
|
||||
handle.abort();
|
||||
}
|
||||
}
|
||||
ManagerMessage::Tick => {
|
||||
if let Some(mut handle) = self.finding.take() {
|
||||
if matches!(Pin::new(&mut handle).poll(cx), Poll::Pending) {
|
||||
self.finding = Some(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let handle = tokio::spawn(find_task(self.tx.clone()));
|
||||
self.finding = Some(handle);
|
||||
}
|
||||
ManagerMessage::List(sender) => {
|
||||
let _ = sender.send(self.decks.values().cloned().collect());
|
||||
}
|
||||
ManagerMessage::Found(mut found_decks) => {
|
||||
let known = self.ports.keys().cloned().collect::<HashSet<_>>();
|
||||
let found = found_decks.keys().cloned().collect::<HashSet<_>>();
|
||||
|
||||
for port_name in found.difference(&known) {
|
||||
if let Some(mut handle) = self.opening.remove(port_name) {
|
||||
if matches!(Pin::new(&mut handle).poll(cx), Poll::Pending) {
|
||||
self.opening.insert(port_name.clone(), handle);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(config) = found_decks.remove(port_name) {
|
||||
log::info!("New deck: {}", config.port_name);
|
||||
let handle = tokio::spawn(open_task(config, self.tx.clone()));
|
||||
self.opening.insert(port_name.clone(), handle);
|
||||
}
|
||||
}
|
||||
|
||||
for port_name in known.difference(&found) {
|
||||
log::info!("Removed deck: {}", port_name);
|
||||
if let Some(handle) = self.ports.remove(port_name) {
|
||||
handle.abort();
|
||||
}
|
||||
self.decks.remove(port_name);
|
||||
}
|
||||
}
|
||||
ManagerMessage::Opened(deck, port) => {
|
||||
let handle = tokio::spawn(
|
||||
Deck {
|
||||
manager: self.tx.clone(),
|
||||
input: self.input_tx.clone(),
|
||||
port,
|
||||
config: deck.clone(),
|
||||
}
|
||||
.run(),
|
||||
);
|
||||
self.ports.insert(deck.port_name.clone(), handle);
|
||||
self.decks.insert(deck.port_name.clone(), deck);
|
||||
}
|
||||
ManagerMessage::Closed(port_name) => {
|
||||
if let Some(handle) = self.ports.remove(&port_name) {
|
||||
handle.abort();
|
||||
}
|
||||
self.decks.remove(&port_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn task(
|
||||
input_tx: Sender<InputMessage>,
|
||||
tx: Sender<ManagerMessage>,
|
||||
mut rx: Receiver<ManagerMessage>,
|
||||
) {
|
||||
let mut state = Manager::new(input_tx, tx);
|
||||
|
||||
while let Some(msg) = rx.recv().await {
|
||||
state.turn(msg).await;
|
||||
}
|
||||
}
|
||||
|
||||
struct Turn<'a>(&'a mut Manager, Option<ManagerMessage>);
|
||||
|
||||
impl<'a> Future for Turn<'a> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let msg = self.1.take().unwrap();
|
||||
self.0.do_turn(msg, cx);
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn tick_task(tx: Sender<ManagerMessage>) {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(5));
|
||||
|
||||
loop {
|
||||
let _ = tx.send(ManagerMessage::Tick).await;
|
||||
interval.tick().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_task(tx: Sender<ManagerMessage>) {
|
||||
match find_streamdecks().await {
|
||||
Ok(streamdecks) => {
|
||||
let _ = tx.send(ManagerMessage::Found(streamdecks)).await;
|
||||
}
|
||||
Err(e) => log::error!("Error finding streamdecks: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn open_task(deck: DeckConfig, tx: Sender<ManagerMessage>) {
|
||||
match Port::open(deck.port_name.clone()).await {
|
||||
Ok(port) => {
|
||||
let _ = tx.send(ManagerMessage::Opened(deck, port)).await;
|
||||
}
|
||||
Err(e) => log::error!("Error opening streamdeck {}: {}", deck.port_name, e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_streamdecks() -> Result<HashMap<String, DeckConfig>, anyhow::Error> {
|
||||
tokio::task::spawn_blocking(|| {
|
||||
Ok(serialport::available_ports()?
|
||||
.into_iter()
|
||||
.filter_map(|port| {
|
||||
if let SerialPortType::UsbPort(info) = port.port_type {
|
||||
if is_valid_port(&info) {
|
||||
return Some((
|
||||
port.port_name.clone(),
|
||||
DeckConfig {
|
||||
port_name: port.port_name,
|
||||
product_name: info.product?,
|
||||
serial_number: info.serial_number?,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.collect())
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
fn is_valid_port(_: &UsbPortInfo) -> bool {
|
||||
// TODO: Check for BasedOtt pid/vid
|
||||
true
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Manager {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let ports = self.ports.keys().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||
|
||||
f.debug_struct("Manager").field("ports", &ports).finish()
|
||||
}
|
||||
}
|
64
src/message.rs
Normal file
64
src/message.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use crate::port::Port;
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::oneshot::Sender;
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub(crate) enum Command {
|
||||
SwitchScene { name: String },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Query {
|
||||
GetScenes(Sender<Vec<String>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum State {
|
||||
Disconnected,
|
||||
Unauthenticated,
|
||||
Connected,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ObsMessage {
|
||||
Query(Query),
|
||||
Command(Command),
|
||||
Connect(String, u16),
|
||||
Authenticate(String),
|
||||
State(Sender<State>),
|
||||
Disconnect,
|
||||
}
|
||||
|
||||
pub(crate) enum InputMessage {
|
||||
Press(String, u8),
|
||||
ReadInput(Sender<(String, u8)>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct DeckConfig {
|
||||
pub(crate) port_name: String,
|
||||
pub(crate) product_name: String,
|
||||
pub(crate) serial_number: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ManagerMessage {
|
||||
EnableDiscovery,
|
||||
DisableDiscovery,
|
||||
Tick,
|
||||
List(Sender<Vec<DeckConfig>>),
|
||||
Found(HashMap<String, DeckConfig>),
|
||||
Opened(DeckConfig, Port),
|
||||
Closed(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for State {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
State::Disconnected => write!(f, "Disconnected"),
|
||||
State::Unauthenticated => write!(f, "Unauthenticated"),
|
||||
State::Connected => write!(f, "Connected"),
|
||||
}
|
||||
}
|
||||
}
|
197
src/obs.rs
Normal file
197
src/obs.rs
Normal file
|
@ -0,0 +1,197 @@
|
|||
use crate::message::{Command, ObsMessage, Query, State};
|
||||
use obws::Client;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
|
||||
pub(crate) struct Obs {
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
enum Inner {
|
||||
Disconnected(Disconnected),
|
||||
Unauthenticated(Unauthenticated),
|
||||
Connected(Connected),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Disconnected;
|
||||
|
||||
struct Unauthenticated {
|
||||
client: Option<Client>,
|
||||
challenge: Option<String>,
|
||||
salt: Option<String>,
|
||||
}
|
||||
|
||||
struct Connected {
|
||||
client: Option<Client>,
|
||||
}
|
||||
|
||||
pub(crate) async fn task(rx: Receiver<ObsMessage>) {
|
||||
Obs::new().run(rx).await
|
||||
}
|
||||
|
||||
impl Obs {
|
||||
fn new() -> Self {
|
||||
Obs {
|
||||
inner: Inner::Disconnected(Disconnected),
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(mut self, mut rx: Receiver<ObsMessage>) {
|
||||
while let Some(msg) = rx.recv().await {
|
||||
self.handle(msg).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle(&mut self, message: ObsMessage) {
|
||||
match message {
|
||||
ObsMessage::State(sender) => {
|
||||
let state = match &self.inner {
|
||||
Inner::Disconnected(_) => State::Disconnected,
|
||||
Inner::Unauthenticated(_) => State::Unauthenticated,
|
||||
Inner::Connected(_) => State::Connected,
|
||||
};
|
||||
|
||||
let _ = sender.send(state);
|
||||
}
|
||||
message => {
|
||||
self.inner.handle(message).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
async fn handle(&mut self, message: ObsMessage) {
|
||||
let new_state = match self {
|
||||
Inner::Disconnected(disconnected) => disconnected.handle(message).await,
|
||||
Inner::Unauthenticated(unauthenticated) => unauthenticated.handle(message).await,
|
||||
Inner::Connected(connected) => connected.handle(message).await,
|
||||
};
|
||||
|
||||
if let Some(state) = new_state {
|
||||
let _ = std::mem::replace(self, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Disconnected {
|
||||
async fn handle(&mut self, message: ObsMessage) -> Option<Inner> {
|
||||
match message {
|
||||
ObsMessage::Connect(host, port) => self
|
||||
.connect(host, port)
|
||||
.await
|
||||
.map_err(|e| log::error!("{}", e))
|
||||
.ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn connect(&mut self, host: String, port: u16) -> Result<Inner, anyhow::Error> {
|
||||
log::info!("Connecting to {}:{}", host, port);
|
||||
let client = Client::connect(host, port).await?;
|
||||
let auth_required = client.general().get_auth_required().await?;
|
||||
|
||||
if auth_required.auth_required {
|
||||
return Ok(Inner::Unauthenticated(Unauthenticated {
|
||||
client: Some(client),
|
||||
challenge: auth_required.challenge,
|
||||
salt: auth_required.salt,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(Inner::Connected(Connected {
|
||||
client: Some(client),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Unauthenticated {
|
||||
async fn handle(&mut self, message: ObsMessage) -> Option<Inner> {
|
||||
match message {
|
||||
ObsMessage::Authenticate(password) => self.authenticate(password).await.ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn authenticate(&mut self, password: String) -> Result<Inner, anyhow::Error> {
|
||||
if let Some(client) = self.client.as_ref() {
|
||||
if let Err(e) = client.login(Some(password)).await {
|
||||
if client.general().get_version().await.is_ok() {
|
||||
return Err(e.into());
|
||||
} else {
|
||||
return Ok(Inner::Disconnected(Disconnected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Inner::Connected(Connected {
|
||||
client: self.client.take(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Connected {
|
||||
async fn handle(&mut self, message: ObsMessage) -> Option<Inner> {
|
||||
match message {
|
||||
ObsMessage::Command(command) => {
|
||||
let res = self.command(command).await;
|
||||
self.check_err(res).await
|
||||
}
|
||||
ObsMessage::Query(query) => {
|
||||
let res = self.query(query).await;
|
||||
self.check_err(res).await
|
||||
}
|
||||
ObsMessage::Disconnect => {
|
||||
let _ = self.disconnect().await;
|
||||
Some(Inner::Disconnected(Disconnected))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_err(&mut self, res: Result<(), anyhow::Error>) -> Option<Inner> {
|
||||
match res {
|
||||
Ok(()) => None,
|
||||
Err(_) => {
|
||||
if let Some(client) = self.client.as_ref() {
|
||||
if client.general().get_version().await.is_ok() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(Inner::Disconnected(Disconnected))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn command(&self, command: Command) -> Result<(), anyhow::Error> {
|
||||
match command {
|
||||
Command::SwitchScene { name } => {
|
||||
if let Some(client) = self.client.as_ref() {
|
||||
client.scenes().set_current_scene(&name).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn query(&self, query: Query) -> Result<(), anyhow::Error> {
|
||||
match query {
|
||||
Query::GetScenes(sender) => {
|
||||
if let Some(client) = self.client.as_ref() {
|
||||
let scene_list = client.scenes().get_scene_list().await?;
|
||||
|
||||
let _ = sender.send(scene_list.scenes.into_iter().map(|s| s.name).collect());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn disconnect(&mut self) -> Result<(), anyhow::Error> {
|
||||
if let Some(client) = self.client.as_mut() {
|
||||
client.disconnect().await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
17
src/port.rs
17
src/port.rs
|
@ -6,6 +6,7 @@ use std::{
|
|||
|
||||
pub(crate) struct Port(Inner<Box<dyn SerialPort>>);
|
||||
pub(crate) struct LinesReader(Inner<BufReader<Box<dyn SerialPort>>>);
|
||||
pub(crate) struct BytesReader(Inner<Box<dyn SerialPort>>);
|
||||
|
||||
struct Inner<I>(Option<I>);
|
||||
|
||||
|
@ -74,6 +75,10 @@ impl Port {
|
|||
LinesReader(Inner(self.0.take().map(BufReader::new)))
|
||||
}
|
||||
|
||||
pub(crate) fn bytes(mut self) -> BytesReader {
|
||||
BytesReader(Inner(self.0.take()))
|
||||
}
|
||||
|
||||
pub(crate) async fn bytes_to_write(&mut self) -> Result<u32, anyhow::Error> {
|
||||
self.0
|
||||
.with_inner(move |port| {
|
||||
|
@ -85,6 +90,18 @@ impl Port {
|
|||
}
|
||||
}
|
||||
|
||||
impl BytesReader {
|
||||
pub(crate) async fn read_byte(&mut self) -> Result<u8, anyhow::Error> {
|
||||
self.0
|
||||
.with_inner(move |mut port| {
|
||||
let mut bytes = [0u8; 1];
|
||||
let res = port.read_exact(&mut bytes);
|
||||
(port, res.map(|_| bytes[0]))
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl LinesReader {
|
||||
pub(crate) async fn read_line(&mut self) -> Result<String, anyhow::Error> {
|
||||
self.0
|
||||
|
|
111
src/store.rs
Normal file
111
src/store.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
use crate::message::Command;
|
||||
use sled::{Db, IVec, Tree};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Store {
|
||||
commands: Tree,
|
||||
db: Db,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub(crate) async fn build(db: Db) -> Result<Self, anyhow::Error> {
|
||||
let db2 = db.clone();
|
||||
|
||||
Ok(Store {
|
||||
commands: tokio::task::spawn_blocking(move || {
|
||||
db2.open_tree("dog.asonix.git.asonix.streamdeck/commands")
|
||||
})
|
||||
.await??,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn unset(&self, deck: &str, input: u8) -> Result<(), anyhow::Error> {
|
||||
let key = self.cmd_key(deck, input);
|
||||
|
||||
let commands = self.commands.clone();
|
||||
tokio::task::spawn_blocking(move || commands.remove(key)).await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn store(
|
||||
&self,
|
||||
deck: &str,
|
||||
input: u8,
|
||||
command: &Command,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let key = self.cmd_key(deck, input);
|
||||
let value = self.cmd_value(command)?;
|
||||
|
||||
let commands = self.commands.clone();
|
||||
tokio::task::spawn_blocking(move || commands.insert(key, value)).await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn pressed(
|
||||
&self,
|
||||
deck: &str,
|
||||
input: u8,
|
||||
) -> Result<Option<Command>, anyhow::Error> {
|
||||
let key = self.cmd_key(deck, input);
|
||||
|
||||
let commands = self.commands.clone();
|
||||
let opt = tokio::task::spawn_blocking(move || commands.get(key)).await??;
|
||||
|
||||
if let Some(ivec) = opt {
|
||||
return Ok(Some(serde_json::from_slice(&ivec)?));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_commands(
|
||||
&self,
|
||||
deck: &str,
|
||||
) -> Result<Vec<(u8, Command)>, anyhow::Error> {
|
||||
let prefix = self.cmd_prefix(deck);
|
||||
|
||||
let commands = self.commands.clone();
|
||||
let vec = tokio::task::spawn_blocking(move || {
|
||||
let prefix_len = prefix.len();
|
||||
commands
|
||||
.scan_prefix(prefix)
|
||||
.filter_map(|res| {
|
||||
let (key, value) = res.ok()?;
|
||||
|
||||
if key.len() != prefix_len + 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let key = key[prefix_len];
|
||||
|
||||
let command = serde_json::from_slice(&value).ok()?;
|
||||
|
||||
Some((key, command))
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
fn cmd_prefix(&self, deck: &str) -> IVec {
|
||||
let mut prefix = deck.as_bytes().to_vec();
|
||||
prefix.push(b'/');
|
||||
|
||||
IVec::from(prefix)
|
||||
}
|
||||
|
||||
fn cmd_key(&self, deck: &str, input: u8) -> IVec {
|
||||
let mut key = deck.as_bytes().to_vec();
|
||||
key.push(b'/');
|
||||
key.push(input);
|
||||
|
||||
IVec::from(key)
|
||||
}
|
||||
|
||||
fn cmd_value(&self, command: &Command) -> Result<IVec, anyhow::Error> {
|
||||
Ok(IVec::from(serde_json::to_vec(&command)?))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue