got it building
This commit is contained in:
parent
d3851d1e56
commit
3562bca493
18 changed files with 145 additions and 2031 deletions
401
Cargo.lock
generated
401
Cargo.lock
generated
|
@ -476,25 +476,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-sys-rs 0.15.1",
|
||||
"cairo-sys-rs",
|
||||
"glib 0.15.12",
|
||||
"libc",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cairo-rs"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ac2a4d0e69036cf0062976f6efcba1aaee3e448594e6514bb2ddf87acce562"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cairo-sys-rs 0.19.2",
|
||||
"glib 0.19.9",
|
||||
"libc",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cairo-sys-rs"
|
||||
version = "0.15.1"
|
||||
|
@ -506,17 +493,6 @@ dependencies = [
|
|||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cairo-sys-rs"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3bb3119664efbd78b5e6c93957447944f16bdbced84c17a9f41c7829b81e64"
|
||||
dependencies = [
|
||||
"glib-sys 0.19.8",
|
||||
"libc",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_toml"
|
||||
version = "0.15.3"
|
||||
|
@ -1270,13 +1246,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-rs 0.15.12",
|
||||
"gdk-pixbuf 0.15.11",
|
||||
"cairo-rs",
|
||||
"gdk-pixbuf",
|
||||
"gdk-sys",
|
||||
"gio 0.15.12",
|
||||
"gio",
|
||||
"glib 0.15.12",
|
||||
"libc",
|
||||
"pango 0.15.10",
|
||||
"pango",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1286,24 +1262,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"gdk-pixbuf-sys 0.15.10",
|
||||
"gio 0.15.12",
|
||||
"gdk-pixbuf-sys",
|
||||
"gio",
|
||||
"glib 0.15.12",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdk-pixbuf"
|
||||
version = "0.19.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624eaba126021103c7339b2e179ae4ee8cdab842daab419040710f38ed9f8699"
|
||||
dependencies = [
|
||||
"gdk-pixbuf-sys 0.19.8",
|
||||
"gio 0.19.8",
|
||||
"glib 0.19.9",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdk-pixbuf-sys"
|
||||
version = "0.15.10"
|
||||
|
@ -1317,93 +1281,23 @@ dependencies = [
|
|||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdk-pixbuf-sys"
|
||||
version = "0.19.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4efa05a4f83c8cc50eb4d883787b919b85e5f1d8dd10b5a1df53bf5689782379"
|
||||
dependencies = [
|
||||
"gio-sys 0.19.8",
|
||||
"glib-sys 0.19.8",
|
||||
"gobject-sys 0.19.8",
|
||||
"libc",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdk-sys"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88"
|
||||
dependencies = [
|
||||
"cairo-sys-rs 0.15.1",
|
||||
"gdk-pixbuf-sys 0.15.10",
|
||||
"cairo-sys-rs",
|
||||
"gdk-pixbuf-sys",
|
||||
"gio-sys 0.15.10",
|
||||
"glib-sys 0.15.10",
|
||||
"gobject-sys 0.15.10",
|
||||
"libc",
|
||||
"pango-sys 0.15.10",
|
||||
"pango-sys",
|
||||
"pkg-config",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdk4"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db265c9dd42d6a371e09e52deab3a84808427198b86ac792d75fd35c07990a07"
|
||||
dependencies = [
|
||||
"cairo-rs 0.19.4",
|
||||
"gdk-pixbuf 0.19.8",
|
||||
"gdk4-sys",
|
||||
"gio 0.19.8",
|
||||
"glib 0.19.9",
|
||||
"libc",
|
||||
"pango 0.19.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdk4-sys"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9418fb4e8a67074919fe7604429c45aa74eb9df82e7ca529767c6d4e9dc66dd"
|
||||
dependencies = [
|
||||
"cairo-sys-rs 0.19.2",
|
||||
"gdk-pixbuf-sys 0.19.8",
|
||||
"gio-sys 0.19.8",
|
||||
"glib-sys 0.19.8",
|
||||
"gobject-sys 0.19.8",
|
||||
"libc",
|
||||
"pango-sys 0.19.8",
|
||||
"pkg-config",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdk4-win32"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3294d0d08b179198a01ac2623786e6227e3d7a98e042e26e4d020f31ba8fac41"
|
||||
dependencies = [
|
||||
"gdk4",
|
||||
"gdk4-win32-sys",
|
||||
"gio 0.19.8",
|
||||
"glib 0.19.9",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdk4-win32-sys"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6682a82d3a1616fbe17a9a757528bd3ef0ede3eab7487f0ecf6ed2a499de117d"
|
||||
dependencies = [
|
||||
"gdk4-sys",
|
||||
"glib-sys 0.19.8",
|
||||
"libc",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdkwayland-sys"
|
||||
version = "0.15.3"
|
||||
|
@ -1533,24 +1427,6 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gio"
|
||||
version = "0.19.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c49f117d373ffcc98a35d114db5478bc223341cff53e39a5d6feced9e2ddffe"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"gio-sys 0.19.8",
|
||||
"glib 0.19.9",
|
||||
"libc",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gio-sys"
|
||||
version = "0.15.10"
|
||||
|
@ -1708,88 +1584,6 @@ dependencies = [
|
|||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "graphene-rs"
|
||||
version = "0.19.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5fb86031d24d9ec0a2a15978fc7a65d545a2549642cf1eb7c3dda358da42bcf"
|
||||
dependencies = [
|
||||
"glib 0.19.9",
|
||||
"graphene-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "graphene-sys"
|
||||
version = "0.19.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f530e0944bccba4b55065e9c69f4975ad691609191ebac16e13ab8e1f27af05"
|
||||
dependencies = [
|
||||
"glib-sys 0.19.8",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gsk4"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7563884bf6939f4468e5d94654945bdd9afcaf8c3ba4c5dd17b5342b747221be"
|
||||
dependencies = [
|
||||
"cairo-rs 0.19.4",
|
||||
"gdk4",
|
||||
"glib 0.19.9",
|
||||
"graphene-rs",
|
||||
"gsk4-sys",
|
||||
"libc",
|
||||
"pango 0.19.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gsk4-sys"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23024bf2636c38bbd1f822f58acc9d1c25b28da896ff0f291a1a232d4272b3dc"
|
||||
dependencies = [
|
||||
"cairo-sys-rs 0.19.2",
|
||||
"gdk4-sys",
|
||||
"glib-sys 0.19.8",
|
||||
"gobject-sys 0.19.8",
|
||||
"graphene-sys",
|
||||
"libc",
|
||||
"pango-sys 0.19.8",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gst-plugin-gtk4"
|
||||
version = "0.12.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8b7fbcf995782f9b710c9bb2e2b2409dc3846005f81ba484adc2c7aefb8835f"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"gdk4-win32",
|
||||
"gst-plugin-version-helper",
|
||||
"gstreamer",
|
||||
"gstreamer-base",
|
||||
"gstreamer-gl",
|
||||
"gstreamer-video",
|
||||
"gtk4",
|
||||
"once_cell",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gst-plugin-version-helper"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e5e874f1660252fd2ec81c602066df3633b3a6fcbe2b196f7f93c27cf069b2a"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"toml_edit 0.22.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer"
|
||||
version = "0.22.7"
|
||||
|
@ -1870,36 +1664,6 @@ dependencies = [
|
|||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-gl"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2776369ce07de81b1e6f52786caec898db5be5d4678a8104e8fcbffdae68332d"
|
||||
dependencies = [
|
||||
"glib 0.19.9",
|
||||
"gstreamer",
|
||||
"gstreamer-base",
|
||||
"gstreamer-gl-sys",
|
||||
"gstreamer-video",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-gl-sys"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "050a2cf158354bd5633079baf73d12767a5c90efc6377b4f9507aca082734286"
|
||||
dependencies = [
|
||||
"glib-sys 0.19.8",
|
||||
"gobject-sys 0.19.8",
|
||||
"gstreamer-base-sys",
|
||||
"gstreamer-sys",
|
||||
"gstreamer-video-sys",
|
||||
"libc",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-sys"
|
||||
version = "0.22.6"
|
||||
|
@ -1912,37 +1676,6 @@ dependencies = [
|
|||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-video"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25acba301f86b02584a642de0f224317be2bd0ceec3acda49a0ef111cbced98c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-channel",
|
||||
"glib 0.19.9",
|
||||
"gstreamer",
|
||||
"gstreamer-base",
|
||||
"gstreamer-video-sys",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-video-sys"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2ec210495f94cabaa45d08003081b550095c2d4ab12d5320f64856a91f3f01c"
|
||||
dependencies = [
|
||||
"glib-sys 0.19.8",
|
||||
"gobject-sys 0.19.8",
|
||||
"gstreamer-base-sys",
|
||||
"gstreamer-sys",
|
||||
"libc",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gtk"
|
||||
version = "0.15.5"
|
||||
|
@ -1951,18 +1684,18 @@ checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0"
|
|||
dependencies = [
|
||||
"atk",
|
||||
"bitflags 1.3.2",
|
||||
"cairo-rs 0.15.12",
|
||||
"cairo-rs",
|
||||
"field-offset",
|
||||
"futures-channel",
|
||||
"gdk",
|
||||
"gdk-pixbuf 0.15.11",
|
||||
"gio 0.15.12",
|
||||
"gdk-pixbuf",
|
||||
"gio",
|
||||
"glib 0.15.12",
|
||||
"gtk-sys",
|
||||
"gtk3-macros",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"pango 0.15.10",
|
||||
"pango",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
|
@ -1973,14 +1706,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84"
|
||||
dependencies = [
|
||||
"atk-sys",
|
||||
"cairo-sys-rs 0.15.1",
|
||||
"gdk-pixbuf-sys 0.15.10",
|
||||
"cairo-sys-rs",
|
||||
"gdk-pixbuf-sys",
|
||||
"gdk-sys",
|
||||
"gio-sys 0.15.10",
|
||||
"glib-sys 0.15.10",
|
||||
"gobject-sys 0.15.10",
|
||||
"libc",
|
||||
"pango-sys 0.15.10",
|
||||
"pango-sys",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
|
@ -1998,58 +1731,6 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gtk4"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b04e11319b08af11358ab543105a9e49b0c491faca35e2b8e7e36bfba8b671ab"
|
||||
dependencies = [
|
||||
"cairo-rs 0.19.4",
|
||||
"field-offset",
|
||||
"futures-channel",
|
||||
"gdk-pixbuf 0.19.8",
|
||||
"gdk4",
|
||||
"gio 0.19.8",
|
||||
"glib 0.19.9",
|
||||
"graphene-rs",
|
||||
"gsk4",
|
||||
"gtk4-macros",
|
||||
"gtk4-sys",
|
||||
"libc",
|
||||
"pango 0.19.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gtk4-macros"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec655a7ef88d8ce9592899deb8b2d0fa50bab1e6dd69182deb764e643c522408"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.1.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gtk4-sys"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c8aa86b7f85ea71d66ea88c1d4bae1cfacf51ca4856274565133838d77e57b5"
|
||||
dependencies = [
|
||||
"cairo-sys-rs 0.19.2",
|
||||
"gdk-pixbuf-sys 0.19.8",
|
||||
"gdk4-sys",
|
||||
"gio-sys 0.19.8",
|
||||
"glib-sys 0.19.8",
|
||||
"gobject-sys 0.19.8",
|
||||
"graphene-sys",
|
||||
"gsk4-sys",
|
||||
"libc",
|
||||
"pango-sys 0.19.8",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.26"
|
||||
|
@ -2995,19 +2676,7 @@ dependencies = [
|
|||
"glib 0.15.12",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"pango-sys 0.15.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pango"
|
||||
version = "0.19.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f0d328648058085cfd6897c9ae4272884098a926f3a833cd50c8c73e6eccecd"
|
||||
dependencies = [
|
||||
"gio 0.19.8",
|
||||
"glib 0.19.9",
|
||||
"libc",
|
||||
"pango-sys 0.19.8",
|
||||
"pango-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3022,18 +2691,6 @@ dependencies = [
|
|||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pango-sys"
|
||||
version = "0.19.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff03da4fa086c0b244d4a4587d3e20622a3ecdb21daea9edf66597224c634ba0"
|
||||
dependencies = [
|
||||
"glib-sys 0.19.8",
|
||||
"gobject-sys 0.19.8",
|
||||
"libc",
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.0"
|
||||
|
@ -3954,7 +3611,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"gio 0.15.12",
|
||||
"gio",
|
||||
"glib 0.15.12",
|
||||
"libc",
|
||||
"once_cell",
|
||||
|
@ -4089,7 +3746,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "575c856fc21e551074869dcfaad8f706412bd5b803dfa0fbf6881c4ff4bfafab"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-rs 0.15.12",
|
||||
"cairo-rs",
|
||||
"cc",
|
||||
"cocoa",
|
||||
"core-foundation",
|
||||
|
@ -4097,11 +3754,11 @@ dependencies = [
|
|||
"crossbeam-channel",
|
||||
"dispatch",
|
||||
"gdk",
|
||||
"gdk-pixbuf 0.15.11",
|
||||
"gdk-pixbuf",
|
||||
"gdk-sys",
|
||||
"gdkwayland-sys",
|
||||
"gdkx11-sys",
|
||||
"gio 0.15.12",
|
||||
"gio",
|
||||
"glib 0.15.12",
|
||||
"glib-sys 0.15.10",
|
||||
"gtk",
|
||||
|
@ -4899,10 +4556,8 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-util",
|
||||
"gilrs",
|
||||
"gst-plugin-gtk4",
|
||||
"gstreamer",
|
||||
"gstreamer-app",
|
||||
"gtk4",
|
||||
"log",
|
||||
"serde",
|
||||
"snafu",
|
||||
|
@ -5061,10 +4716,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-rs 0.15.12",
|
||||
"cairo-rs",
|
||||
"gdk",
|
||||
"gdk-sys",
|
||||
"gio 0.15.12",
|
||||
"gio",
|
||||
"gio-sys 0.15.10",
|
||||
"glib 0.15.12",
|
||||
"glib-sys 0.15.10",
|
||||
|
@ -5086,8 +4741,8 @@ checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3"
|
|||
dependencies = [
|
||||
"atk-sys",
|
||||
"bitflags 1.3.2",
|
||||
"cairo-sys-rs 0.15.1",
|
||||
"gdk-pixbuf-sys 0.15.10",
|
||||
"cairo-sys-rs",
|
||||
"gdk-pixbuf-sys",
|
||||
"gdk-sys",
|
||||
"gio-sys 0.15.10",
|
||||
"glib-sys 0.15.10",
|
||||
|
@ -5095,7 +4750,7 @@ dependencies = [
|
|||
"gtk-sys",
|
||||
"javascriptcore-rs-sys",
|
||||
"libc",
|
||||
"pango-sys 0.15.10",
|
||||
"pango-sys",
|
||||
"pkg-config",
|
||||
"soup2-sys",
|
||||
"system-deps 6.2.2",
|
||||
|
@ -5517,7 +5172,7 @@ dependencies = [
|
|||
"crossbeam-channel",
|
||||
"dunce",
|
||||
"gdk",
|
||||
"gio 0.15.12",
|
||||
"gio",
|
||||
"glib 0.15.12",
|
||||
"gtk",
|
||||
"html5ever",
|
||||
|
|
|
@ -20,8 +20,6 @@ futures-util = { version = "0.3.30", features = ["tokio-io"] }
|
|||
gilrs = "0.10.6"
|
||||
gstreamer = { version = "0.22.4", features = ["v1_22"] }
|
||||
gstreamer-app = { version = "0.22.0", features = ["v1_22"] }
|
||||
gst-plugin-gtk4 = { version = "0.12.2", features = ["gtk_v4_12"] }
|
||||
gtk = { version = "0.8.1", package = "gtk4", features = ["v4_12"] }
|
||||
log = "0.4.21"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
tokio = { version = "1.37.0", features = ["rt-multi-thread", "time", "sync"] }
|
||||
|
|
21
controller/build.rs
Normal file
21
controller/build.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use std::process::Command;
|
||||
|
||||
const INPUT_CSS_PATH: &str = "./templates/input.css";
|
||||
const OUTPUT_CSS_PATH: &str = "./static/css/main.css";
|
||||
|
||||
fn main() {
|
||||
run_tailwind();
|
||||
}
|
||||
|
||||
fn run_tailwind() {
|
||||
Command::new("tailwindcss")
|
||||
.args([
|
||||
"-i",
|
||||
INPUT_CSS_PATH,
|
||||
"-o",
|
||||
OUTPUT_CSS_PATH,
|
||||
"--minify",
|
||||
])
|
||||
.spawn()
|
||||
.expect("Couldn't run tailwindcss, please run it manually");
|
||||
}
|
|
@ -8,7 +8,6 @@ use std::time::Instant;
|
|||
use async_channel::{Receiver, Sender};
|
||||
use futures_util::{stream::SplitSink, SinkExt, StreamExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
|
||||
use tracing::{debug, error, info, instrument};
|
||||
|
@ -17,7 +16,7 @@ use crate::config::AppConfig;
|
|||
use crate::coordinator::socket_listen;
|
||||
use crate::coordinator::tracker_state::TrackerState;
|
||||
use crate::gstreamer_pipeline;
|
||||
use crate::{sources::joystick_source::joystick_loop, ui::GuiUpdate};
|
||||
use crate::sources::joystick_source::joystick_loop;
|
||||
|
||||
use super::perf_state::TrackerMetrics;
|
||||
use super::remote_video_processor::remote_video_loop;
|
||||
|
@ -43,8 +42,6 @@ pub struct CoordState<'a> {
|
|||
|
||||
pub mec: Pin<&'a mut Receiver<ApplicationEvent>>,
|
||||
pub to_mec: Sender<ApplicationEvent>,
|
||||
pub to_gui: Sender<GuiUpdate>,
|
||||
pub rt: Handle,
|
||||
|
||||
pub pipeline: gstreamer_pipeline::WebcamPipeline,
|
||||
|
||||
|
@ -56,14 +53,12 @@ impl<'a> CoordState<'a> {
|
|||
pub fn new(
|
||||
mec: Pin<&'a mut Receiver<ApplicationEvent>>,
|
||||
to_mec: Sender<ApplicationEvent>,
|
||||
to_gui: Sender<GuiUpdate>,
|
||||
rt: Handle,
|
||||
settings: Arc<RwLock<AppConfig>>,
|
||||
jpeg_quality: i32,
|
||||
) -> Self {
|
||||
CoordState {
|
||||
settings,
|
||||
tracker_metrics: TrackerMetrics::new(to_gui.clone()),
|
||||
tracker_metrics: TrackerMetrics::new(),
|
||||
|
||||
sck_outbound: None,
|
||||
stay_alive_sck_recvr: Arc::new(AtomicBool::new(false)),
|
||||
|
@ -74,8 +69,6 @@ impl<'a> CoordState<'a> {
|
|||
|
||||
mec,
|
||||
to_mec,
|
||||
to_gui,
|
||||
rt,
|
||||
|
||||
pipeline: gstreamer_pipeline::WebcamPipeline::new(jpeg_quality).unwrap(),
|
||||
|
||||
|
@ -113,10 +106,6 @@ impl<'a> CoordState<'a> {
|
|||
pub async fn socket_start(&mut self) {
|
||||
self.stay_alive_sck_recvr.store(true, Ordering::SeqCst);
|
||||
|
||||
if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnecting).await {
|
||||
error!("Cannot send message to gui thread: {e}");
|
||||
}
|
||||
|
||||
let conn_string: String = {
|
||||
let read_settings = self.settings.read().await;
|
||||
|
||||
|
@ -132,22 +121,16 @@ impl<'a> CoordState<'a> {
|
|||
info!("Socket connection to camera made successfully");
|
||||
|
||||
let (outbound, inbound) = val.split();
|
||||
self.rt.spawn(socket_listen(
|
||||
let _socket_task = tokio::spawn(socket_listen(
|
||||
self.to_mec.clone(),
|
||||
self.stay_alive_sck_recvr.clone(),
|
||||
inbound,
|
||||
));
|
||||
self.sck_outbound = Some(outbound);
|
||||
|
||||
if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnected).await {
|
||||
error!("Cannot send message to gui thread: {e}");
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
error!("Couldn't connect to URL!");
|
||||
if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await {
|
||||
error!("Cannot send message to gui thread: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,10 +144,6 @@ impl<'a> CoordState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await {
|
||||
error!("Cannot send message to gui thread: {e}");
|
||||
}
|
||||
|
||||
self.stay_alive_sck_recvr.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
|
@ -179,7 +158,7 @@ impl<'a> CoordState<'a> {
|
|||
)
|
||||
};
|
||||
|
||||
self.rt.spawn(remote_video_loop(
|
||||
let _remote_loop = tokio::spawn(remote_video_loop(
|
||||
conn_string,
|
||||
self.pipeline.sink_frame.clone(),
|
||||
self.to_mec.clone(),
|
||||
|
@ -191,7 +170,7 @@ impl<'a> CoordState<'a> {
|
|||
// This one needs to always be alive, and restart after a crash
|
||||
if !self.joystick_loop_alive.load(Ordering::SeqCst) {
|
||||
info!("Restarting joystick loop");
|
||||
self.rt.spawn(joystick_loop(
|
||||
let _joystick_future = tokio::spawn(joystick_loop(
|
||||
self.to_mec.clone(),
|
||||
self.joystick_loop_alive.clone(),
|
||||
));
|
||||
|
@ -224,7 +203,6 @@ impl<'a> CoordState<'a> {
|
|||
self.socket_close().await;
|
||||
|
||||
self.joystick_loop_alive.store(false, Ordering::SeqCst);
|
||||
self.to_gui.close();
|
||||
self.mec.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ use futures_util::{stream::SplitStream, StreamExt};
|
|||
use gstreamer::prelude::ElementExt;
|
||||
use gstreamer::State;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_tungstenite::{tungstenite::Message, MaybeTlsStream, WebSocketStream};
|
||||
use tracing::{debug, error, info, instrument};
|
||||
|
@ -21,8 +20,8 @@ mod remote_video_processor;
|
|||
|
||||
use crate::states::perf_state;
|
||||
use crate::states::tracker_state;
|
||||
use crate::states::box_coords::NormalizedBoxCoords;
|
||||
use crate::config::AppConfig;
|
||||
use crate::ui::{GuiUpdate, NormalizedBoxCoords};
|
||||
pub use coord_state::{CoordState, SocketState};
|
||||
|
||||
const PRIORITY_TIMEOUT: Duration = Duration::from_secs(2);
|
||||
|
@ -36,7 +35,7 @@ pub struct MoveEvent {
|
|||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
||||
pub enum ConnectionType {
|
||||
Local,
|
||||
Remote,
|
||||
// Remote,
|
||||
Automated,
|
||||
}
|
||||
|
||||
|
@ -44,7 +43,6 @@ pub enum TrackerUpdate {
|
|||
Clear,
|
||||
Fail,
|
||||
Update(TrackerUpdatePackage),
|
||||
HeaderUpdate(String),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -55,12 +53,10 @@ pub struct TrackerUpdatePackage {
|
|||
}
|
||||
|
||||
pub enum ApplicationEvent {
|
||||
CameraConnectionPress,
|
||||
SocketMessage(Message),
|
||||
MoveEvent(MoveEvent, ConnectionType),
|
||||
TrackerUpdate(TrackerUpdate),
|
||||
ChangeTracking(u32),
|
||||
EnableAutomatic(bool),
|
||||
Close,
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
|
@ -68,8 +64,6 @@ pub async fn start_coordinator(
|
|||
// Main_Event_Channel
|
||||
mec: Receiver<ApplicationEvent>,
|
||||
to_mec: Sender<ApplicationEvent>,
|
||||
to_gui: Sender<GuiUpdate>,
|
||||
runtime: Handle,
|
||||
settings: Arc<RwLock<AppConfig>>,
|
||||
) {
|
||||
info!("Starting coordinator!");
|
||||
|
@ -81,8 +75,6 @@ pub async fn start_coordinator(
|
|||
let mut state = CoordState::new(
|
||||
mec,
|
||||
to_mec,
|
||||
to_gui,
|
||||
runtime,
|
||||
settings,
|
||||
jpeg_quality,
|
||||
);
|
||||
|
@ -93,44 +85,18 @@ pub async fn start_coordinator(
|
|||
.set_state(State::Playing)
|
||||
.expect("Could not set pipeline state to playing");
|
||||
|
||||
if let Err(e) = state
|
||||
.to_gui
|
||||
.send(GuiUpdate::UpdatePaintable(
|
||||
state.pipeline.sink_paintable.clone(),
|
||||
))
|
||||
.await
|
||||
{
|
||||
error!("Could not send new paintable to GUI: {e}");
|
||||
}
|
||||
|
||||
state.check_states().await;
|
||||
|
||||
while let Some(msg) = state.mec.next().await {
|
||||
state.check_states().await;
|
||||
|
||||
match msg {
|
||||
ApplicationEvent::CameraConnectionPress => {
|
||||
if state.socket_connected() {
|
||||
state.socket_close().await;
|
||||
} else {
|
||||
state.socket_start().await;
|
||||
}
|
||||
ApplicationEvent::Close => {
|
||||
break;
|
||||
}
|
||||
ApplicationEvent::SocketMessage(socket_message) => {
|
||||
state.socket_send(socket_message).await;
|
||||
}
|
||||
ApplicationEvent::ChangeTracking(new_id) => {
|
||||
state.tracker_state.tracking_id = new_id;
|
||||
}
|
||||
ApplicationEvent::EnableAutomatic(do_enable) => {
|
||||
state.tracker_state.enabled = do_enable;
|
||||
state
|
||||
.tracker_connection_state
|
||||
.stay_connected
|
||||
.store(do_enable, Ordering::SeqCst);
|
||||
|
||||
state.check_states().await;
|
||||
}
|
||||
ApplicationEvent::MoveEvent(coord, priority) => {
|
||||
// If Automatic control, but local event happens, override the automatice events for 2 seconds
|
||||
if priority <= state.current_priority
|
||||
|
@ -139,10 +105,6 @@ pub async fn start_coordinator(
|
|||
state.last_update_of_priority = Instant::now();
|
||||
state.current_priority = priority;
|
||||
|
||||
if let Err(e) = state.to_gui.send(GuiUpdate::MoveEvent(coord.clone())).await {
|
||||
panic!("Could not set message to gui channel; Unrecoverable: {e}");
|
||||
}
|
||||
|
||||
if state.socket_connected() {
|
||||
let message = format!(
|
||||
"{}{}:{}{}",
|
||||
|
@ -157,14 +119,9 @@ pub async fn start_coordinator(
|
|||
}
|
||||
}
|
||||
ApplicationEvent::TrackerUpdate(update) => match update {
|
||||
TrackerUpdate::HeaderUpdate(_) => {}
|
||||
TrackerUpdate::Clear => {
|
||||
state.tracker_state.clear();
|
||||
state.tracker_metrics.clear_times();
|
||||
if let Err(e) = state.to_gui.send(GuiUpdate::TrackerUpdate(TrackerUpdate::Clear)).await {
|
||||
error!("Could not send message to GUI: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
TrackerUpdate::Fail => {
|
||||
let fail_count: usize = state.tracker_metrics.fail_count + 1;
|
||||
|
@ -174,16 +131,6 @@ pub async fn start_coordinator(
|
|||
let mut x_adj: i32 = 0;
|
||||
let mut y_adj: i32 = 0;
|
||||
|
||||
if let Err(e) = state.to_gui
|
||||
.send(GuiUpdate::TrackerUpdate(TrackerUpdate::Update(
|
||||
update.clone(),
|
||||
)))
|
||||
.await
|
||||
{
|
||||
error!("Could not send message to the GUI: {e}");
|
||||
break;
|
||||
}
|
||||
|
||||
state.tracker_state.update_from_boxes(update.boxes);
|
||||
state.tracker_state.last_detect = update.time;
|
||||
|
||||
|
@ -210,9 +157,6 @@ pub async fn start_coordinator(
|
|||
{
|
||||
error!("Could not send to MEC... even though in the MEC?! {e}");
|
||||
}
|
||||
if let Err(e) = state.to_gui.send(GuiUpdate::MoveEvent(me)).await {
|
||||
error!("Could not send to MEC... even though in the MEC?! {e}");
|
||||
}
|
||||
state.tracker_metrics.insert_time(update.request_duration);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ui::NormalizedBoxCoords;
|
||||
use crate::states::box_coords::NormalizedBoxCoords;
|
||||
|
||||
pub fn process_incoming_string(message: String) -> Result<Vec<NormalizedBoxCoords>, String> {
|
||||
let mut boxes: Vec<NormalizedBoxCoords> = Vec::new();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use gstreamer::{prelude::*, PadLinkError};
|
||||
use gstreamer::{Element, ElementFactory, Pipeline};
|
||||
use gstreamer_app::AppSink;
|
||||
use gtk::glib::BoolError;
|
||||
use gstreamer::glib::BoolError;
|
||||
use snafu::prelude::*;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use gtk::prelude::{ApplicationExt, ApplicationExtManual};
|
||||
use gtk::{glib, Application};
|
||||
use std::{env, sync::Arc};
|
||||
use tokio::{runtime, sync::RwLock};
|
||||
use tracing::{self, info, Level};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync:: RwLock;
|
||||
use tracing::{self, info};
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
use tracing_subscriber;
|
||||
|
@ -14,14 +12,12 @@ mod coordinator;
|
|||
mod gstreamer_pipeline;
|
||||
mod sources;
|
||||
mod states;
|
||||
mod ui;
|
||||
mod webapp;
|
||||
|
||||
const APP_ID: &str = "net.nickiel.joystick-controller-client";
|
||||
use coordinator::{start_coordinator, ApplicationEvent};
|
||||
|
||||
fn main() -> glib::ExitCode {
|
||||
// set the environment var to make gtk use window's default action bar
|
||||
env::set_var("gtk_csd", "0");
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
|
@ -46,36 +42,21 @@ fn main() -> glib::ExitCode {
|
|||
}
|
||||
|
||||
|
||||
let span = tracing::span!(Level::TRACE, "main");
|
||||
let _enter = span.enter();
|
||||
|
||||
info!("Logging intialized");
|
||||
|
||||
let config: Arc<RwLock<AppConfig>> = Arc::new(RwLock::new(load_config()));
|
||||
|
||||
gstreamer::init().expect("Unable to start gstreamer");
|
||||
gstgtk4::plugin_register_static().expect("Unable to register gtk4 plugin");
|
||||
|
||||
let rt = runtime::Runtime::new().expect("Could not start tokio runtime");
|
||||
let handle = rt.handle().clone();
|
||||
let (to_mec, mec) = async_channel::bounded::<ApplicationEvent>(10);
|
||||
|
||||
handle.spawn(webapp::start_webui());
|
||||
let coordinator = tokio::spawn(start_coordinator(
|
||||
mec,
|
||||
to_mec.clone(),
|
||||
config
|
||||
));
|
||||
|
||||
let app = Application::builder().application_id(APP_ID).build();
|
||||
webapp::start_webui().await;
|
||||
|
||||
app.connect_startup(ui::on_activate);
|
||||
|
||||
app.connect_activate(move |app| {
|
||||
ui::build_ui(app, config.clone(), handle.clone());
|
||||
});
|
||||
|
||||
let exit_code = app.run();
|
||||
|
||||
info!("Gtk application has closed");
|
||||
|
||||
rt.block_on(async {});
|
||||
|
||||
info!("Tokio runtime has shut down");
|
||||
|
||||
exit_code
|
||||
let _ = to_mec.send(ApplicationEvent::Close).await;
|
||||
}
|
||||
|
|
56
controller/src/states/box_coords.rs
Normal file
56
controller/src/states/box_coords.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BoxCoords {
|
||||
pub id: u32,
|
||||
pub x1: u32,
|
||||
pub y1: u32,
|
||||
pub x2: u32,
|
||||
pub y2: u32,
|
||||
}
|
||||
|
||||
impl Display for BoxCoords {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Absolute Box {}, x1: {}, y1: {}, x2: {}, y2: {}",
|
||||
self.id, self.x1, self.y1, self.x2, self.y2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NormalizedBoxCoords {
|
||||
pub id: u32,
|
||||
pub x1: f32,
|
||||
pub y1: f32,
|
||||
pub x2: f32,
|
||||
pub y2: f32,
|
||||
}
|
||||
|
||||
impl NormalizedBoxCoords {
|
||||
fn absolute_coords(&self, width: i32, height: i32) -> BoxCoords {
|
||||
BoxCoords {
|
||||
id: self.id,
|
||||
x1: (self.x1 * width as f32) as u32,
|
||||
y1: (self.y1 * height as f32) as u32,
|
||||
x2: (self.x2 * width as f32) as u32,
|
||||
y2: (self.y2 * height as f32) as u32,
|
||||
}
|
||||
}
|
||||
|
||||
fn area(&self) -> f32 {
|
||||
(self.x2 - self.x1) * (self.y2 - self.y1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NormalizedBoxCoords {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Normalized Box {}, x1: {}, y1: {}, x2: {}, y2: {}",
|
||||
self.id, self.x1, self.y1, self.x2, self.y2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
pub mod perf_state;
|
||||
pub mod tracker_state;
|
||||
pub mod box_coords;
|
||||
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
use std::{collections::VecDeque, time::Duration};
|
||||
|
||||
use async_channel::Sender;
|
||||
use tracing::error;
|
||||
|
||||
use crate::coordinator::TrackerUpdate;
|
||||
use crate::ui::GuiUpdate;
|
||||
|
||||
const MAX_RECORDED_TIMES: usize = 10;
|
||||
const DEGRADED_TRACKER_TIME: u128 = 150;
|
||||
|
||||
|
@ -14,26 +8,22 @@ pub struct TrackerMetrics {
|
|||
pub header_text: String,
|
||||
pub fail_count: usize,
|
||||
tracker_times: VecDeque<u128>,
|
||||
to_gui: Sender<GuiUpdate>,
|
||||
}
|
||||
|
||||
impl TrackerMetrics {
|
||||
pub fn new(to_gui: Sender<GuiUpdate>) -> Self {
|
||||
pub fn new() -> Self {
|
||||
let mut ret = TrackerMetrics {
|
||||
header_text: String::from(""),
|
||||
fail_count: 0,
|
||||
|
||||
tracker_times: VecDeque::with_capacity(MAX_RECORDED_TIMES),
|
||||
to_gui,
|
||||
};
|
||||
ret.clear_times();
|
||||
ret
|
||||
}
|
||||
|
||||
fn update_gui(&mut self) {
|
||||
if let Err(e) = self.to_gui.send_blocking(GuiUpdate::TrackerUpdate(TrackerUpdate::HeaderUpdate(self.header_text.clone()))) {
|
||||
error!("TrackerMetrics couldnt' send update to GUI: {e}");
|
||||
}
|
||||
todo!("No gui channel sent yet");
|
||||
}
|
||||
|
||||
pub fn starting_connection(&mut self, fail_count: Option<usize>) {
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{
|
|||
time::Instant,
|
||||
};
|
||||
|
||||
use crate::ui::NormalizedBoxCoords;
|
||||
use crate::states::box_coords::NormalizedBoxCoords;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TrackerState {
|
||||
|
|
|
@ -1,188 +0,0 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use async_channel::Sender;
|
||||
use gtk::{
|
||||
glib::{self, object::CastNone},
|
||||
prelude::{
|
||||
BoxExt, ButtonExt, Cast, GObjectPropertyExpressionExt, ListItemExt, ToggleButtonExt,
|
||||
},
|
||||
Box, Button, Expander, Label, ListItem, ListView, ScrolledWindow, SignalListItemFactory,
|
||||
SingleSelection, StringList, StringObject, ToggleButton, Widget,
|
||||
};
|
||||
use tracing::{error, event, span, Level};
|
||||
|
||||
#[cfg(feature = "tracker-state-debug")]
|
||||
use tracing::debug;
|
||||
|
||||
use crate::coordinator::ApplicationEvent;
|
||||
use crate::states::tracker_state::TrackerState;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ControlPanel {
|
||||
top_level: Box,
|
||||
|
||||
pub connection_buttons: ExpanderMenu,
|
||||
pub current_id: Label,
|
||||
|
||||
pub items: StringList,
|
||||
pub list_view: ListView,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExpanderMenu {
|
||||
pub top_level: Expander,
|
||||
|
||||
pub camera_connection: Button,
|
||||
pub tracker_enable_toggle: ToggleButton,
|
||||
}
|
||||
|
||||
impl ControlPanel {
|
||||
pub fn new(tracker_state: Arc<Mutex<TrackerState>>) -> ControlPanel {
|
||||
let factory = SignalListItemFactory::new();
|
||||
factory.connect_setup(move |_, list_item| {
|
||||
let list_item = list_item
|
||||
.downcast_ref::<ListItem>()
|
||||
.expect("Needs to be a List Item");
|
||||
|
||||
let label = Label::new(None);
|
||||
list_item.set_child(Some(&label));
|
||||
|
||||
list_item
|
||||
.property_expression("item")
|
||||
.chain_property::<StringObject>("string")
|
||||
.bind(&label, "label", Widget::NONE);
|
||||
});
|
||||
|
||||
let items = StringList::new(&["Please connect automatic source"]);
|
||||
|
||||
let model = SingleSelection::builder()
|
||||
.model(&items)
|
||||
.autoselect(false)
|
||||
.can_unselect(false)
|
||||
.build();
|
||||
|
||||
model.connect_selected_item_notify(move |x| {
|
||||
let item = x.selected_item().and_downcast::<StringObject>();
|
||||
|
||||
if let Some(item) = item {
|
||||
if let Ok(id) = item.string().parse::<u32>() {
|
||||
#[cfg(feature = "tracker-state-debug")]
|
||||
debug!("Getting lock on tracker state for setting active tracking id!");
|
||||
if let Ok(mut ts) = tracker_state.lock() {
|
||||
ts.tracking_id = id;
|
||||
}
|
||||
} else {
|
||||
error!("An unparsable ID was clicked");
|
||||
}
|
||||
} else {
|
||||
error!("An invalid id was selected from the selection");
|
||||
}
|
||||
});
|
||||
|
||||
let list_view = ListView::new(Some(model), Some(factory));
|
||||
|
||||
let scrolled_window = ScrolledWindow::builder()
|
||||
.child(&list_view)
|
||||
.hscrollbar_policy(gtk::PolicyType::Never)
|
||||
.height_request(200)
|
||||
.build();
|
||||
|
||||
let top_level = Box::builder()
|
||||
.orientation(gtk::Orientation::Vertical)
|
||||
.spacing(5)
|
||||
.margin_top(24)
|
||||
.margin_start(24)
|
||||
.margin_end(24)
|
||||
.margin_bottom(12)
|
||||
.build();
|
||||
|
||||
let expander = ExpanderMenu::new();
|
||||
|
||||
let current_id = Label::builder()
|
||||
.label("Not Tracking")
|
||||
.can_focus(false)
|
||||
.can_target(false)
|
||||
.css_classes(["current-id"])
|
||||
.build();
|
||||
|
||||
top_level.append(&expander.top_level);
|
||||
top_level.append(¤t_id);
|
||||
top_level.append(&scrolled_window);
|
||||
|
||||
ControlPanel {
|
||||
top_level,
|
||||
|
||||
connection_buttons: expander,
|
||||
current_id,
|
||||
|
||||
items,
|
||||
list_view,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_top_level(&self) -> &Box {
|
||||
&self.top_level
|
||||
}
|
||||
|
||||
pub fn connect_button_callbacks(&self, to_mec: Sender<ApplicationEvent>) {
|
||||
self.connection_buttons
|
||||
.tracker_enable_toggle
|
||||
.connect_clicked(glib::clone!(@strong to_mec => move |button| {
|
||||
let span = span!(Level::TRACE, "tracker_enable_toggle callback");
|
||||
let _enter = span.enter();
|
||||
if let Err(e) =
|
||||
to_mec.send_blocking(ApplicationEvent::EnableAutomatic(button.is_active()))
|
||||
{
|
||||
event!(Level::ERROR, error = ?e, "Could not send message to the MEC");
|
||||
}
|
||||
}));
|
||||
|
||||
self.connection_buttons.camera_connection.connect_clicked(glib::clone!(@strong to_mec => move |_button| {
|
||||
let span = span!(Level::TRACE, "camera_connection callback");
|
||||
let _enter = span.enter();
|
||||
match to_mec.try_send(ApplicationEvent::CameraConnectionPress) {
|
||||
Ok(_) => {},
|
||||
Err(async_channel::TrySendError::Closed(_)) => panic!("Coordinator MEC is closed. Unrecoverable error."),
|
||||
Err(e) => event!(Level::ERROR, error = ?e, message = "There was an error sending to the MEC"),
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpanderMenu {
|
||||
pub fn new() -> Self {
|
||||
let content_box = Box::builder()
|
||||
.orientation(gtk::Orientation::Vertical)
|
||||
.spacing(10)
|
||||
.margin_top(12)
|
||||
.margin_start(24)
|
||||
.margin_end(24)
|
||||
.margin_bottom(12)
|
||||
.build();
|
||||
let expander = Expander::builder()
|
||||
.child(&content_box)
|
||||
.expanded(true)
|
||||
.label("Connections")
|
||||
.build();
|
||||
|
||||
let camera_connection = Button::builder()
|
||||
.label("Connect to Camera")
|
||||
.margin_top(12)
|
||||
.build();
|
||||
let tracker_enable_toggle = ToggleButton::builder()
|
||||
.label("Connect to Tracker Computer")
|
||||
.active(false)
|
||||
.margin_top(12)
|
||||
.build();
|
||||
|
||||
content_box.append(&camera_connection);
|
||||
content_box.append(&tracker_enable_toggle);
|
||||
|
||||
ExpanderMenu {
|
||||
top_level: expander,
|
||||
|
||||
camera_connection,
|
||||
tracker_enable_toggle,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
use std::{
|
||||
cmp::Ordering,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use async_channel::Sender;
|
||||
use gtk::{
|
||||
gdk::Paintable,
|
||||
prelude::{BoxExt, GestureExt, WidgetExt},
|
||||
AspectFrame, Box, DrawingArea, EventControllerMotion, GestureClick, Label, Overlay, Picture,
|
||||
};
|
||||
|
||||
use crate::coordinator::ApplicationEvent;
|
||||
use crate::states::tracker_state::TrackerState;
|
||||
|
||||
use super::NormalizedBoxCoords;
|
||||
|
||||
pub struct LiveViewPanel {
|
||||
top_level: gtk::Box,
|
||||
|
||||
pub tracker_status_label: Label,
|
||||
pub cam_status_label: Label,
|
||||
pub adjustment_label: Label,
|
||||
picture: Picture,
|
||||
overlay: Overlay,
|
||||
}
|
||||
|
||||
impl LiveViewPanel {
|
||||
pub fn new(tracker_state: Arc<Mutex<TrackerState>>, to_mec: Sender<ApplicationEvent>) -> Self {
|
||||
let right_box = gtk::Box::builder()
|
||||
.orientation(gtk::Orientation::Vertical)
|
||||
.hexpand(true)
|
||||
.valign(gtk::Align::Center)
|
||||
.build();
|
||||
|
||||
let tracker_status_label = Label::builder()
|
||||
.label("No Status Yet".to_string())
|
||||
.can_focus(true)
|
||||
.css_classes(vec!["large-label", "NoConnection"])
|
||||
.build();
|
||||
|
||||
let cam_status_label = Label::builder()
|
||||
.label("No Connection".to_string())
|
||||
.css_classes(vec!["NoConnection"])
|
||||
.can_focus(true)
|
||||
.build();
|
||||
|
||||
let adjustment_label = Label::builder()
|
||||
.label("X: 0 Y: )")
|
||||
.justify(gtk::Justification::Center)
|
||||
.css_classes(vec!["JoystickCurrent"])
|
||||
.build();
|
||||
|
||||
let webcam_picture = gtk::Picture::builder().can_focus(false).build();
|
||||
|
||||
let overlay_box = gtk::Overlay::builder().build();
|
||||
overlay_box.set_child(Some(&webcam_picture));
|
||||
|
||||
let click_handler = GestureClick::builder()
|
||||
.button(gtk::gdk::ffi::GDK_BUTTON_PRIMARY as u32)
|
||||
.propagation_limit(gtk::PropagationLimit::SameNative)
|
||||
.build();
|
||||
|
||||
let move_handler = EventControllerMotion::builder()
|
||||
.propagation_limit(gtk::PropagationLimit::SameNative)
|
||||
.build();
|
||||
|
||||
let tracker_state_2 = tracker_state.clone();
|
||||
|
||||
let handler_picture = webcam_picture.clone();
|
||||
move_handler.connect_motion(move |_motion_handler, x, y| {
|
||||
LiveViewPanel::motion_callback(&handler_picture, &tracker_state_2, x, y);
|
||||
});
|
||||
|
||||
let handler_picture = webcam_picture.clone();
|
||||
|
||||
click_handler.connect_pressed(move |gesture, _id, x, y| {
|
||||
gesture.set_state(gtk::EventSequenceState::Claimed);
|
||||
LiveViewPanel::click_gesture_callback(&handler_picture, &tracker_state, &to_mec, x, y)
|
||||
});
|
||||
|
||||
overlay_box.add_controller(click_handler);
|
||||
overlay_box.add_controller(move_handler);
|
||||
|
||||
let aspect = AspectFrame::builder()
|
||||
.ratio(16.0 / 9.0)
|
||||
.obey_child(false)
|
||||
.child(&overlay_box)
|
||||
.build();
|
||||
|
||||
right_box.append(&tracker_status_label);
|
||||
right_box.append(&aspect);
|
||||
right_box.append(&cam_status_label);
|
||||
right_box.append(&adjustment_label);
|
||||
|
||||
LiveViewPanel {
|
||||
adjustment_label,
|
||||
tracker_status_label,
|
||||
cam_status_label,
|
||||
overlay: overlay_box,
|
||||
picture: webcam_picture,
|
||||
top_level: right_box,
|
||||
}
|
||||
}
|
||||
|
||||
fn click_gesture_callback(
|
||||
overlay: &Picture,
|
||||
tracker_state: &Arc<Mutex<TrackerState>>,
|
||||
to_mec: &Sender<ApplicationEvent>,
|
||||
x_coord: f64,
|
||||
y_coord: f64,
|
||||
) {
|
||||
let x_size = overlay.size(gtk::Orientation::Horizontal);
|
||||
let y_size = overlay.size(gtk::Orientation::Vertical);
|
||||
let x_coord = x_coord as f32 / x_size as f32;
|
||||
let y_coord = y_coord as f32 / y_size as f32;
|
||||
|
||||
if let Ok(mut ts) = tracker_state.lock() {
|
||||
if let Some(v) = calc_box_under_mouse(&ts.identity_boxes, x_coord, y_coord) {
|
||||
ts.tracking_id = v;
|
||||
if let Err(e) = to_mec.send_blocking(ApplicationEvent::ChangeTracking(v)) {
|
||||
panic!("Could not send message to MEC, unrecoverable: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn motion_callback(
|
||||
overlay: &Picture,
|
||||
tracker_state: &Arc<Mutex<TrackerState>>,
|
||||
x_coord: f64,
|
||||
y_coord: f64,
|
||||
) {
|
||||
let x_size = overlay.size(gtk::Orientation::Horizontal);
|
||||
let y_size = overlay.size(gtk::Orientation::Vertical);
|
||||
let x_coord = x_coord as f32 / x_size as f32;
|
||||
let y_coord = y_coord as f32 / y_size as f32;
|
||||
|
||||
if let Ok(mut ts) = tracker_state.lock() {
|
||||
ts.highlighted_id = calc_box_under_mouse(&ts.identity_boxes, x_coord, y_coord);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_top_level(&self) -> &Box {
|
||||
&self.top_level
|
||||
}
|
||||
|
||||
pub fn set_drawable(&self, new_drawable: &DrawingArea) {
|
||||
self.overlay.add_overlay(new_drawable);
|
||||
}
|
||||
|
||||
pub fn set_paintable(&self, new_paintable: &Paintable) {
|
||||
self.picture.set_paintable(Some(new_paintable));
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_box_under_mouse(
|
||||
boxes: &[NormalizedBoxCoords],
|
||||
x_coord: f32,
|
||||
y_coord: f32,
|
||||
) -> Option<u32> {
|
||||
let mut mouse_over: Vec<NormalizedBoxCoords> = vec![];
|
||||
|
||||
for nb in boxes.iter() {
|
||||
if nb.x1 < x_coord
|
||||
&& nb.y1 < y_coord
|
||||
&& nb.x2 > x_coord
|
||||
&& nb.y2 > y_coord
|
||||
{
|
||||
mouse_over.push(*nb);
|
||||
}
|
||||
}
|
||||
if mouse_over.len() > 1 {
|
||||
let mut x_coords = mouse_over
|
||||
.iter()
|
||||
.flat_map(|b| [b.x1, b.x2])
|
||||
.collect::<Vec<f32>>();
|
||||
x_coords.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
|
||||
let mid = x_coords.len() / 2;
|
||||
let median_two_x = &x_coords[mid - 1..mid + 1];
|
||||
|
||||
let mut y_coords = mouse_over
|
||||
.iter()
|
||||
.flat_map(|b| [b.y1, b.y2])
|
||||
.collect::<Vec<f32>>();
|
||||
y_coords.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
let median_two_y = &y_coords[mid - 1..mid + 1];
|
||||
|
||||
let overlap_area =
|
||||
(median_two_x[1] - median_two_x[0]) * (median_two_y[1] - median_two_y[0]);
|
||||
|
||||
// We want the one with the largest percentage of it's area as overlap
|
||||
// to be the highlighted one
|
||||
mouse_over.sort_by(|a, b| {
|
||||
let result =
|
||||
((a.area() - overlap_area) / a.area()) - ((b.area() - overlap_area) / b.area());
|
||||
if 0.0001 > result && result > -0.0001 {
|
||||
Ordering::Equal
|
||||
} else if result > 0.0 {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mouse_over
|
||||
.iter()
|
||||
.map(|x| x.id)
|
||||
.collect::<Vec<u32>>()
|
||||
.first()
|
||||
.copied()
|
||||
}
|
|
@ -1,411 +0,0 @@
|
|||
use std::fmt::Display;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use gtk::cairo::Context;
|
||||
use gtk::gdk::Paintable;
|
||||
use gtk::glib::clone;
|
||||
use gtk::{gio, glib, prelude::*, CssProvider};
|
||||
use gtk::{Application, ApplicationWindow};
|
||||
use log::{error, info};
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::states::tracker_state::TrackerState;
|
||||
use crate::coordinator::{start_coordinator, ApplicationEvent, MoveEvent, TrackerUpdate};
|
||||
|
||||
mod control_panel;
|
||||
mod liveview_panel;
|
||||
mod settings_modal;
|
||||
|
||||
use control_panel::ControlPanel;
|
||||
use liveview_panel::LiveViewPanel;
|
||||
|
||||
pub enum GuiUpdate {
|
||||
SocketDisconnected,
|
||||
SocketConnecting,
|
||||
SocketConnected,
|
||||
MoveEvent(MoveEvent),
|
||||
UpdatePaintable(gstreamer::Element),
|
||||
TrackerUpdate(TrackerUpdate),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BoxCoords {
|
||||
pub id: u32,
|
||||
pub x1: u32,
|
||||
pub y1: u32,
|
||||
pub x2: u32,
|
||||
pub y2: u32,
|
||||
}
|
||||
|
||||
impl Display for BoxCoords {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Absolute Box {}, x1: {}, y1: {}, x2: {}, y2: {}",
|
||||
self.id, self.x1, self.y1, self.x2, self.y2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NormalizedBoxCoords {
|
||||
pub id: u32,
|
||||
pub x1: f32,
|
||||
pub y1: f32,
|
||||
pub x2: f32,
|
||||
pub y2: f32,
|
||||
}
|
||||
|
||||
impl NormalizedBoxCoords {
|
||||
fn absolute_coords(&self, width: i32, height: i32) -> BoxCoords {
|
||||
BoxCoords {
|
||||
id: self.id,
|
||||
x1: (self.x1 * width as f32) as u32,
|
||||
y1: (self.y1 * height as f32) as u32,
|
||||
x2: (self.x2 * width as f32) as u32,
|
||||
y2: (self.y2 * height as f32) as u32,
|
||||
}
|
||||
}
|
||||
|
||||
fn area(&self) -> f32 {
|
||||
(self.x2 - self.x1) * (self.y2 - self.y1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NormalizedBoxCoords {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Normalized Box {}, x1: {}, y1: {}, x2: {}, y2: {}",
|
||||
self.id, self.x1, self.y1, self.x2, self.y2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_activate(app: &Application) {
|
||||
let provider = CssProvider::new();
|
||||
provider.load_from_string(include_str!("../../style.css"));
|
||||
|
||||
gtk::style_context_add_provider_for_display(
|
||||
>k::gdk::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("Settings"), &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 left_box = gtk::Box::builder()
|
||||
.width_request(300)
|
||||
.orientation(gtk::Orientation::Vertical)
|
||||
.build();
|
||||
|
||||
// Create a window
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.title("VCC Camera Controller")
|
||||
.show_menubar(true)
|
||||
.default_width(840)
|
||||
.default_height(480)
|
||||
.child(&main_box)
|
||||
.build();
|
||||
|
||||
let rt = runtime.clone();
|
||||
let connections_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, &connections_modal_config);
|
||||
|
||||
connections_modal.window.present();
|
||||
}))
|
||||
.build();
|
||||
|
||||
app.add_action_entries([connections_activate]);
|
||||
|
||||
// Main Event Channel
|
||||
let (to_mec, mec) = async_channel::bounded::<ApplicationEvent>(10);
|
||||
let (to_gui, gui_recv) = async_channel::bounded::<GuiUpdate>(10);
|
||||
let tracker_state = Arc::new(Mutex::new(TrackerState {
|
||||
tracking_id: 0,
|
||||
highlighted_id: None,
|
||||
last_detect: Instant::now(),
|
||||
enabled: true,
|
||||
|
||||
identity_boxes: vec![],
|
||||
update_ids: false,
|
||||
}));
|
||||
|
||||
let coord_config = config.clone();
|
||||
|
||||
runtime.spawn(start_coordinator(
|
||||
mec,
|
||||
to_mec.clone(),
|
||||
to_gui,
|
||||
runtime.clone(),
|
||||
coord_config,
|
||||
));
|
||||
|
||||
let control_panel = ControlPanel::new(tracker_state.clone());
|
||||
control_panel.connect_button_callbacks(to_mec.clone());
|
||||
|
||||
left_box.append(control_panel.get_top_level());
|
||||
|
||||
main_box.append(&left_box);
|
||||
|
||||
let liveview_panel = LiveViewPanel::new(tracker_state.clone(), to_mec.clone());
|
||||
main_box.append(liveview_panel.get_top_level());
|
||||
|
||||
let drawable = gtk::DrawingArea::builder().build();
|
||||
|
||||
let drawable_ts = tracker_state.clone();
|
||||
drawable.set_draw_func(move |_, ctx, width, height| {
|
||||
draw_boxes(width, height, ctx, &drawable_ts);
|
||||
});
|
||||
|
||||
liveview_panel.set_drawable(&drawable);
|
||||
|
||||
let items = control_panel.items.clone();
|
||||
let id_label = control_panel.current_id.clone();
|
||||
let model = control_panel
|
||||
.list_view
|
||||
.model()
|
||||
.expect("The list view should have a model!");
|
||||
|
||||
let tracker_state_2 = tracker_state.clone();
|
||||
|
||||
glib::timeout_add_local(Duration::from_millis(500), move || {
|
||||
#[cfg(feature = "tracker-state-debug")]
|
||||
debug!("Getting lock on tracker state for checking identity boxes");
|
||||
|
||||
// don't update the stringlist until after letting go of the tracker state
|
||||
// due to async interweaving causing a mutex deadlock
|
||||
let mut ids: Option<Vec<String>> = None;
|
||||
let mut current_id: u32 = 0;
|
||||
if let Ok(ts) = tracker_state.lock() {
|
||||
current_id = ts.tracking_id;
|
||||
if ts.update_ids {
|
||||
ids = Some(ts.identity_boxes.iter().map(|t| t.id.to_string()).collect());
|
||||
}
|
||||
}
|
||||
|
||||
if current_id > 0 {
|
||||
id_label.set_text(current_id.to_string().as_str());
|
||||
}
|
||||
|
||||
let current_id: String = current_id.to_string();
|
||||
|
||||
if let Some(mut ids) = ids {
|
||||
let mut active_index = 0;
|
||||
let mut to_delete_indexes: Vec<u32> = vec![];
|
||||
|
||||
// find all indexes where matching item does not exist in
|
||||
// the ids, list, and remove all items in the ids list
|
||||
// that already exists in the stringList
|
||||
for i in 0..items.n_items() {
|
||||
let item = items.string(i).unwrap_or("Empty".into()).to_string();
|
||||
if item == current_id {
|
||||
active_index = i;
|
||||
}
|
||||
if !ids.contains(&item) {
|
||||
to_delete_indexes.push(i);
|
||||
} else if let Some(pos) = ids.iter().position(|x| **x == item) {
|
||||
ids.remove(pos);
|
||||
}
|
||||
}
|
||||
|
||||
if active_index > 0 {
|
||||
model.select_item(active_index, true);
|
||||
}
|
||||
|
||||
// invert the order so we don't have to adjust after each deletion
|
||||
to_delete_indexes.sort();
|
||||
to_delete_indexes.reverse();
|
||||
|
||||
to_delete_indexes.iter().for_each(|x| {
|
||||
items.remove(*x);
|
||||
});
|
||||
items.splice(
|
||||
items.n_items(),
|
||||
0,
|
||||
&ids.iter().map(|x| x.as_str()).collect::<Vec<&str>>()[0..],
|
||||
)
|
||||
}
|
||||
|
||||
glib::ControlFlow::Continue
|
||||
});
|
||||
|
||||
let mut tracker_status_label = liveview_panel.tracker_status_label.clone();
|
||||
let mut tracker_enable_toggle = control_panel
|
||||
.connection_buttons
|
||||
.tracker_enable_toggle
|
||||
.clone();
|
||||
|
||||
glib::spawn_future_local(glib::clone!(@weak drawable => async move {
|
||||
while let Ok(d) = gui_recv.recv().await {
|
||||
drawable.queue_draw();
|
||||
match d {
|
||||
GuiUpdate::MoveEvent(msg) => {
|
||||
liveview_panel.adjustment_label.set_text(
|
||||
format!("X: {:>4} Y: {:>4}", msg.x, msg.y).as_str()
|
||||
);
|
||||
}
|
||||
GuiUpdate::SocketConnected => {
|
||||
control_panel.connection_buttons.camera_connection.set_sensitive(true);
|
||||
control_panel.connection_buttons.camera_connection.set_label("Disconnect Camera");
|
||||
liveview_panel.cam_status_label.set_label("Connected");
|
||||
|
||||
liveview_panel.cam_status_label.set_css_classes(&["YesConnection"]);
|
||||
|
||||
},
|
||||
GuiUpdate::SocketConnecting => {
|
||||
control_panel.connection_buttons.camera_connection.set_sensitive(false);
|
||||
control_panel.connection_buttons.camera_connection.set_label("Please wait");
|
||||
liveview_panel.cam_status_label.set_label("Connecting");
|
||||
|
||||
liveview_panel.cam_status_label.set_css_classes(&["LoadingConnection"]);
|
||||
|
||||
},
|
||||
GuiUpdate::SocketDisconnected => {
|
||||
control_panel.connection_buttons.camera_connection.set_sensitive(true);
|
||||
control_panel.connection_buttons.camera_connection.set_label("Connect to Camera");
|
||||
liveview_panel.cam_status_label.set_label("Not Connected to Camera");
|
||||
|
||||
liveview_panel.cam_status_label.set_css_classes(&["NoConnection"]);
|
||||
}
|
||||
GuiUpdate::UpdatePaintable(sink) => {
|
||||
let paintable = sink.property::<Paintable>("paintable");
|
||||
|
||||
liveview_panel.set_paintable(&paintable);
|
||||
}
|
||||
GuiUpdate::TrackerUpdate(update) => match update {
|
||||
TrackerUpdate::Fail => {}
|
||||
TrackerUpdate::HeaderUpdate(new_header) => {
|
||||
update_tracker_header(new_header, &mut tracker_status_label, &mut tracker_enable_toggle)
|
||||
}
|
||||
TrackerUpdate::Clear => {
|
||||
if let Ok(mut ts) = tracker_state_2.lock() {
|
||||
ts.clear();
|
||||
}
|
||||
}
|
||||
TrackerUpdate::Update(update) => {
|
||||
if let Ok(mut ts) = tracker_state_2.lock() {
|
||||
ts.update_from_boxes(update.boxes);
|
||||
ts.last_detect = update.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
info!("Closing update loop");
|
||||
}));
|
||||
|
||||
window.connect_close_request(move |_| glib::Propagation::Proceed);
|
||||
|
||||
// Present window
|
||||
window.present();
|
||||
}
|
||||
|
||||
fn update_tracker_header(new_header: String, tracker_status_label: &mut gtk::Label, tracker_enable_toggle: &mut gtk::ToggleButton) {
|
||||
tracker_status_label.set_text(&new_header);
|
||||
if new_header.contains("Failed") || new_header.contains("Disconnected") {
|
||||
tracker_status_label.set_css_classes(&["NoConnection"]);
|
||||
|
||||
tracker_enable_toggle.set_label("Connect to Tracker Computer");
|
||||
tracker_enable_toggle.set_active(false);
|
||||
tracker_enable_toggle.set_sensitive(true);
|
||||
} else if new_header.contains("Degraded") {
|
||||
tracker_status_label.set_css_classes(&["LoadingConnection"]);
|
||||
|
||||
tracker_enable_toggle.set_label("Disconnect Tracker Computer");
|
||||
tracker_enable_toggle.set_active(true);
|
||||
tracker_enable_toggle.set_sensitive(true);
|
||||
} else if new_header.contains("Connecting") {
|
||||
tracker_status_label.set_css_classes(&["LoadingConnection"]);
|
||||
|
||||
tracker_enable_toggle.set_label("Please Wait");
|
||||
tracker_enable_toggle.set_active(false);
|
||||
tracker_enable_toggle.set_sensitive(false);
|
||||
} else if new_header.contains("Nominal") {
|
||||
tracker_status_label.set_css_classes(&["YesConnection"]);
|
||||
|
||||
tracker_enable_toggle.set_label("Disconnect Tracker Computer");
|
||||
tracker_enable_toggle.set_active(true);
|
||||
tracker_enable_toggle.set_sensitive(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn draw_boxes(width: i32, height: i32, ctx: &Context, tracker_state: &Arc<Mutex<TrackerState>>) {
|
||||
ctx.set_line_width(5.0);
|
||||
ctx.select_font_face(
|
||||
"Arial",
|
||||
gtk::cairo::FontSlant::Normal,
|
||||
gtk::cairo::FontWeight::Bold,
|
||||
);
|
||||
ctx.set_font_size(24.0);
|
||||
|
||||
#[cfg(feature = "tracker-state-debug")]
|
||||
debug!("Getting tracker state for drawing boxes");
|
||||
if let Ok(ts) = tracker_state.lock() {
|
||||
let active = ts.tracking_id;
|
||||
let highlighted_id = ts.highlighted_id.unwrap_or(0);
|
||||
for nb in ts.identity_boxes.iter() {
|
||||
if nb.id == active {
|
||||
ctx.set_source_rgb(1.0, 0.0, 0.0);
|
||||
} else if nb.id == highlighted_id {
|
||||
ctx.set_source_rgb(0.0, 1.0, 1.0);
|
||||
} else {
|
||||
ctx.set_source_rgb(0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
if nb.id == active && nb.id == highlighted_id {
|
||||
ctx.set_source_rgb(1.0, 1.0, 0.0);
|
||||
}
|
||||
|
||||
let b = nb.absolute_coords(width, height);
|
||||
ctx.rectangle(
|
||||
b.x1 as f64,
|
||||
b.y1 as f64,
|
||||
(b.x2 - b.x1) as f64,
|
||||
(b.y2 - b.y1) as f64,
|
||||
);
|
||||
if let Err(e) = ctx.stroke() {
|
||||
error!("Could not draw cairo stroke: {e}");
|
||||
}
|
||||
ctx.stroke()
|
||||
.expect("GTK Cario context did not have a stroke!");
|
||||
|
||||
ctx.move_to(b.x1 as f64 + 5.0, b.y1 as f64 + 18.0);
|
||||
ctx.set_source_rgb(0.0, 0.0, 0.0);
|
||||
ctx.show_text(&format!("[{}]", b.id))
|
||||
.expect("Couldn't show text!");
|
||||
|
||||
ctx.stroke()
|
||||
.expect("GTK Cario context did not have a stroke!");
|
||||
}
|
||||
} else {
|
||||
error!("Could not get lock on boxes for drawing on the draw area!");
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use gtk::glib::{self, clone};
|
||||
use gtk::{
|
||||
prelude::{BoxExt, ButtonExt, EditableExt, GtkWindowExt},
|
||||
Application, ApplicationWindow, Button, Entry, Label, Window,
|
||||
};
|
||||
use log::{error, info};
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::config::{save_config, AppConfig};
|
||||
|
||||
pub struct ConnectionsModal {
|
||||
pub window: Window,
|
||||
}
|
||||
|
||||
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_clicked(clone!(
|
||||
@strong rt, @weak window,
|
||||
@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();
|
||||
info!("Starting config save");
|
||||
|
||||
{
|
||||
// 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) {
|
||||
error!("Could not save config! {e}");
|
||||
}
|
||||
// FBI!!! OPEN UP!!!!
|
||||
}
|
||||
|
||||
window.close();
|
||||
|
||||
info!("Please nicholas, add a non-crashing parse");
|
||||
}));
|
||||
|
||||
ConnectionsModal {
|
||||
window,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,545 +1 @@
|
|||
/*
|
||||
! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com
|
||||
*/
|
||||
|
||||
/*
|
||||
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
||||
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
||||
*/
|
||||
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
/* 1 */
|
||||
border-width: 0;
|
||||
/* 2 */
|
||||
border-style: solid;
|
||||
/* 2 */
|
||||
border-color: #e5e7eb;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
::before,
|
||||
::after {
|
||||
--tw-content: '';
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use a consistent sensible line-height in all browsers.
|
||||
2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
3. Use a more readable tab size.
|
||||
4. Use the user's configured `sans` font-family by default.
|
||||
5. Use the user's configured `sans` font-feature-settings by default.
|
||||
6. Use the user's configured `sans` font-variation-settings by default.
|
||||
7. Disable tap highlights on iOS
|
||||
*/
|
||||
|
||||
html,
|
||||
:host {
|
||||
line-height: 1.5;
|
||||
/* 1 */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
/* 2 */
|
||||
-moz-tab-size: 4;
|
||||
/* 3 */
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
/* 3 */
|
||||
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
/* 4 */
|
||||
font-feature-settings: normal;
|
||||
/* 5 */
|
||||
font-variation-settings: normal;
|
||||
/* 6 */
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
/* 7 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove the margin in all browsers.
|
||||
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
/* 1 */
|
||||
line-height: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Add the correct height in Firefox.
|
||||
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
|
||||
3. Ensure horizontal rules are visible by default.
|
||||
*/
|
||||
|
||||
hr {
|
||||
height: 0;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 2 */
|
||||
border-top-width: 1px;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct text decoration in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
abbr:where([title]) {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the default font size and weight for headings.
|
||||
*/
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Reset links to optimize for opt-in styling instead of opt-out.
|
||||
*/
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font weight in Edge and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use the user's configured `mono` font-family by default.
|
||||
2. Use the user's configured `mono` font-feature-settings by default.
|
||||
3. Use the user's configured `mono` font-variation-settings by default.
|
||||
4. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
/* 1 */
|
||||
font-feature-settings: normal;
|
||||
/* 2 */
|
||||
font-variation-settings: normal;
|
||||
/* 3 */
|
||||
font-size: 1em;
|
||||
/* 4 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
|
||||
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
|
||||
3. Remove gaps between table borders by default.
|
||||
*/
|
||||
|
||||
table {
|
||||
text-indent: 0;
|
||||
/* 1 */
|
||||
border-color: inherit;
|
||||
/* 2 */
|
||||
border-collapse: collapse;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Change the font styles in all browsers.
|
||||
2. Remove the margin in Firefox and Safari.
|
||||
3. Remove default padding in all browsers.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
/* 1 */
|
||||
font-feature-settings: inherit;
|
||||
/* 1 */
|
||||
font-variation-settings: inherit;
|
||||
/* 1 */
|
||||
font-size: 100%;
|
||||
/* 1 */
|
||||
font-weight: inherit;
|
||||
/* 1 */
|
||||
line-height: inherit;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 1 */
|
||||
margin: 0;
|
||||
/* 2 */
|
||||
padding: 0;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inheritance of text transform in Edge and Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Remove default button styles.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type='button'],
|
||||
[type='reset'],
|
||||
[type='submit'] {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
background-color: transparent;
|
||||
/* 2 */
|
||||
background-image: none;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Use the modern Firefox focus style for all focusable elements.
|
||||
*/
|
||||
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
|
||||
*/
|
||||
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct vertical alignment in Chrome and Firefox.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/*
|
||||
Correct the cursor style of increment and decrement buttons in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-inner-spin-button,
|
||||
::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the odd appearance in Chrome and Safari.
|
||||
2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield;
|
||||
/* 1 */
|
||||
outline-offset: -2px;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
font: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct display in Chrome and Safari.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/*
|
||||
Removes the default spacing and border for appropriate elements.
|
||||
*/
|
||||
|
||||
blockquote,
|
||||
dl,
|
||||
dd,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
figure,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
menu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Reset default styling for dialogs.
|
||||
*/
|
||||
|
||||
dialog {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent resizing textareas horizontally by default.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
|
||||
2. Set the default placeholder color to the user's configured gray 400 color.
|
||||
*/
|
||||
|
||||
input::-moz-placeholder, textarea::-moz-placeholder {
|
||||
opacity: 1;
|
||||
/* 1 */
|
||||
color: #9ca3af;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
opacity: 1;
|
||||
/* 1 */
|
||||
color: #9ca3af;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Set the default cursor for buttons.
|
||||
*/
|
||||
|
||||
button,
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
Make sure disabled buttons don't get the pointer cursor.
|
||||
*/
|
||||
|
||||
:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
|
||||
This can trigger a poorly considered lint error in some tools but is included by design.
|
||||
*/
|
||||
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas,
|
||||
audio,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
display: block;
|
||||
/* 1 */
|
||||
vertical-align: middle;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
*/
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Make elements with the HTML hidden attribute stay hidden by default */
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
*, ::before, ::after {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-gradient-from-position: ;
|
||||
--tw-gradient-via-position: ;
|
||||
--tw-gradient-to-position: ;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
::backdrop {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-gradient-from-position: ;
|
||||
--tw-gradient-via-position: ;
|
||||
--tw-gradient-to-position: ;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
/*! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{-webkit-text-size-adjust:100%;font-feature-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-feature-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.me-0{margin-inline-end:0}.ms-auto{margin-inline-start:auto}.justify-center{justify-content:center}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}
|
|
@ -1,6 +1,14 @@
|
|||
# Functional
|
||||
- Up-direction maxes at -50 instead of 100
|
||||
|
||||
# Tauri Rewrite Todo
|
||||
- propogate changes to the UI from internal state - replace GuiUpdate
|
||||
- Draw boxes onto video feed
|
||||
- UI Can update config options
|
||||
- UI get's 'socket connected/failed' and tracker metrics
|
||||
- UI can select active tracking target
|
||||
|
||||
|
||||
## QoL
|
||||
- Fine tuning the tracking speeds to be non-linear, make sure the pi doesn't have that speed cap (remember could be expecting 6v max speed).
|
||||
- left and right need to hit 50 fast
|
||||
|
@ -9,4 +17,4 @@
|
|||
|
||||
|
||||
## Future ideas
|
||||
- person recognition
|
||||
- person recognition
|
||||
|
|
Loading…
Reference in a new issue