commiting these before I leave
This commit is contained in:
parent
c84a9748e7
commit
e59025f645
14 changed files with 559 additions and 916 deletions
540
Cargo.lock
generated
540
Cargo.lock
generated
|
@ -17,12 +17,6 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aligned-vec"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -44,29 +38,6 @@ version = "1.0.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arbitrary"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arg_enum_proc_macro"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.53",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arrayvec"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-channel"
|
name = "async-channel"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -103,29 +74,6 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "av1-grain"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"arrayvec",
|
|
||||||
"log",
|
|
||||||
"nom",
|
|
||||||
"num-rational",
|
|
||||||
"v_frame",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "avif-serialize"
|
|
||||||
version = "0.8.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2"
|
|
||||||
dependencies = [
|
|
||||||
"arrayvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.71"
|
version = "0.3.71"
|
||||||
|
@ -147,12 +95,6 @@ version = "0.21.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit_field"
|
|
||||||
version = "0.10.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -168,12 +110,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitstream-io"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "06c9989a51171e2e81038ab168b6ae22886fe9ded214430dbb4f41c28cf176da"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
|
@ -183,36 +119,18 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "built"
|
|
||||||
version = "0.7.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41bfbdb21256b87a8b5e80fab81a8eed158178e812fd7ba451907518b2742f16"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.15.4"
|
version = "3.15.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bytemuck"
|
|
||||||
version = "1.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder-lite"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
|
@ -258,10 +176,6 @@ name = "cc"
|
||||||
version = "1.0.90"
|
version = "1.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||||
dependencies = [
|
|
||||||
"jobserver",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-expr"
|
name = "cfg-expr"
|
||||||
|
@ -297,12 +211,6 @@ dependencies = [
|
||||||
"windows-targets 0.52.4",
|
"windows-targets 0.52.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "color_quant"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "concurrent-queue"
|
name = "concurrent-queue"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
|
@ -386,34 +294,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc32fast"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-deque"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-epoch"
|
|
||||||
version = "0.9.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.19"
|
version = "0.8.19"
|
||||||
|
@ -517,31 +397,6 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "exr"
|
|
||||||
version = "1.72.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
|
|
||||||
dependencies = [
|
|
||||||
"bit_field",
|
|
||||||
"flume",
|
|
||||||
"half",
|
|
||||||
"lebe",
|
|
||||||
"miniz_oxide",
|
|
||||||
"rayon-core",
|
|
||||||
"smallvec",
|
|
||||||
"zune-inflate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fdeflate"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
|
|
||||||
dependencies = [
|
|
||||||
"simd-adler32",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "field-offset"
|
name = "field-offset"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -552,25 +407,6 @@ dependencies = [
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flate2"
|
|
||||||
version = "1.0.29"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4556222738635b7a3417ae6130d8f52201e45a0c4d1a907f0826383adb5f85e7"
|
|
||||||
dependencies = [
|
|
||||||
"crc32fast",
|
|
||||||
"miniz_oxide",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flume"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
|
||||||
dependencies = [
|
|
||||||
"spin",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
@ -766,16 +602,6 @@ dependencies = [
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gif"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
|
|
||||||
dependencies = [
|
|
||||||
"color_quant",
|
|
||||||
"weezl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gilrs"
|
name = "gilrs"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
@ -1192,16 +1018,6 @@ dependencies = [
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "half"
|
|
||||||
version = "2.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"crunchy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
|
@ -1276,45 +1092,6 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "image"
|
|
||||||
version = "0.25.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
"byteorder",
|
|
||||||
"color_quant",
|
|
||||||
"exr",
|
|
||||||
"gif",
|
|
||||||
"image-webp",
|
|
||||||
"num-traits",
|
|
||||||
"png",
|
|
||||||
"qoi",
|
|
||||||
"ravif",
|
|
||||||
"rayon",
|
|
||||||
"rgb",
|
|
||||||
"tiff",
|
|
||||||
"zune-core",
|
|
||||||
"zune-jpeg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "image-webp"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder-lite",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "imgref"
|
|
||||||
version = "1.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.2.5"
|
version = "2.2.5"
|
||||||
|
@ -1345,17 +1122,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "interpolate_name"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.53",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "interprocess"
|
name = "interprocess"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -1404,15 +1170,6 @@ version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jobserver"
|
|
||||||
version = "0.1.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "joystick-controller-client"
|
name = "joystick-controller-client"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -1428,7 +1185,6 @@ dependencies = [
|
||||||
"gstreamer-app",
|
"gstreamer-app",
|
||||||
"gstreamer-video",
|
"gstreamer-video",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
"image",
|
|
||||||
"interprocess",
|
"interprocess",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -1438,12 +1194,6 @@ dependencies = [
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jpeg-decoder"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.69"
|
version = "0.3.69"
|
||||||
|
@ -1470,29 +1220,12 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lebe"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.153"
|
version = "0.2.153"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libfuzzer-sys"
|
|
||||||
version = "0.4.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
|
|
||||||
dependencies = [
|
|
||||||
"arbitrary",
|
|
||||||
"cc",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libudev-sys"
|
name = "libudev-sys"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -1509,31 +1242,12 @@ version = "0.5.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.21"
|
version = "0.4.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "loop9"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
|
|
||||||
dependencies = [
|
|
||||||
"imgref",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mach2"
|
name = "mach2"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -1543,16 +1257,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "maybe-rayon"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"rayon",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.1"
|
version = "2.7.1"
|
||||||
|
@ -1581,7 +1285,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
"simd-adler32",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1601,12 +1304,6 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0"
|
checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "new_debug_unreachable"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.28.0"
|
version = "0.28.0"
|
||||||
|
@ -1629,40 +1326,12 @@ dependencies = [
|
||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "noop_proc_macro"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-bigint"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-derive"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.53",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.46"
|
version = "0.1.46"
|
||||||
|
@ -1679,7 +1348,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"num-bigint",
|
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
@ -1857,19 +1525,6 @@ version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "png"
|
|
||||||
version = "0.17.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"crc32fast",
|
|
||||||
"fdeflate",
|
|
||||||
"flate2",
|
|
||||||
"miniz_oxide",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "powerfmt"
|
name = "powerfmt"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -1924,40 +1579,6 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "profiling"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
|
|
||||||
dependencies = [
|
|
||||||
"profiling-procmacros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "profiling-procmacros"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.53",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "qoi"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quick-error"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.35"
|
version = "1.0.35"
|
||||||
|
@ -1997,91 +1618,12 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rav1e"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
|
|
||||||
dependencies = [
|
|
||||||
"arbitrary",
|
|
||||||
"arg_enum_proc_macro",
|
|
||||||
"arrayvec",
|
|
||||||
"av1-grain",
|
|
||||||
"bitstream-io",
|
|
||||||
"built",
|
|
||||||
"cfg-if",
|
|
||||||
"interpolate_name",
|
|
||||||
"itertools",
|
|
||||||
"libc",
|
|
||||||
"libfuzzer-sys",
|
|
||||||
"log",
|
|
||||||
"maybe-rayon",
|
|
||||||
"new_debug_unreachable",
|
|
||||||
"noop_proc_macro",
|
|
||||||
"num-derive",
|
|
||||||
"num-traits",
|
|
||||||
"once_cell",
|
|
||||||
"paste",
|
|
||||||
"profiling",
|
|
||||||
"rand",
|
|
||||||
"rand_chacha",
|
|
||||||
"simd_helpers",
|
|
||||||
"system-deps",
|
|
||||||
"thiserror",
|
|
||||||
"v_frame",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ravif"
|
|
||||||
version = "0.11.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234"
|
|
||||||
dependencies = [
|
|
||||||
"avif-serialize",
|
|
||||||
"imgref",
|
|
||||||
"loop9",
|
|
||||||
"quick-error",
|
|
||||||
"rav1e",
|
|
||||||
"rayon",
|
|
||||||
"rgb",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon"
|
|
||||||
version = "1.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
"rayon-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon-core"
|
|
||||||
version = "1.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "recvmsg"
|
name = "recvmsg"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
|
checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rgb"
|
|
||||||
version = "0.8.37"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ron"
|
name = "ron"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
@ -2131,12 +1673,6 @@ version = "1.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
|
@ -2205,21 +1741,6 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simd-adler32"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simd_helpers"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simplelog"
|
name = "simplelog"
|
||||||
version = "0.12.2"
|
version = "0.12.2"
|
||||||
|
@ -2256,15 +1777,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spin"
|
|
||||||
version = "0.9.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
|
@ -2347,17 +1859,6 @@ dependencies = [
|
||||||
"syn 2.0.53",
|
"syn 2.0.53",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tiff"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
|
|
||||||
dependencies = [
|
|
||||||
"flate2",
|
|
||||||
"jpeg-decoder",
|
|
||||||
"weezl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.34"
|
version = "0.3.34"
|
||||||
|
@ -2586,17 +2087,6 @@ version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "v_frame"
|
|
||||||
version = "0.3.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b"
|
|
||||||
dependencies = [
|
|
||||||
"aligned-vec",
|
|
||||||
"num-traits",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
@ -2685,12 +2175,6 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "weezl"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "widestring"
|
name = "widestring"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -2924,27 +2408,3 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zune-core"
|
|
||||||
version = "0.4.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zune-inflate"
|
|
||||||
version = "0.2.54"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
|
||||||
dependencies = [
|
|
||||||
"simd-adler32",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zune-jpeg"
|
|
||||||
version = "0.4.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448"
|
|
||||||
dependencies = [
|
|
||||||
"zune-core",
|
|
||||||
]
|
|
||||||
|
|
|
@ -27,4 +27,3 @@ tokio = { version = "1.37.0", features = ["rt-multi-thread", "time"] }
|
||||||
tokio-tungstenite = "0.21.0"
|
tokio-tungstenite = "0.21.0"
|
||||||
toml = "0.8.12"
|
toml = "0.8.12"
|
||||||
interprocess = { version = "2.0.0", features = ["tokio"] }
|
interprocess = { version = "2.0.0", features = ["tokio"] }
|
||||||
image = "0.25.1"
|
|
||||||
|
|
|
@ -1,12 +1,34 @@
|
||||||
use config::{Config, FileFormat};
|
use config::{Config, FileFormat};
|
||||||
use err_derive::Error;
|
use err_derive::Error;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use crate::ui::AppState;
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct AppConfig {
|
||||||
|
pub camera_ip: String,
|
||||||
|
pub camera_port: u32,
|
||||||
|
|
||||||
pub fn load_config() -> AppState {
|
pub tracker_ip: String,
|
||||||
|
pub tracker_port: u32,
|
||||||
|
pub tracker_refresh_rate_millis: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
AppConfig {
|
||||||
|
camera_ip: "10.0.0.33".to_string(),
|
||||||
|
camera_port: 8765,
|
||||||
|
|
||||||
|
tracker_ip: "10.0.0.210".to_string(),
|
||||||
|
tracker_port: 8765,
|
||||||
|
tracker_refresh_rate_millis: 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_config() -> AppConfig {
|
||||||
let settings = Config::builder()
|
let settings = Config::builder()
|
||||||
.add_source(config::File::new("./settings.toml", FileFormat::Toml))
|
.add_source(config::File::new("./settings.toml", FileFormat::Toml))
|
||||||
.build();
|
.build();
|
||||||
|
@ -24,7 +46,7 @@ pub enum SaveConfigError {
|
||||||
IoError(#[cause] std::io::Error),
|
IoError(#[cause] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_config(config: &AppState) -> Result<(), SaveConfigError> {
|
pub fn save_config(config: &AppConfig) -> Result<(), SaveConfigError> {
|
||||||
let toml_str = toml::to_string(&config)?;
|
let toml_str = toml::to_string(&config)?;
|
||||||
let mut file = File::create("./settings.toml")?;
|
let mut file = File::create("./settings.toml")?;
|
||||||
file.write_all(toml_str.as_bytes())?;
|
file.write_all(toml_str.as_bytes())?;
|
||||||
|
|
|
@ -13,15 +13,21 @@ use futures_util::{
|
||||||
};
|
};
|
||||||
use gstreamer::prelude::ElementExt;
|
use gstreamer::prelude::ElementExt;
|
||||||
use gstreamer::State;
|
use gstreamer::State;
|
||||||
use log::{error, info, debug};
|
use log::{error, info};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::runtime::Handle;
|
use tokio::runtime::Handle;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
|
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
|
||||||
|
|
||||||
|
mod process_box_string;
|
||||||
|
mod remote_video_processor;
|
||||||
|
use crate::config::AppConfig;
|
||||||
use crate::remote_sources::TrackerState;
|
use crate::remote_sources::TrackerState;
|
||||||
use crate::{gstreamer_pipeline, remote_sources};
|
use crate::{gstreamer_pipeline, remote_sources};
|
||||||
use crate::{joystick_source::joystick_loop, ui::GuiUpdate};
|
use crate::{joystick_source::joystick_loop, ui::GuiUpdate};
|
||||||
|
|
||||||
|
use self::remote_video_processor::remote_video_loop;
|
||||||
|
|
||||||
const PRIORITY_TIMEOUT: Duration = Duration::from_secs(2);
|
const PRIORITY_TIMEOUT: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -38,13 +44,21 @@ pub enum ConnectionType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ApplicationEvent {
|
pub enum ApplicationEvent {
|
||||||
StartSocket(String),
|
StartCameraSocket,
|
||||||
|
StartTrackerSocket,
|
||||||
SocketMessage(Message),
|
SocketMessage(Message),
|
||||||
MoveEvent(MoveEvent, ConnectionType),
|
MoveEvent(MoveEvent, ConnectionType),
|
||||||
EnableAutomatic(bool),
|
EnableAutomatic(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SocketState {
|
||||||
|
pub is_connected: Arc<AtomicBool>,
|
||||||
|
pub stay_connected: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
struct CoordState<'a> {
|
struct CoordState<'a> {
|
||||||
|
pub settings: Arc<RwLock<AppConfig>>,
|
||||||
|
|
||||||
pub sck_outbound: Option<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>>,
|
pub sck_outbound: Option<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>>,
|
||||||
pub sck_alive_server: Arc<AtomicBool>,
|
pub sck_alive_server: Arc<AtomicBool>,
|
||||||
pub sck_alive_recvr: Arc<AtomicBool>,
|
pub sck_alive_recvr: Arc<AtomicBool>,
|
||||||
|
@ -71,8 +85,11 @@ impl<'a> CoordState<'a> {
|
||||||
to_gui: Sender<GuiUpdate>,
|
to_gui: Sender<GuiUpdate>,
|
||||||
rt: Handle,
|
rt: Handle,
|
||||||
tracker_state: Arc<Mutex<TrackerState>>,
|
tracker_state: Arc<Mutex<TrackerState>>,
|
||||||
|
settings: Arc<RwLock<AppConfig>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let this = CoordState {
|
let this = CoordState {
|
||||||
|
settings,
|
||||||
|
|
||||||
sck_outbound: None,
|
sck_outbound: None,
|
||||||
sck_alive_recvr: Arc::new(AtomicBool::new(false)),
|
sck_alive_recvr: Arc::new(AtomicBool::new(false)),
|
||||||
sck_alive_server: Arc::new(AtomicBool::new(false)),
|
sck_alive_server: Arc::new(AtomicBool::new(false)),
|
||||||
|
@ -91,14 +108,6 @@ impl<'a> CoordState<'a> {
|
||||||
|
|
||||||
tracker_state,
|
tracker_state,
|
||||||
};
|
};
|
||||||
this.rt.spawn(
|
|
||||||
crate::remote_sources::shared_video_pipe::create_outbound_pipe(
|
|
||||||
this.pipeline.sink_frame.clone(),
|
|
||||||
this.to_mec.clone(),
|
|
||||||
this.keep_windows_pipe_alive.clone(),
|
|
||||||
this.tracker_state.clone(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,10 +125,16 @@ impl<'a> CoordState<'a> {
|
||||||
self.sck_outbound.is_some()
|
self.sck_outbound.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn socket_start(&mut self, conn: String) {
|
async fn socket_start(&mut self) {
|
||||||
info!("Starting socket");
|
info!("Starting socket");
|
||||||
|
|
||||||
match connect_async(conn).await {
|
let conn_string: String = {
|
||||||
|
let read_settings = self.settings.read().await;
|
||||||
|
|
||||||
|
format!("ws://{}:{}", read_settings.camera_ip, read_settings.camera_port.to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
match connect_async(conn_string).await {
|
||||||
Ok((val, _)) => {
|
Ok((val, _)) => {
|
||||||
info!("Socket connection to camera made successfully");
|
info!("Socket connection to camera made successfully");
|
||||||
|
|
||||||
|
@ -146,6 +161,26 @@ impl<'a> CoordState<'a> {
|
||||||
self.sck_alive_recvr.store(false, Ordering::SeqCst);
|
self.sck_alive_recvr.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn start_video_loop(&mut self) {
|
||||||
|
let conn_string: String = {
|
||||||
|
let read_settings = self.settings.read().await;
|
||||||
|
|
||||||
|
format!("ws://{}:{}", read_settings.tracker_ip, read_settings.tracker_port.to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
self.rt.spawn(
|
||||||
|
remote_video_loop(
|
||||||
|
conn_string,
|
||||||
|
self.pipeline.sink_frame.clone(),
|
||||||
|
self.to_mec.clone(),
|
||||||
|
self.keep_windows_pipe_alive.clone(),
|
||||||
|
self.tracker_state.clone(),
|
||||||
|
self.rt.clone()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn check_states(&mut self) {
|
pub async fn check_states(&mut self) {
|
||||||
if !self.joystick_loop_alive.load(Ordering::SeqCst) {
|
if !self.joystick_loop_alive.load(Ordering::SeqCst) {
|
||||||
info!("Restarting joystick loop");
|
info!("Restarting joystick loop");
|
||||||
|
@ -155,6 +190,10 @@ impl<'a> CoordState<'a> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.keep_windows_pipe_alive.load(Ordering::SeqCst) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if !self.sck_alive_server.load(Ordering::SeqCst) {
|
if !self.sck_alive_server.load(Ordering::SeqCst) {
|
||||||
info!("Restarting socket server");
|
info!("Restarting socket server");
|
||||||
self.sck_alive_server.store(true, Ordering::SeqCst);
|
self.sck_alive_server.store(true, Ordering::SeqCst);
|
||||||
|
@ -197,12 +236,13 @@ pub async fn start_coordinator(
|
||||||
to_gui: Sender<GuiUpdate>,
|
to_gui: Sender<GuiUpdate>,
|
||||||
runtime: Handle,
|
runtime: Handle,
|
||||||
tracker_state: Arc<Mutex<TrackerState>>,
|
tracker_state: Arc<Mutex<TrackerState>>,
|
||||||
|
settings: Arc<RwLock<AppConfig>>,
|
||||||
) {
|
) {
|
||||||
info!("Starting coordinator!");
|
info!("Starting coordinator!");
|
||||||
|
|
||||||
let mec = pin!(mec);
|
let mec = pin!(mec);
|
||||||
|
|
||||||
let mut state = CoordState::new(mec, to_mec, to_gui, runtime, tracker_state);
|
let mut state = CoordState::new(mec, to_mec, to_gui, runtime, tracker_state, settings);
|
||||||
|
|
||||||
state
|
state
|
||||||
.pipeline
|
.pipeline
|
||||||
|
@ -226,8 +266,11 @@ pub async fn start_coordinator(
|
||||||
state.check_states().await;
|
state.check_states().await;
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
ApplicationEvent::StartSocket(conn) => {
|
ApplicationEvent::StartCameraSocket => {
|
||||||
state.socket_start(conn).await;
|
state.socket_start().await;
|
||||||
|
}
|
||||||
|
ApplicationEvent::StartTrackerSocket => {
|
||||||
|
state.start_video_loop().await;
|
||||||
}
|
}
|
||||||
ApplicationEvent::SocketMessage(socket_message) => {
|
ApplicationEvent::SocketMessage(socket_message) => {
|
||||||
if let Err(e) = state.to_gui.send(GuiUpdate::SocketState(true)).await {
|
if let Err(e) = state.to_gui.send(GuiUpdate::SocketState(true)).await {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::ui::NormalizedBoxCoords;
|
|
||||||
use super::TrackerState;
|
use super::TrackerState;
|
||||||
|
use crate::ui::NormalizedBoxCoords;
|
||||||
|
|
||||||
pub fn process_incoming_string(
|
pub fn process_incoming_string(
|
||||||
message: String,
|
message: String,
|
200
src/coordinator/remote_video_processor.rs
Normal file
200
src/coordinator/remote_video_processor.rs
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
use std::{
|
||||||
|
cmp::{max, min},
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc, Mutex,
|
||||||
|
},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use async_channel::Sender;
|
||||||
|
use futures_util::{stream::SplitStream, SinkExt, StreamExt, TryStreamExt};
|
||||||
|
use gstreamer_app::AppSink;
|
||||||
|
use gstreamer_video::{video_frame::Readable, VideoFrame, VideoInfo};
|
||||||
|
use log::{error, info};
|
||||||
|
use tokio::{
|
||||||
|
net::TcpStream,
|
||||||
|
runtime::Handle,
|
||||||
|
time::{sleep_until, Instant},
|
||||||
|
};
|
||||||
|
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
|
||||||
|
|
||||||
|
use crate::remote_sources::TrackerState;
|
||||||
|
|
||||||
|
use super::{process_box_string::process_incoming_string, ApplicationEvent};
|
||||||
|
|
||||||
|
pub async fn remote_video_loop(
|
||||||
|
conn_string: String,
|
||||||
|
appsink: Arc<Mutex<AppSink>>,
|
||||||
|
to_mec: Sender<ApplicationEvent>,
|
||||||
|
keep_alive: Arc<AtomicBool>,
|
||||||
|
tracker_state: Arc<Mutex<TrackerState>>,
|
||||||
|
runtime: Handle,
|
||||||
|
) {
|
||||||
|
let video_info =
|
||||||
|
gstreamer_video::VideoInfo::builder(gstreamer_video::VideoFormat::Rgb, 640, 480)
|
||||||
|
.build()
|
||||||
|
.expect("Could not build video info!");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match connect_async(&conn_string).await {
|
||||||
|
Err(e) => {
|
||||||
|
error!("Could not connect to remote video loop! Trying again in 5 seconds: {e}");
|
||||||
|
sleep_until(Instant::now() + Duration::from_secs(5)).await;
|
||||||
|
}
|
||||||
|
Ok((connection, _)) => {
|
||||||
|
let (mut sender, recvr) = connection.split();
|
||||||
|
|
||||||
|
runtime.spawn(listen_to_messages(
|
||||||
|
recvr,
|
||||||
|
to_mec.clone(),
|
||||||
|
tracker_state.clone(),
|
||||||
|
keep_alive.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Do this in an encloser to not keep a lock on the appsink
|
||||||
|
let video_frame = match {
|
||||||
|
let appsnk = match appsink.lock() {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Unrecoverable error: Could not get a lock on the appsink in remote video loop {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
get_video_frame(&appsnk, &video_info)
|
||||||
|
} {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Could not get video frame! {e}");
|
||||||
|
if let Err(e) = sender.close().await {
|
||||||
|
error!("Could not close socket to remote computer: {e}")
|
||||||
|
}
|
||||||
|
keep_alive.store(false, Ordering::SeqCst);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = sender
|
||||||
|
.send(Message::binary(
|
||||||
|
video_frame
|
||||||
|
.plane_data(0)
|
||||||
|
.expect("Could not get video frame data"),
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("There was an error sending the video frame to the server: {e}");
|
||||||
|
if let Err(e) = sender.close().await {
|
||||||
|
error!("Could not close socket to remote computer: {e}")
|
||||||
|
}
|
||||||
|
keep_alive.store(false, Ordering::SeqCst);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rate limit updates
|
||||||
|
sleep_until(Instant::now() + Duration::from_millis(50)).await;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !keep_alive.load(Ordering::SeqCst) {
|
||||||
|
info!("Shutting down remote video loop");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn listen_to_messages(
|
||||||
|
mut recvr: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
|
||||||
|
to_mec: Sender<ApplicationEvent>,
|
||||||
|
tracker_state: Arc<Mutex<TrackerState>>,
|
||||||
|
keep_alive: Arc<AtomicBool>,
|
||||||
|
) {
|
||||||
|
while keep_alive.load(Ordering::SeqCst) {
|
||||||
|
match recvr.try_next().await {
|
||||||
|
Ok(Some(message)) => {
|
||||||
|
let (x_off, y_off, do_send) =
|
||||||
|
process_incoming_string(message.to_string(), &tracker_state)
|
||||||
|
.and_then(|_| calculate_tracking(&tracker_state))
|
||||||
|
.unwrap_or((0, 0, false));
|
||||||
|
|
||||||
|
if do_send {
|
||||||
|
if let Err(e) = to_mec
|
||||||
|
.send(ApplicationEvent::MoveEvent(
|
||||||
|
super::MoveEvent { x: x_off, y: y_off },
|
||||||
|
super::ConnectionType::Automated,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("Could not send message to MEC, assuming critical failure: {e}");
|
||||||
|
keep_alive.store(false, Ordering::SeqCst);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => info!("Recieved an empty message from the remote computer"),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Got an error on while recieving from remote computer: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_video_frame(
|
||||||
|
appsink: &AppSink,
|
||||||
|
video_info: &VideoInfo,
|
||||||
|
) -> Result<VideoFrame<Readable>, String> {
|
||||||
|
let sample = appsink
|
||||||
|
.pull_sample()
|
||||||
|
.map_err(|e| format!("Could not pull appsink sample: {e}"))?;
|
||||||
|
let buffer = sample.buffer_owned().unwrap();
|
||||||
|
gstreamer_video::VideoFrame::from_buffer_readable(buffer, video_info)
|
||||||
|
.map_err(|_| format!("Unable to make video frame from buffer!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_tracking(
|
||||||
|
tracker_state: &Arc<Mutex<TrackerState>>,
|
||||||
|
) -> core::result::Result<(i32, i32, bool), String> {
|
||||||
|
#[cfg(feature = "tracker-state-debug")]
|
||||||
|
debug!("Getting lock on tracker state for caculate tracking");
|
||||||
|
if let Ok(mut ts) = tracker_state.lock() {
|
||||||
|
// if ts.last_detect + Duration::from_secs(2) < Instant::now() && !ts.identity_boxes.is_empty() {
|
||||||
|
// info!("Setting new target: {}", ts.identity_boxes[0].id);
|
||||||
|
// ts.tracking_id = ts.identity_boxes[0].id;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if let Some(target_box) = ts.identity_boxes.iter().find(|e| e.id == ts.tracking_id) {
|
||||||
|
let x_adjust = calc_x_adjust(target_box.x1, target_box.x2);
|
||||||
|
let y_adjust = calc_y_adjust(target_box.y1);
|
||||||
|
ts.last_detect = std::time::Instant::now();
|
||||||
|
Ok((x_adjust, y_adjust, ts.enabled))
|
||||||
|
} else {
|
||||||
|
Err("Couldn't find target in results".to_string())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("Couldn't lock tracker state".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_x_adjust(x1: f32, x2: f32) -> i32 {
|
||||||
|
let dist_from_center = ((x1 + x2) / 2.0) - 0.5;
|
||||||
|
let mut x_adjust = ((dist_from_center / 0.5 * 2.0) * 100.0) as i32;
|
||||||
|
if x_adjust < 15 && x_adjust > -15 {
|
||||||
|
x_adjust = 0;
|
||||||
|
}
|
||||||
|
min(max(x_adjust, -100), 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_y_adjust(y1: f32) -> i32 {
|
||||||
|
// All values are normalized, then multiplied by 1000. 500 == 50% of the screen
|
||||||
|
let mut y_adjust = ((y1 - 0.1) * 250.0) as i32;
|
||||||
|
if y_adjust < 0 {
|
||||||
|
y_adjust -= 20;
|
||||||
|
} else if y_adjust < 30 {
|
||||||
|
y_adjust = 0;
|
||||||
|
} else {
|
||||||
|
y_adjust = (y_adjust as f32 * 0.75) as i32;
|
||||||
|
}
|
||||||
|
min(max(y_adjust, -100), 100)
|
||||||
|
}
|
28
src/main.rs
28
src/main.rs
|
@ -1,16 +1,17 @@
|
||||||
use gtk::gdk::Display;
|
|
||||||
use gtk::{glib, Application};
|
use gtk::{glib, Application};
|
||||||
use gtk::{prelude::*, CssProvider};
|
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
|
use gtk::prelude::{ApplicationExt, ApplicationExtManual};
|
||||||
use simplelog::SimpleLogger;
|
use simplelog::SimpleLogger;
|
||||||
use std::env;
|
use std::{env, sync::Arc};
|
||||||
use tokio::runtime;
|
use tokio::{runtime, sync::RwLock};
|
||||||
|
|
||||||
|
use crate::config::load_config;
|
||||||
|
|
||||||
mod config;
|
|
||||||
mod coordinator;
|
mod coordinator;
|
||||||
mod gstreamer_pipeline;
|
mod gstreamer_pipeline;
|
||||||
mod joystick_source;
|
mod joystick_source;
|
||||||
mod remote_sources;
|
mod remote_sources;
|
||||||
|
mod config;
|
||||||
mod ui;
|
mod ui;
|
||||||
const APP_ID: &str = "net.nickiel.joystick-controller-client";
|
const APP_ID: &str = "net.nickiel.joystick-controller-client";
|
||||||
|
|
||||||
|
@ -23,8 +24,9 @@ fn main() -> glib::ExitCode {
|
||||||
error!("Failed to init the simplelogger!: {e}");
|
error!("Failed to init the simplelogger!: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
gstreamer::init().expect("Unable to start gstreamer");
|
let config = Arc::new(RwLock::new(load_config()));
|
||||||
|
|
||||||
|
gstreamer::init().expect("Unable to start gstreamer");
|
||||||
gstgtk4::plugin_register_static().expect("Unable to register gtk4 plugin");
|
gstgtk4::plugin_register_static().expect("Unable to register gtk4 plugin");
|
||||||
|
|
||||||
let rt = runtime::Runtime::new().expect("Could not start tokio runtime");
|
let rt = runtime::Runtime::new().expect("Could not start tokio runtime");
|
||||||
|
@ -32,10 +34,10 @@ fn main() -> glib::ExitCode {
|
||||||
|
|
||||||
let app = Application::builder().application_id(APP_ID).build();
|
let app = Application::builder().application_id(APP_ID).build();
|
||||||
|
|
||||||
app.connect_startup(|_| load_css());
|
app.connect_startup(ui::on_activate);
|
||||||
|
|
||||||
app.connect_activate(move |app| {
|
app.connect_activate(move |app| {
|
||||||
ui::build_ui(app, handle.clone());
|
ui::build_ui(app, config.clone(), handle.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
let exit_code = app.run();
|
let exit_code = app.run();
|
||||||
|
@ -49,13 +51,3 @@ fn main() -> glib::ExitCode {
|
||||||
exit_code
|
exit_code
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_css() {
|
|
||||||
let provider = CssProvider::new();
|
|
||||||
provider.load_from_string(include_str!("../style.css"));
|
|
||||||
|
|
||||||
gtk::style_context_add_provider_for_display(
|
|
||||||
&Display::default().expect("Could not connect to a display"),
|
|
||||||
&provider,
|
|
||||||
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -20,11 +20,12 @@ use tokio_tungstenite::{
|
||||||
tungstenite::{Error, Message, Result},
|
tungstenite::{Error, Message, Result},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod process_box_string;
|
|
||||||
mod remote_source;
|
mod remote_source;
|
||||||
pub mod shared_video_pipe;
|
|
||||||
|
|
||||||
use crate::{coordinator::{ApplicationEvent, ConnectionType}, ui::NormalizedBoxCoords};
|
use crate::{
|
||||||
|
coordinator::{ApplicationEvent, ConnectionType},
|
||||||
|
ui::NormalizedBoxCoords,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct TrackerState {
|
pub struct TrackerState {
|
||||||
pub tracking_id: u32,
|
pub tracking_id: u32,
|
||||||
|
|
|
@ -1,217 +0,0 @@
|
||||||
use std::{
|
|
||||||
cmp::{max, min},
|
|
||||||
sync::{atomic::AtomicBool, Arc, Mutex},
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use async_channel::Sender;
|
|
||||||
use gstreamer_app::AppSink;
|
|
||||||
use gstreamer_video::{video_frame::Readable, VideoFrame, VideoInfo};
|
|
||||||
use interprocess::os::windows::named_pipe::{
|
|
||||||
pipe_mode::{self, Bytes},
|
|
||||||
tokio::{DuplexPipeStream, PipeStream},
|
|
||||||
};
|
|
||||||
use log::{error, info, debug};
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
coordinator::{ApplicationEvent, MoveEvent},
|
|
||||||
remote_sources::process_box_string,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::TrackerState;
|
|
||||||
|
|
||||||
struct LoopState {
|
|
||||||
pub appsink: Arc<Mutex<AppSink>>,
|
|
||||||
pub mec: Sender<ApplicationEvent>,
|
|
||||||
pub tracker_state: Arc<Mutex<TrackerState>>,
|
|
||||||
|
|
||||||
pub pipe: PipeStream<Bytes, Bytes>,
|
|
||||||
|
|
||||||
pub video_info: VideoInfo,
|
|
||||||
pub byte_buffer: Vec<u8>,
|
|
||||||
pub len_buf: [u8; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LoopState {
|
|
||||||
fn get_video_frame(&mut self) -> Result<VideoFrame<Readable>, String> {
|
|
||||||
let sample = self
|
|
||||||
.appsink
|
|
||||||
.lock()
|
|
||||||
.map_err(|e| format!("Could not get a lock on the appsink: {e}"))?
|
|
||||||
.pull_sample()
|
|
||||||
.map_err(|e| format!("Could not pull appsink sample: {e}"))?;
|
|
||||||
let buffer = sample.buffer_owned().unwrap();
|
|
||||||
gstreamer_video::VideoFrame::from_buffer_readable(buffer, &self.video_info)
|
|
||||||
.map_err(|_| format!("Unable to make video frame from buffer!"))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_return_message(&mut self) -> Result<String, String> {
|
|
||||||
// Read message size from the pipe
|
|
||||||
if let Err(e) = self.pipe.read_exact(&mut self.len_buf).await {
|
|
||||||
return Err(format!(
|
|
||||||
"Couldn't read message length from the windows pipe: {e}"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let length = u32::from_le_bytes(self.len_buf);
|
|
||||||
self.byte_buffer.resize(length as usize, 0);
|
|
||||||
// Read the message of message length from the pipe
|
|
||||||
if let Err(e) = self.pipe.read_exact(&mut self.byte_buffer).await {
|
|
||||||
return Err(format!(
|
|
||||||
"Couldn't read the message from the windows pipe: {e}"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(String::from_utf8_lossy(&self.byte_buffer).to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_outbound_pipe(
|
|
||||||
appsink: Arc<Mutex<AppSink>>,
|
|
||||||
mec: Sender<ApplicationEvent>,
|
|
||||||
keep_alive: Arc<AtomicBool>,
|
|
||||||
tracker_state: Arc<Mutex<TrackerState>>,
|
|
||||||
) {
|
|
||||||
if let Ok(pipe) =
|
|
||||||
DuplexPipeStream::<pipe_mode::Bytes>::connect_by_path(r"\\.\pipe\example_pipe").await
|
|
||||||
{
|
|
||||||
let mut state = LoopState {
|
|
||||||
appsink,
|
|
||||||
mec,
|
|
||||||
tracker_state,
|
|
||||||
|
|
||||||
pipe,
|
|
||||||
|
|
||||||
video_info: gstreamer_video::VideoInfo::builder(
|
|
||||||
gstreamer_video::VideoFormat::Rgb,
|
|
||||||
640,
|
|
||||||
480,
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
.expect("Couldn't build video info!"),
|
|
||||||
byte_buffer: Vec::new(),
|
|
||||||
len_buf: [0; 4],
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut prev_tick = Instant::now();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if !keep_alive.load(std::sync::atomic::Ordering::SeqCst) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if prev_tick + Duration::from_millis(30) < Instant::now() {
|
|
||||||
tokio::time::sleep(prev_tick + Duration::from_millis(30) - Instant::now()).await;
|
|
||||||
}
|
|
||||||
prev_tick = Instant::now();
|
|
||||||
|
|
||||||
state.len_buf = [0; 4];
|
|
||||||
|
|
||||||
let video_frame = match state.get_video_frame() {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(e) => {
|
|
||||||
error!("{}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send video frame to pipe
|
|
||||||
if let Err(e) = send_to_pipe(&mut state.pipe, video_frame.plane_data(0).unwrap()).await
|
|
||||||
{
|
|
||||||
error!("Error in sending to the pipe: {e}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let message = match state.read_return_message().await {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(e) => {
|
|
||||||
error!("{}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let x_off: i32;
|
|
||||||
let y_off: i32;
|
|
||||||
|
|
||||||
// Load the tracking boxes into identity_boxes, then do the adjustment calcuations on the updated tracking info (side-effects)
|
|
||||||
(x_off, y_off) = process_box_string::process_incoming_string(
|
|
||||||
message.to_string(),
|
|
||||||
&state.tracker_state,
|
|
||||||
)
|
|
||||||
.and_then(|_| calculate_tracking(&state.tracker_state))
|
|
||||||
.unwrap_or((0, 0));
|
|
||||||
|
|
||||||
if let Err(e) = state
|
|
||||||
.mec
|
|
||||||
.send(ApplicationEvent::MoveEvent(
|
|
||||||
MoveEvent { x: x_off, y: y_off },
|
|
||||||
crate::coordinator::ConnectionType::Automated,
|
|
||||||
))
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
error!("MEC Unavailable, closing the connection on the pipe: {e}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Windows pipe has been shut down");
|
|
||||||
|
|
||||||
keep_alive.store(false, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
if let Err(e) = state.pipe.shutdown().await {
|
|
||||||
error!("Couldn't shut down pipe: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_to_pipe<'a>(
|
|
||||||
pipe: &mut PipeStream<pipe_mode::Bytes, pipe_mode::Bytes>,
|
|
||||||
message: &'a [u8],
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
pipe.write_all(message).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_tracking(
|
|
||||||
tracker_state: &Arc<Mutex<TrackerState>>,
|
|
||||||
) -> core::result::Result<(i32, i32), String> {
|
|
||||||
#[cfg(feature = "tracker-state-debug")]
|
|
||||||
debug!("Getting lock on tracker state for caculate tracking");
|
|
||||||
if let Ok(mut ts) = tracker_state.lock() {
|
|
||||||
// if ts.last_detect + Duration::from_secs(2) < Instant::now() && !ts.identity_boxes.is_empty() {
|
|
||||||
// info!("Setting new target: {}", ts.identity_boxes[0].id);
|
|
||||||
// ts.tracking_id = ts.identity_boxes[0].id;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if let Some(target_box) = ts.identity_boxes.iter().find(|e| e.id == ts.tracking_id) {
|
|
||||||
let x_adjust = calc_x_adjust(target_box.x1, target_box.x2);
|
|
||||||
let y_adjust = calc_y_adjust(target_box.y1);
|
|
||||||
ts.last_detect = Instant::now();
|
|
||||||
Ok((x_adjust, y_adjust))
|
|
||||||
} else {
|
|
||||||
Err("Couldn't find target in results".to_string())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err("Couldn't lock tracker state".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calc_x_adjust(x1: f32, x2: f32) -> i32 {
|
|
||||||
let dist_from_center = ((x1 + x2) / 2.0) - 0.5;
|
|
||||||
let mut x_adjust = ((dist_from_center / 0.5 * 2.0) * 100.0) as i32;
|
|
||||||
if x_adjust < 15 && x_adjust > -15 {
|
|
||||||
x_adjust = 0;
|
|
||||||
}
|
|
||||||
min(max(x_adjust, -100), 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calc_y_adjust(y1: f32) -> i32 {
|
|
||||||
// All values are normalized, then multiplied by 1000. 500 == 50% of the screen
|
|
||||||
let mut y_adjust = ((y1 - 0.1) * 250.0) as i32;
|
|
||||||
if y_adjust < 0 {
|
|
||||||
y_adjust -= 20;
|
|
||||||
} else if y_adjust < 30 {
|
|
||||||
y_adjust = 0;
|
|
||||||
} else {
|
|
||||||
y_adjust = (y_adjust as f32 * 0.75) as i32;
|
|
||||||
}
|
|
||||||
min(max(y_adjust, -100), 100)
|
|
||||||
}
|
|
149
src/ui/mod.rs
149
src/ui/mod.rs
|
@ -2,37 +2,25 @@ use std::sync::{Arc, Mutex};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use gtk::cairo::Context;
|
use gtk::cairo::Context;
|
||||||
use gtk::gdk::Paintable;
|
use gtk::gdk::{Display, Paintable};
|
||||||
use gtk::{glib, prelude::*, AspectFrame, Label, ListBox};
|
use gtk::glib::clone;
|
||||||
|
use gtk::{gio, glib, prelude::*, AspectFrame, CssProvider, Label, ListBox};
|
||||||
use gtk::{Application, ApplicationWindow};
|
use gtk::{Application, ApplicationWindow};
|
||||||
use log::{error, info, debug};
|
use log::{error, info};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tokio::runtime::Handle;
|
use tokio::runtime::Handle;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use crate::config::load_config;
|
use crate::config::AppConfig;
|
||||||
use crate::coordinator::{start_coordinator, ApplicationEvent, MoveEvent};
|
use crate::coordinator::{start_coordinator, ApplicationEvent, MoveEvent};
|
||||||
use crate::remote_sources::TrackerState;
|
use crate::remote_sources::TrackerState;
|
||||||
|
|
||||||
mod socket_panel;
|
mod socket_panel;
|
||||||
mod tracker_panel;
|
mod tracker_panel;
|
||||||
|
mod settings_modal;
|
||||||
|
|
||||||
use socket_panel::SocketPanel;
|
use socket_panel::SocketPanel;
|
||||||
use tracker_panel::TrackerPanel;
|
use tracker_panel::TrackerPanel;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct AppState {
|
|
||||||
pub ip: String,
|
|
||||||
pub port: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AppState {
|
|
||||||
fn default() -> Self {
|
|
||||||
AppState {
|
|
||||||
ip: "10.0.0.30".to_string(),
|
|
||||||
port: 8765,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum GuiUpdate {
|
pub enum GuiUpdate {
|
||||||
SocketState(bool),
|
SocketState(bool),
|
||||||
|
@ -68,8 +56,38 @@ impl NormalizedBoxCoords {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_ui(app: &Application, runtime: Handle) {
|
pub fn on_activate(app: &Application) {
|
||||||
let initial_settings = load_config();
|
let provider = CssProvider::new();
|
||||||
|
provider.load_from_string(include_str!("../../style.css"));
|
||||||
|
|
||||||
|
gtk::style_context_add_provider_for_display(
|
||||||
|
&Display::default().expect("Could not connect to a display"),
|
||||||
|
&provider,
|
||||||
|
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||||
|
);
|
||||||
|
|
||||||
|
let menubar = {
|
||||||
|
let connections_menu = {
|
||||||
|
let settings = gio::MenuItem::new(Some("Edit IPs"), Some("app.connections"));
|
||||||
|
|
||||||
|
let connections_menu = gio::Menu::new();
|
||||||
|
connections_menu.append_item(&settings);
|
||||||
|
|
||||||
|
connections_menu
|
||||||
|
};
|
||||||
|
|
||||||
|
let menubar = gio::Menu::new();
|
||||||
|
menubar.append_submenu(Some("Connections"), &connections_menu);
|
||||||
|
|
||||||
|
menubar
|
||||||
|
};
|
||||||
|
|
||||||
|
app.set_menubar(Some(&menubar));
|
||||||
|
info!("Menu bar set up");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Handle) {
|
||||||
let main_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
let main_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||||
let left_box = ListBox::builder().width_request(200).build();
|
let left_box = ListBox::builder().width_request(200).build();
|
||||||
|
|
||||||
|
@ -77,11 +95,26 @@ pub fn build_ui(app: &Application, runtime: Handle) {
|
||||||
let window = ApplicationWindow::builder()
|
let window = ApplicationWindow::builder()
|
||||||
.application(app)
|
.application(app)
|
||||||
.title("VCC Camera Controller")
|
.title("VCC Camera Controller")
|
||||||
|
.show_menubar(true)
|
||||||
.default_width(840)
|
.default_width(840)
|
||||||
.default_height(480)
|
.default_height(480)
|
||||||
.child(&main_box)
|
.child(&main_box)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
let rt = runtime.clone();
|
||||||
|
let config_modal_config = config.clone();
|
||||||
|
let connections_activate = gio::ActionEntry::builder("connections")
|
||||||
|
.activate(clone!(@weak window => move |app: >k::Application, _, _| {
|
||||||
|
let connections_modal = settings_modal::ConnectionsModal::new(&app, &window, &rt, &config_modal_config);
|
||||||
|
|
||||||
|
connections_modal.window.present();
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
app.add_action_entries([connections_activate]);
|
||||||
|
|
||||||
|
|
||||||
// Main Event Channel
|
// Main Event Channel
|
||||||
let (to_mec, mec) = async_channel::unbounded::<ApplicationEvent>();
|
let (to_mec, mec) = async_channel::unbounded::<ApplicationEvent>();
|
||||||
let (to_gui, gui_recv) = async_channel::bounded::<GuiUpdate>(10);
|
let (to_gui, gui_recv) = async_channel::bounded::<GuiUpdate>(10);
|
||||||
|
@ -94,12 +127,15 @@ pub fn build_ui(app: &Application, runtime: Handle) {
|
||||||
update_ids: false,
|
update_ids: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
let coord_config = config.clone();
|
||||||
|
|
||||||
runtime.spawn(start_coordinator(
|
runtime.spawn(start_coordinator(
|
||||||
mec,
|
mec,
|
||||||
to_mec.clone(),
|
to_mec.clone(),
|
||||||
to_gui,
|
to_gui,
|
||||||
runtime.clone(),
|
runtime.clone(),
|
||||||
tracker_state.clone(),
|
tracker_state.clone(),
|
||||||
|
coord_config,
|
||||||
));
|
));
|
||||||
|
|
||||||
// let conn_status_label = Label::new(Some(&"No Connection".to_string()));
|
// let conn_status_label = Label::new(Some(&"No Connection".to_string()));
|
||||||
|
@ -110,7 +146,7 @@ pub fn build_ui(app: &Application, runtime: Handle) {
|
||||||
|
|
||||||
let tabpanel = gtk::Notebook::builder().enable_popup(true).build();
|
let tabpanel = gtk::Notebook::builder().enable_popup(true).build();
|
||||||
|
|
||||||
let socket_panel = Arc::new(SocketPanel::new(&initial_settings));
|
let socket_panel = Arc::new(SocketPanel::new());
|
||||||
socket_panel.connect_button_callback(to_mec.clone());
|
socket_panel.connect_button_callback(to_mec.clone());
|
||||||
tabpanel.append_page(
|
tabpanel.append_page(
|
||||||
socket_panel.get_top_level(),
|
socket_panel.get_top_level(),
|
||||||
|
@ -146,17 +182,11 @@ pub fn build_ui(app: &Application, runtime: Handle) {
|
||||||
.build();
|
.build();
|
||||||
main_box.append(&aspect);
|
main_box.append(&aspect);
|
||||||
|
|
||||||
let drawable = gtk::DrawingArea::builder()
|
let drawable = gtk::DrawingArea::builder().build();
|
||||||
.build();
|
|
||||||
|
|
||||||
let drawable_ts = tracker_state.clone();
|
let drawable_ts = tracker_state.clone();
|
||||||
drawable.set_draw_func(move |_, ctx, width, height| {
|
drawable.set_draw_func(move |_, ctx, width, height| {
|
||||||
draw_boxes(
|
draw_boxes(width, height, &ctx, &drawable_ts);
|
||||||
width,
|
|
||||||
height,
|
|
||||||
&ctx,
|
|
||||||
&drawable_ts,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
overlay_box.set_child(Some(&webcam_picture));
|
overlay_box.set_child(Some(&webcam_picture));
|
||||||
|
@ -165,36 +195,38 @@ pub fn build_ui(app: &Application, runtime: Handle) {
|
||||||
let items = tracker_panel.items.clone();
|
let items = tracker_panel.items.clone();
|
||||||
let id_label = tracker_panel.current_id.clone();
|
let id_label = tracker_panel.current_id.clone();
|
||||||
|
|
||||||
glib::timeout_add_seconds_local(1,
|
glib::timeout_add_seconds_local(
|
||||||
|
1,
|
||||||
glib::clone!(@strong items, @strong id_label => move || {
|
glib::clone!(@strong items, @strong id_label => move || {
|
||||||
#[cfg(feature = "tracker-state-debug")]
|
#[cfg(feature = "tracker-state-debug")]
|
||||||
debug!("Getting lock on tracker state for checking identity boxes");
|
debug!("Getting lock on tracker state for checking identity boxes");
|
||||||
|
|
||||||
// don't update the stringlist until after letting go of the tracker state
|
// don't update the stringlist until after letting go of the tracker state
|
||||||
// due to async interweaving causing a mutex deadlock
|
// due to async interweaving causing a mutex deadlock
|
||||||
let mut ids: Option<Vec<String>> = None;
|
let mut ids: Option<Vec<String>> = None;
|
||||||
let mut current_id: Option<u32> = None;
|
let mut current_id: Option<u32> = None;
|
||||||
if let Ok(ts) = tracker_state.lock() {
|
if let Ok(ts) = tracker_state.lock() {
|
||||||
current_id = Some(ts.tracking_id);
|
current_id = Some(ts.tracking_id);
|
||||||
if ts.update_ids {
|
if ts.update_ids {
|
||||||
ids = Some(ts.identity_boxes
|
ids = Some(ts.identity_boxes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| t.id.to_string())
|
.map(|t| t.id.to_string())
|
||||||
.collect());
|
.collect());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(id) = current_id {
|
if let Some(id) = current_id {
|
||||||
id_label.set_text(id.to_string().as_str());
|
id_label.set_text(id.to_string().as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some (ids) = ids {
|
if let Some (ids) = ids {
|
||||||
let old_len = items.n_items();
|
let old_len = items.n_items();
|
||||||
items.splice(0, old_len, &ids.iter().map(|x| x.as_str()).collect::<Vec<&str>>()[0..])
|
items.splice(0, old_len, &ids.iter().map(|x| x.as_str()).collect::<Vec<&str>>()[0..])
|
||||||
}
|
}
|
||||||
|
|
||||||
glib::ControlFlow::Continue
|
glib::ControlFlow::Continue
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
glib::spawn_future_local(
|
glib::spawn_future_local(
|
||||||
glib::clone!(@weak axis_label, @weak conn_status_label, @weak tabpanel, @strong socket_panel, @strong gui_recv, @weak drawable => async move {
|
glib::clone!(@weak axis_label, @weak conn_status_label, @weak tabpanel, @strong socket_panel, @strong gui_recv, @weak drawable => async move {
|
||||||
|
@ -209,11 +241,9 @@ pub fn build_ui(app: &Application, runtime: Handle) {
|
||||||
GuiUpdate::SocketState(v) => {
|
GuiUpdate::SocketState(v) => {
|
||||||
let label = {
|
let label = {
|
||||||
if v {
|
if v {
|
||||||
socket_panel.set_sensitive(false);
|
|
||||||
// tabpanel.set_show_tabs(true);
|
// tabpanel.set_show_tabs(true);
|
||||||
"Currently Connected"
|
"Currently Connected"
|
||||||
} else {
|
} else {
|
||||||
socket_panel.set_sensitive(true);
|
|
||||||
// tabpanel.set_page(0);
|
// tabpanel.set_page(0);
|
||||||
// tabpanel.set_show_tabs(false);
|
// tabpanel.set_show_tabs(false);
|
||||||
"Currently Disconnected"
|
"Currently Disconnected"
|
||||||
|
@ -246,12 +276,7 @@ pub fn build_ui(app: &Application, runtime: Handle) {
|
||||||
window.present();
|
window.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_boxes(
|
fn draw_boxes(width: i32, height: i32, ctx: &Context, tracker_state: &Arc<Mutex<TrackerState>>) {
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
ctx: &Context,
|
|
||||||
tracker_state: &Arc<Mutex<TrackerState>>,
|
|
||||||
) {
|
|
||||||
ctx.set_line_width(5.0);
|
ctx.set_line_width(5.0);
|
||||||
ctx.select_font_face(
|
ctx.select_font_face(
|
||||||
"Arial",
|
"Arial",
|
||||||
|
|
157
src/ui/settings_modal.rs
Normal file
157
src/ui/settings_modal.rs
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use gtk::glib::{self, clone};
|
||||||
|
use gtk::{prelude::{BoxExt, ButtonExt, EditableExt}, Application, ApplicationWindow, Box, Button, Entry, Label, Window};
|
||||||
|
use tokio::runtime::Handle;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
use log::{error, info};
|
||||||
|
|
||||||
|
use crate::config::{save_config, AppConfig};
|
||||||
|
|
||||||
|
|
||||||
|
pub struct ConnectionsModal {
|
||||||
|
pub window: Window,
|
||||||
|
|
||||||
|
pub top_level: Box,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectionsModal {
|
||||||
|
pub fn new(app: &Application, parent: &ApplicationWindow, rt: &Handle, app_config: &Arc<RwLock<AppConfig>>) -> Self {
|
||||||
|
|
||||||
|
// Send help :(
|
||||||
|
let config_read = rt.block_on(async {app_config.read().await});
|
||||||
|
|
||||||
|
let main_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||||
|
|
||||||
|
let window = Window::builder()
|
||||||
|
.application(app)
|
||||||
|
.title("Change Connection Information")
|
||||||
|
.modal(true)
|
||||||
|
.transient_for(parent)
|
||||||
|
.destroy_with_parent(true)
|
||||||
|
.resizable(false)
|
||||||
|
.child(&main_box)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let camera_label = Label::builder()
|
||||||
|
.label("Camera Connection")
|
||||||
|
.margin_end(5)
|
||||||
|
.margin_start(5)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let camera_ip_entry = Entry::builder()
|
||||||
|
.placeholder_text("IP Address")
|
||||||
|
.text(config_read.camera_ip.clone())
|
||||||
|
.margin_top(5)
|
||||||
|
.margin_end(5)
|
||||||
|
.margin_start(5)
|
||||||
|
.margin_bottom(5)
|
||||||
|
.can_focus(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let camera_port_entry = Entry::builder()
|
||||||
|
.placeholder_text("Port")
|
||||||
|
.text(config_read.camera_port.to_string())
|
||||||
|
.margin_top(5)
|
||||||
|
.margin_end(5)
|
||||||
|
.margin_start(5)
|
||||||
|
.margin_bottom(5)
|
||||||
|
.can_focus(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let tracker_label = Label::builder()
|
||||||
|
.label("Tracker Connection")
|
||||||
|
.margin_end(5)
|
||||||
|
.margin_start(5)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let tracker_ip_entry = Entry::builder()
|
||||||
|
.placeholder_text("IP Address")
|
||||||
|
.text(config_read.tracker_ip.clone())
|
||||||
|
.margin_top(5)
|
||||||
|
.margin_end(5)
|
||||||
|
.margin_start(5)
|
||||||
|
.margin_bottom(5)
|
||||||
|
.can_focus(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let tracker_port_entry = Entry::builder()
|
||||||
|
.placeholder_text("Port")
|
||||||
|
.text(config_read.tracker_port.to_string())
|
||||||
|
.margin_top(5)
|
||||||
|
.margin_end(5)
|
||||||
|
.margin_start(5)
|
||||||
|
.margin_bottom(5)
|
||||||
|
.can_focus(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let tracker_refresh_millis = Entry::builder()
|
||||||
|
.placeholder_text("Refresh timeout")
|
||||||
|
.text(config_read.tracker_refresh_rate_millis.to_string())
|
||||||
|
.margin_top(5)
|
||||||
|
.margin_end(5)
|
||||||
|
.margin_start(5)
|
||||||
|
.margin_bottom(5)
|
||||||
|
.can_focus(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let quit_button = Button::builder()
|
||||||
|
.label("Save")
|
||||||
|
// .action_name("window.close")
|
||||||
|
.margin_top(5)
|
||||||
|
.margin_end(5)
|
||||||
|
.margin_start(5)
|
||||||
|
.margin_bottom(5)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
main_box.append(&camera_label);
|
||||||
|
main_box.append(&camera_ip_entry);
|
||||||
|
main_box.append(&camera_port_entry);
|
||||||
|
main_box.append(&tracker_label);
|
||||||
|
main_box.append(&tracker_ip_entry);
|
||||||
|
main_box.append(&tracker_port_entry);
|
||||||
|
main_box.append(&tracker_refresh_millis);
|
||||||
|
main_box.append(&quit_button);
|
||||||
|
|
||||||
|
let new_ref = app_config.clone();
|
||||||
|
quit_button
|
||||||
|
.connect_activate(clone!(
|
||||||
|
@strong rt,
|
||||||
|
@weak camera_ip_entry, @weak camera_port_entry,
|
||||||
|
@weak tracker_ip_entry, @weak tracker_port_entry,
|
||||||
|
@weak tracker_refresh_millis => move |_| {
|
||||||
|
|
||||||
|
let new_camera_ip = camera_ip_entry.text().to_string();
|
||||||
|
let new_camera_port = camera_port_entry.text().parse::<u32>().unwrap();
|
||||||
|
|
||||||
|
let new_tracker_ip = tracker_ip_entry.text().to_string();
|
||||||
|
let new_tracker_port = tracker_port_entry.text().parse::<u32>().unwrap();
|
||||||
|
let new_tracker_millis = tracker_refresh_millis.text().parse::<u32>().unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
// maybe just send the police.
|
||||||
|
let mut write_lock = rt.block_on(async {new_ref.write().await});
|
||||||
|
|
||||||
|
write_lock.camera_ip = new_camera_ip;
|
||||||
|
write_lock.camera_port = new_camera_port;
|
||||||
|
|
||||||
|
write_lock.tracker_ip = new_tracker_ip;
|
||||||
|
write_lock.tracker_port = new_tracker_port;
|
||||||
|
write_lock.tracker_refresh_rate_millis = new_tracker_millis;
|
||||||
|
// why does this feel like the borrow checker gave me a pass?
|
||||||
|
if let Err(e) = save_config(&write_lock.to_owned()) {
|
||||||
|
error!("Could not save config! {e}");
|
||||||
|
}
|
||||||
|
// FBI!!! OPEN UP!!!!
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Please nicholas, add a non-crashing parse");
|
||||||
|
}));
|
||||||
|
|
||||||
|
ConnectionsModal {
|
||||||
|
window,
|
||||||
|
|
||||||
|
top_level: main_box,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,19 @@
|
||||||
use async_channel::Sender;
|
use async_channel::Sender;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib,
|
glib, prelude::{BoxExt, ButtonExt}, Box, Button
|
||||||
prelude::{BoxExt, ButtonExt, EditableExt, WidgetExt},
|
|
||||||
Box, Button, Entry,
|
|
||||||
};
|
};
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
||||||
use crate::{config::save_config, coordinator::ApplicationEvent};
|
use crate::coordinator::ApplicationEvent;
|
||||||
|
|
||||||
use super::AppState;
|
|
||||||
|
|
||||||
pub struct SocketPanel {
|
pub struct SocketPanel {
|
||||||
top_level: Box,
|
top_level: Box,
|
||||||
|
|
||||||
ip_entry: Entry,
|
|
||||||
port_entry: Entry,
|
|
||||||
connect_button: Button,
|
connect_button: Button,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SocketPanel {
|
impl SocketPanel {
|
||||||
pub fn new(initial_settings: &crate::ui::AppState) -> SocketPanel {
|
pub fn new() -> SocketPanel {
|
||||||
let content_box = Box::builder()
|
let content_box = Box::builder()
|
||||||
.orientation(gtk::Orientation::Vertical)
|
.orientation(gtk::Orientation::Vertical)
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
|
@ -29,26 +23,12 @@ impl SocketPanel {
|
||||||
.margin_bottom(12)
|
.margin_bottom(12)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let ip_entry = Entry::builder()
|
|
||||||
.placeholder_text("IP Address")
|
|
||||||
.text(&initial_settings.ip)
|
|
||||||
.can_focus(true)
|
|
||||||
.build();
|
|
||||||
let port_entry = Entry::builder()
|
|
||||||
.placeholder_text("Port")
|
|
||||||
.text(&initial_settings.port.to_string())
|
|
||||||
.can_focus(true)
|
|
||||||
.build();
|
|
||||||
let button = Button::builder().margin_top(12).build();
|
let button = Button::builder().margin_top(12).build();
|
||||||
|
|
||||||
content_box.append(&ip_entry);
|
|
||||||
content_box.append(&port_entry);
|
|
||||||
content_box.append(&button);
|
content_box.append(&button);
|
||||||
|
|
||||||
SocketPanel {
|
SocketPanel {
|
||||||
top_level: content_box,
|
top_level: content_box,
|
||||||
ip_entry,
|
|
||||||
port_entry,
|
|
||||||
connect_button: button,
|
connect_button: button,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,42 +37,19 @@ impl SocketPanel {
|
||||||
&self.top_level
|
&self.top_level
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_sensitive(&self, is_sensitive: bool) {
|
|
||||||
self.ip_entry.set_sensitive(is_sensitive);
|
|
||||||
self.port_entry.set_sensitive(is_sensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn button_label(&self, new_label: &str) {
|
pub fn button_label(&self, new_label: &str) {
|
||||||
self.connect_button.set_label(new_label);
|
self.connect_button.set_label(new_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect_button_callback(&self, to_mec: Sender<ApplicationEvent>) {
|
pub fn connect_button_callback(&self, to_mec: Sender<ApplicationEvent>) {
|
||||||
let ip_entry = &self.ip_entry;
|
|
||||||
let port_entry = &self.port_entry;
|
|
||||||
|
|
||||||
self.connect_button.connect_clicked(glib::clone!(@weak ip_entry, @weak port_entry, @strong to_mec => move |_button| {
|
self.connect_button.connect_clicked(glib::clone!(@strong to_mec => move |_button| {
|
||||||
let ip_text = ip_entry.text();
|
match to_mec.try_send(ApplicationEvent::StartCameraSocket) {
|
||||||
let port_text = port_entry.text();
|
|
||||||
|
|
||||||
if ip_text.len() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let Ok(val) = port_text.parse::<u32>() {
|
|
||||||
if let Err(error) = save_config(&AppState {
|
|
||||||
ip: ip_text.to_string(),
|
|
||||||
port: val
|
|
||||||
}) {
|
|
||||||
error!("{error}");
|
|
||||||
};
|
|
||||||
|
|
||||||
match to_mec.try_send(ApplicationEvent::StartSocket(
|
|
||||||
format!("ws://{}:{}", ip_text, val),
|
|
||||||
)) {
|
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(async_channel::TrySendError::Closed(_)) => panic!("Coordinator MEC is closed. Unrecoverable error."),
|
Err(async_channel::TrySendError::Closed(_)) => panic!("Coordinator MEC is closed. Unrecoverable error."),
|
||||||
Err(e) => error!("There was an error sending to the MEC: {}", e),
|
Err(e) => error!("There was an error sending to the MEC: {}", e),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,13 @@ use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use async_channel::Sender;
|
use async_channel::Sender;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::object::CastNone, prelude::{
|
glib::object::CastNone,
|
||||||
BoxExt, ButtonExt, Cast, GObjectPropertyExpressionExt, ListItemExt, ListModelExt, ToggleButtonExt
|
prelude::{
|
||||||
}, Box, Label, ListItem, ListView, ScrolledWindow, SignalListItemFactory, SingleSelection, StringList, StringObject, ToggleButton, Widget
|
BoxExt, ButtonExt, Cast, GObjectPropertyExpressionExt, ListItemExt, ListModelExt,
|
||||||
|
ToggleButtonExt,
|
||||||
|
},
|
||||||
|
Box, Label, ListItem, ListView, ScrolledWindow, SignalListItemFactory, SingleSelection,
|
||||||
|
StringList, StringObject, ToggleButton, Widget,
|
||||||
};
|
};
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
|
|
||||||
|
@ -47,9 +51,7 @@ impl TrackerPanel {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
model.connect_selected_item_notify(move |x| {
|
model.connect_selected_item_notify(move |x| {
|
||||||
let item = x
|
let item = x.selected_item().and_downcast::<StringObject>();
|
||||||
.selected_item()
|
|
||||||
.and_downcast::<StringObject>();
|
|
||||||
|
|
||||||
if let Some(item) = item {
|
if let Some(item) = item {
|
||||||
if let Ok(id) = item.string().parse::<u32>() {
|
if let Ok(id) = item.string().parse::<u32>() {
|
||||||
|
@ -108,13 +110,11 @@ impl TrackerPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn get_top_level(&self) -> &Box {
|
pub fn get_top_level(&self) -> &Box {
|
||||||
&self.top_level
|
&self.top_level
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect_button_callback(&self, to_mec: Sender<ApplicationEvent>) {
|
pub fn connect_button_callback(&self, to_mec: Sender<ApplicationEvent>) {
|
||||||
|
|
||||||
self.enable_disable.connect_clicked(move |button| {
|
self.enable_disable.connect_clicked(move |button| {
|
||||||
if let Err(e) =
|
if let Err(e) =
|
||||||
to_mec.send_blocking(ApplicationEvent::EnableAutomatic(button.is_active()))
|
to_mec.send_blocking(ApplicationEvent::EnableAutomatic(button.is_active()))
|
||||||
|
|
|
@ -2,6 +2,10 @@ scrolledwindow > listview {
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menuitem > * {
|
||||||
|
font-size: 16pt;
|
||||||
|
}
|
||||||
|
|
||||||
label.current-id {
|
label.current-id {
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
color: black;
|
color: black;
|
||||||
|
|
Loading…
Reference in a new issue