Compare commits
2 Commits
a1e6c6e5fb
...
6c3ce607e0
Author | SHA1 | Date | |
---|---|---|---|
6c3ce607e0 | |||
3ec983cd95 |
301
src-tauri/Cargo.lock
generated
301
src-tauri/Cargo.lock
generated
@ -34,9 +34,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.69"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
@ -70,7 +70,7 @@ dependencies = [
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -135,9 +135,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1"
|
||||
checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
@ -182,7 +182,7 @@ checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"libc",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -228,9 +228,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
version = "0.11.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa"
|
||||
checksum = "a35b255461940a32985c627ce82900867c61db1659764d3675ea81963f72a4c6"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
@ -243,9 +243,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
version = "0.1.49"
|
||||
version = "0.1.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c"
|
||||
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
@ -268,9 +268,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cocoa-foundation"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
|
||||
checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block",
|
||||
@ -361,9 +361,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
|
||||
checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@ -420,7 +420,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"smallvec",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -430,7 +430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -440,7 +440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -464,7 +464,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -475,7 +475,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -488,7 +488,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -599,13 +599,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||
checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"winapi",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -651,7 +651,7 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.2.16",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
@ -707,9 +707,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549"
|
||||
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -722,9 +722,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac"
|
||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@ -732,15 +732,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83"
|
||||
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@ -749,32 +749,32 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91"
|
||||
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6"
|
||||
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2"
|
||||
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879"
|
||||
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
@ -784,9 +784,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -848,7 +848,7 @@ dependencies = [
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -865,7 +865,7 @@ dependencies = [
|
||||
"libc",
|
||||
"pango-sys",
|
||||
"pkg-config",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -877,7 +877,7 @@ dependencies = [
|
||||
"gdk-sys",
|
||||
"glib-sys",
|
||||
"libc",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
"x11",
|
||||
]
|
||||
|
||||
@ -896,9 +896,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.6"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
@ -952,7 +952,7 @@ dependencies = [
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@ -978,9 +978,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glib-macros"
|
||||
version = "0.15.11"
|
||||
version = "0.15.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64"
|
||||
checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck 0.4.1",
|
||||
@ -988,7 +988,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -998,7 +998,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1028,7 +1028,7 @@ checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"libc",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1069,21 +1069,21 @@ dependencies = [
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"pango-sys",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gtk3-macros"
|
||||
version = "0.15.4"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9"
|
||||
checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"proc-macro-crate",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1139,7 +1139,7 @@ dependencies = [
|
||||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1211,9 +1211,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.5"
|
||||
version = "0.24.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945"
|
||||
checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
@ -1224,9 +1224,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
@ -1252,19 +1252,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.6"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
|
||||
checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.4"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
|
||||
checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"io-lifetimes",
|
||||
@ -1382,9 +1383,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@ -1608,7 +1609,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1647,20 +1648,20 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "3.4.0"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21ecf2487e799604735754d2c5896106785987b441b5aee58f242e4d4963179a"
|
||||
checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8"
|
||||
dependencies = [
|
||||
"pathdiff",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.83"
|
||||
version = "0.9.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b"
|
||||
checksum = "3a20eace9dc2d82904039cb76dcf50fb1a0bba071cfd1629720b5d6f1ddba0fa"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
@ -1721,14 +1722,14 @@ dependencies = [
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paris"
|
||||
version = "1.5.13"
|
||||
version = "1.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eaf2319cd71dd9ff38c72bebde61b9ea657134abcf26ae4205f54f772a32810"
|
||||
checksum = "8fecab3723493c7851f292cb060f3ee1c42f19b8d749345d0d7eaf3fd19aa62d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
@ -1748,7 +1749,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.2.16",
|
||||
"smallvec",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
@ -1834,7 +1835,7 @@ dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1848,7 +1849,7 @@ dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1889,14 +1890,14 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "plist"
|
||||
version = "1.4.2"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffac6a51110e97610dd3ac73e34a65b27e56a1e305df41bad1f616d8e1cb22f4"
|
||||
checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"indexmap",
|
||||
"line-wrap",
|
||||
"quick-xml 0.27.1",
|
||||
"quick-xml 0.28.1",
|
||||
"serde",
|
||||
"time",
|
||||
]
|
||||
@ -1944,7 +1945,7 @@ dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
@ -1967,9 +1968,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.52"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
|
||||
checksum = "1d0dd4be24fcdcfeaa12a432d588dc59bbad6cad3510c67e74a2b6b2fc950564"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -1985,9 +1986,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.27.1"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc053f057dd768a56f62cd7e434c42c831d296968997e9ac1f76ea7c2d14c41"
|
||||
checksum = "e5c1a97b1bc42b1d550bfb48d4262153fe400a12bab1511821736f7eac76d7e2"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -2084,9 +2085,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f851a03551ceefd30132e447f07f96cb7011d6b658374f3aed847333adb5559"
|
||||
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
@ -2097,6 +2098,15 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.3"
|
||||
@ -2104,15 +2114,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.2.16",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.1"
|
||||
version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@ -2130,9 +2140,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.28"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
@ -2145,9 +2155,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.9"
|
||||
version = "0.37.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
|
||||
checksum = "d097081ed288dfe45699b72f5b5d648e5f15d64d900c7080273baa20c16a6849"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@ -2227,29 +2237,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.156"
|
||||
version = "1.0.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4"
|
||||
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.156"
|
||||
version = "1.0.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d"
|
||||
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.94"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
|
||||
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
|
||||
dependencies = [
|
||||
"itoa 1.0.6",
|
||||
"ryu",
|
||||
@ -2258,13 +2268,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc"
|
||||
checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2295,7 +2305,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2317,7 +2327,7 @@ checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2476,6 +2486,17 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "5.0.0"
|
||||
@ -2491,14 +2512,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.0.3"
|
||||
version = "6.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff"
|
||||
checksum = "555fc8147af6256f3931a36bb83ad0023240ce9cf2b319dec8236fd1f220b05f"
|
||||
dependencies = [
|
||||
"cfg-expr 0.11.0",
|
||||
"cfg-expr 0.14.0",
|
||||
"heck 0.4.1",
|
||||
"pkg-config",
|
||||
"toml 0.5.11",
|
||||
"toml 0.7.3",
|
||||
"version-compare 0.1.1",
|
||||
]
|
||||
|
||||
@ -2657,7 +2678,7 @@ dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"tauri-codegen",
|
||||
"tauri-utils",
|
||||
]
|
||||
@ -2732,15 +2753,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.4.0"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
|
||||
checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.3.5",
|
||||
"rustix",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2769,6 +2790,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"color_space",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"display-info",
|
||||
"env_logger",
|
||||
@ -2796,22 +2818,22 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2868,14 +2890,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.26.0"
|
||||
version = "1.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
|
||||
checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
@ -2888,13 +2909,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.8.2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
||||
checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2929,9 +2950,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.7"
|
||||
version = "0.19.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274"
|
||||
checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@ -2960,7 +2981,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3019,9 +3040,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.11"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c"
|
||||
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@ -3188,7 +3209,7 @@ dependencies = [
|
||||
"pango-sys",
|
||||
"pkg-config",
|
||||
"soup2-sys",
|
||||
"system-deps 6.0.3",
|
||||
"system-deps 6.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3211,7 +3232,7 @@ checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3305,7 +3326,7 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7"
|
||||
dependencies = [
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
"windows-tokens",
|
||||
]
|
||||
|
||||
@ -3434,9 +3455,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.3.6"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966"
|
||||
checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -31,6 +31,7 @@ toml = "0.7.3"
|
||||
paho-mqtt = "0.12.1"
|
||||
time = {version="0.3.20", features= ["formatting"] }
|
||||
itertools = "0.10.5"
|
||||
core-foundation = "0.9.3"
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||
|
@ -21,6 +21,8 @@ impl ConfigManager {
|
||||
let configs = LedStripConfigGroup::read_config().await.unwrap();
|
||||
let (config_update_sender, config_update_receiver) =
|
||||
tokio::sync::watch::channel(configs.clone());
|
||||
|
||||
config_update_sender.send(configs.clone()).unwrap();
|
||||
ConfigManager {
|
||||
config: Arc::new(RwLock::new(configs)),
|
||||
config_update_receiver,
|
||||
|
@ -1,25 +1,31 @@
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
|
||||
use paris::{info, warn};
|
||||
use tauri::async_runtime::{Mutex, RwLock};
|
||||
use tokio::{sync::watch, time::sleep};
|
||||
use paris::warn;
|
||||
use tauri::async_runtime::RwLock;
|
||||
use tokio::{
|
||||
sync::{broadcast, watch},
|
||||
time::sleep,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ambient_light::{config, ConfigManager},
|
||||
rpc::MqttRpc,
|
||||
screenshot::Screenshot,
|
||||
screenshot_manager::ScreenshotManager, led_color::LedColor,
|
||||
screenshot::LedSamplePoints,
|
||||
screenshot_manager::{self, ScreenshotManager},
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::{LedStripConfigGroup, SamplePointConfig};
|
||||
use super::{LedStripConfigGroup, SamplePointConfig, SamplePointMapper};
|
||||
|
||||
pub struct LedColorsPublisher {
|
||||
sorted_colors_rx: Arc<RwLock<watch::Receiver<Vec<u8>>>>,
|
||||
sorted_colors_tx: Arc<RwLock<watch::Sender<Vec<u8>>>>,
|
||||
colors_rx: Arc<RwLock<watch::Receiver<Vec<LedColor>>>>,
|
||||
colors_tx: Arc<RwLock<watch::Sender<Vec<LedColor>>>>,
|
||||
colors_rx: Arc<RwLock<watch::Receiver<Vec<u8>>>>,
|
||||
colors_tx: Arc<RwLock<watch::Sender<Vec<u8>>>>,
|
||||
display_colors_rx: Arc<RwLock<broadcast::Receiver<(u32, Vec<u8>)>>>,
|
||||
display_colors_tx: Arc<RwLock<broadcast::Sender<(u32, Vec<u8>)>>>,
|
||||
inner_tasks_version: Arc<RwLock<usize>>,
|
||||
}
|
||||
|
||||
impl LedColorsPublisher {
|
||||
@ -29,6 +35,7 @@ impl LedColorsPublisher {
|
||||
|
||||
let (sorted_tx, sorted_rx) = watch::channel(Vec::new());
|
||||
let (tx, rx) = watch::channel(Vec::new());
|
||||
let (display_colors_tx, display_colors_rx) = broadcast::channel(8);
|
||||
|
||||
LED_COLORS_PUBLISHER_GLOBAL
|
||||
.get_or_init(|| async {
|
||||
@ -37,91 +44,289 @@ impl LedColorsPublisher {
|
||||
sorted_colors_tx: Arc::new(RwLock::new(sorted_tx)),
|
||||
colors_rx: Arc::new(RwLock::new(rx)),
|
||||
colors_tx: Arc::new(RwLock::new(tx)),
|
||||
display_colors_rx: Arc::new(RwLock::new(display_colors_rx)),
|
||||
display_colors_tx: Arc::new(RwLock::new(display_colors_tx)),
|
||||
inner_tasks_version: Arc::new(RwLock::new(0)),
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn start(&self) {
|
||||
let sorted_colors_tx = self.sorted_colors_tx.clone();
|
||||
let colors_tx = self.colors_tx.clone();
|
||||
fn start_one_display_colors_fetcher(
|
||||
&self,
|
||||
display_id: u32,
|
||||
sample_points: Vec<Vec<LedSamplePoints>>,
|
||||
) {
|
||||
let display_colors_tx = self.display_colors_tx.clone();
|
||||
let internal_tasks_version = self.inner_tasks_version.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let display_colors_tx = display_colors_tx.read().await.clone();
|
||||
|
||||
let colors = screenshot_manager::get_display_colors(display_id, &sample_points);
|
||||
|
||||
if let Err(err) = colors {
|
||||
warn!("Failed to get colors: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut start: tokio::time::Instant = tokio::time::Instant::now();
|
||||
let mut interval = tokio::time::interval(Duration::from_millis(66));
|
||||
let init_version = internal_tasks_version.read().await.clone();
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
tokio::time::sleep(Duration::from_millis(1)).await;
|
||||
|
||||
if internal_tasks_version.read().await.clone() != init_version {
|
||||
log::info!(
|
||||
"inner task version changed, stop. {} != {}",
|
||||
internal_tasks_version.read().await.clone(),
|
||||
init_version
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// log::info!("tick: {}ms", start.elapsed().as_millis());
|
||||
start = tokio::time::Instant::now();
|
||||
let colors = screenshot_manager::get_display_colors(display_id, &sample_points);
|
||||
|
||||
if let Err(err) = colors {
|
||||
warn!("Failed to get colors: {}", err);
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
continue;
|
||||
}
|
||||
|
||||
let colors = colors.unwrap();
|
||||
|
||||
let color_len = colors.len();
|
||||
|
||||
match display_colors_tx.send((
|
||||
display_id,
|
||||
colors
|
||||
.into_iter()
|
||||
.map(|color| color.get_rgb())
|
||||
.flatten()
|
||||
.collect::<Vec<_>>(),
|
||||
)) {
|
||||
Ok(_) => {
|
||||
// log::info!("sent colors: {:?}", color_len);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Failed to send display_colors: {}", err);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn start_all_colors_worker(&self, display_ids: Vec<u32>, mappers: Vec<SamplePointMapper>) {
|
||||
let sorted_colors_tx = self.sorted_colors_tx.clone();
|
||||
let colors_tx = self.colors_tx.clone();
|
||||
let display_colors_rx = self.display_colors_rx.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
for _ in 0..10 {
|
||||
let mut rx = display_colors_rx.read().await.resubscribe();
|
||||
|
||||
let sorted_colors_tx = sorted_colors_tx.write().await;
|
||||
let colors_tx = colors_tx.write().await;
|
||||
let screenshot_manager = ScreenshotManager::global().await;
|
||||
|
||||
let config_manager = ConfigManager::global().await;
|
||||
let config_receiver = config_manager.clone_config_update_receiver();
|
||||
let configs = config_receiver.borrow().clone();
|
||||
let configs = Self::get_colors_configs(&configs).await;
|
||||
|
||||
let mut some_screenshot_receiver_is_none = false;
|
||||
let mut all_colors: Vec<Option<Vec<u8>>> = vec![None; display_ids.len()];
|
||||
let mut start: tokio::time::Instant = tokio::time::Instant::now();
|
||||
|
||||
log::info!("start all_colors_worker");
|
||||
loop {
|
||||
let mut screenshots = Vec::new();
|
||||
// log::info!("display_colors_rx changed");
|
||||
let color_info = rx.recv().await;
|
||||
|
||||
for rx in configs.screenshot_receivers.to_owned() {
|
||||
let mut rx = rx.lock_owned().await;
|
||||
if rx.is_none() {
|
||||
some_screenshot_receiver_is_none = true;
|
||||
warn!("screenshot receiver is none");
|
||||
continue;
|
||||
}
|
||||
|
||||
let rx = rx.as_mut().unwrap();
|
||||
|
||||
if let Err(err) = rx.changed().await {
|
||||
warn!("rx changed error: {}", err);
|
||||
continue;
|
||||
}
|
||||
// log::info!("screenshot updated");
|
||||
|
||||
let screenshot = rx.borrow().clone();
|
||||
|
||||
screenshots.push(screenshot);
|
||||
}
|
||||
|
||||
let colors = screenshot_manager
|
||||
.get_all_colors(&configs.sample_point_groups, &screenshots)
|
||||
.await;
|
||||
|
||||
let sorted_colors =
|
||||
ScreenshotManager::get_sorted_colors(&colors, &configs.mappers).await;
|
||||
|
||||
match colors_tx.send(colors) {
|
||||
Ok(_) => {
|
||||
// log::info!("colors updated");
|
||||
}
|
||||
Err(_) => {
|
||||
warn!("colors update failed");
|
||||
if let Err(err) = color_info {
|
||||
match err {
|
||||
broadcast::error::RecvError::Closed => {
|
||||
break;
|
||||
}
|
||||
broadcast::error::RecvError::Lagged(_) => {
|
||||
warn!("display_colors_rx lagged");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
let (display_id, colors) = color_info.unwrap();
|
||||
|
||||
match sorted_colors_tx.send(sorted_colors) {
|
||||
Ok(_) => {
|
||||
// log::info!("colors updated");
|
||||
}
|
||||
Err(_) => {
|
||||
warn!("colors update failed");
|
||||
}
|
||||
let index = display_ids.iter().position(|id| *id == display_id);
|
||||
|
||||
if index.is_none() {
|
||||
warn!("display id not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
if some_screenshot_receiver_is_none {
|
||||
info!("some screenshot receiver is none. reload.");
|
||||
sleep(Duration::from_millis(1000)).await;
|
||||
break;
|
||||
}
|
||||
all_colors[index.unwrap()] = Some(colors);
|
||||
|
||||
if config_receiver.has_changed().unwrap_or(true) {
|
||||
info!("config changed. reload.");
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
break;
|
||||
if all_colors.iter().all(|color| color.is_some()) {
|
||||
let flatten_colors = all_colors
|
||||
.clone()
|
||||
.into_iter()
|
||||
.flat_map(|c| c.unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match colors_tx.send(flatten_colors.clone()) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
warn!("Failed to send colors: {}", err);
|
||||
}
|
||||
};
|
||||
|
||||
let sorted_colors =
|
||||
ScreenshotManager::get_sorted_colors(&flatten_colors, &mappers);
|
||||
|
||||
match sorted_colors_tx.send(sorted_colors) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
warn!("Failed to send sorted colors: {}", err);
|
||||
}
|
||||
};
|
||||
log::info!("tick: {}ms", start.elapsed().as_millis());
|
||||
start = tokio::time::Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn start(&self) {
|
||||
let inner_tasks_version = self.inner_tasks_version.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let publisher = Self::global().await;
|
||||
|
||||
let mut inner_tasks_version = inner_tasks_version.write().await;
|
||||
*inner_tasks_version = inner_tasks_version.overflowing_add(1).0;
|
||||
|
||||
let config_manager = ConfigManager::global().await;
|
||||
let mut config_receiver = config_manager.clone_config_update_receiver();
|
||||
|
||||
log::info!("waiting for config update...");
|
||||
|
||||
while config_receiver.changed().await.is_ok() {
|
||||
log::info!("config updated, restart inner tasks...");
|
||||
let configs = config_receiver.borrow().clone();
|
||||
let configs = Self::get_colors_configs(&configs).await;
|
||||
|
||||
if let Err(err) = configs {
|
||||
warn!("Failed to get configs: {}", err);
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
continue;
|
||||
}
|
||||
|
||||
let configs = configs.unwrap();
|
||||
|
||||
for sample_point_group in configs.sample_point_groups.clone() {
|
||||
let display_id = sample_point_group.display_id;
|
||||
let sample_points = sample_point_group.points;
|
||||
|
||||
publisher.start_one_display_colors_fetcher(display_id, sample_points);
|
||||
}
|
||||
|
||||
let display_ids = configs.sample_point_groups;
|
||||
publisher.start_all_colors_worker(
|
||||
display_ids.iter().map(|c| c.display_id).collect(),
|
||||
configs.mappers,
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// tokio::spawn(async move {
|
||||
// loop {
|
||||
// let sorted_colors_tx = sorted_colors_tx.write().await;
|
||||
// let colors_tx = colors_tx.write().await;
|
||||
// let screenshot_manager = ScreenshotManager::global().await;
|
||||
|
||||
// let config_manager = ConfigManager::global().await;
|
||||
// let config_receiver = config_manager.clone_config_update_receiver();
|
||||
// let configs = config_receiver.borrow().clone();
|
||||
// let configs = Self::get_colors_configs(&configs).await;
|
||||
|
||||
// if let Err(err) = configs {
|
||||
// warn!("Failed to get configs: {}", err);
|
||||
// sleep(Duration::from_millis(100)).await;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// let configs = configs.unwrap();
|
||||
|
||||
// let mut merged_screenshot_receiver =
|
||||
// screenshot_manager.clone_merged_screenshot_rx().await;
|
||||
|
||||
// let mut screenshots = HashMap::new();
|
||||
|
||||
// // let mut start = tokio::time::Instant::now();
|
||||
|
||||
// loop {
|
||||
// let screenshot = merged_screenshot_receiver.recv().await;
|
||||
|
||||
// if let Err(err) = screenshot {
|
||||
// match err {
|
||||
// tokio::sync::broadcast::error::RecvError::Closed => {
|
||||
// warn!("closed");
|
||||
// continue;
|
||||
// }
|
||||
// tokio::sync::broadcast::error::RecvError::Lagged(_) => {
|
||||
// warn!("lagged");
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// let screenshot = screenshot.unwrap();
|
||||
// // log::info!("got screenshot: {:?}", screenshot.display_id);
|
||||
|
||||
// screenshots.insert(screenshot.display_id, screenshot);
|
||||
|
||||
// if screenshots.len() == configs.sample_point_groups.len() {
|
||||
// // log::info!("{}", start.elapsed().as_millis().to_string());
|
||||
// {
|
||||
// let screenshots = configs
|
||||
// .sample_point_groups
|
||||
// .iter()
|
||||
// .map(|strip| screenshots.get(&strip.display_id).unwrap())
|
||||
// .collect::<Vec<_>>();
|
||||
|
||||
// let colors = screenshot_manager
|
||||
// .get_all_colors(&configs.sample_point_groups, &screenshots)
|
||||
// .await;
|
||||
|
||||
// let sorted_colors =
|
||||
// ScreenshotManager::get_sorted_colors(&colors, &configs.mappers)
|
||||
// .await;
|
||||
|
||||
// match colors_tx.send(colors) {
|
||||
// Ok(_) => {
|
||||
// // log::info!("colors updated");
|
||||
// }
|
||||
// Err(_) => {
|
||||
// warn!("colors update failed");
|
||||
// }
|
||||
// }
|
||||
|
||||
// match sorted_colors_tx.send(sorted_colors) {
|
||||
// Ok(_) => {
|
||||
// // log::info!("colors updated");
|
||||
// }
|
||||
// Err(_) => {
|
||||
// warn!("colors update failed");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // screenshots.clear();
|
||||
// // start = tokio::time::Instant::now();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
let rx = self.sorted_colors_rx.clone();
|
||||
tokio::spawn(async move {
|
||||
@ -139,7 +344,7 @@ impl LedColorsPublisher {
|
||||
|
||||
match Self::send_colors(colors).await {
|
||||
Ok(_) => {
|
||||
log::info!("colors sent. len: {}", len);
|
||||
// log::info!("colors sent. len: {}", len);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("colors send failed: {}", err);
|
||||
@ -158,12 +363,11 @@ impl LedColorsPublisher {
|
||||
pub async fn clone_sorted_colors_receiver(&self) -> watch::Receiver<Vec<u8>> {
|
||||
self.sorted_colors_rx.read().await.clone()
|
||||
}
|
||||
|
||||
pub async fn get_colors_configs(configs: &LedStripConfigGroup) -> AllColorConfig {
|
||||
pub async fn get_colors_configs(
|
||||
configs: &LedStripConfigGroup,
|
||||
) -> anyhow::Result<AllColorConfig> {
|
||||
let screenshot_manager = ScreenshotManager::global().await;
|
||||
|
||||
let channels = screenshot_manager.channels.read().await;
|
||||
|
||||
let display_ids = configs
|
||||
.strips
|
||||
.iter()
|
||||
@ -173,67 +377,83 @@ impl LedColorsPublisher {
|
||||
|
||||
let mappers = configs.mappers.clone();
|
||||
|
||||
let mut local_rx_list = Vec::new();
|
||||
let mut colors_configs = Vec::new();
|
||||
|
||||
for display_id in display_ids.clone().iter() {
|
||||
let display_id = *display_id;
|
||||
let mut merged_screenshot_receiver = screenshot_manager.clone_merged_screenshot_rx().await;
|
||||
|
||||
let channel = channels.get(&display_id);
|
||||
let channel = match channel {
|
||||
Some(channel) => Some(channel.clone()),
|
||||
None => None,
|
||||
};
|
||||
local_rx_list.push(Arc::new(Mutex::new(channel.clone())));
|
||||
let mut screenshots = HashMap::new();
|
||||
|
||||
let led_strip_configs: Vec<_> = configs
|
||||
.strips
|
||||
.iter()
|
||||
.filter(|c| c.display_id == display_id)
|
||||
.collect();
|
||||
loop {
|
||||
log::info!("waiting merged screenshot...");
|
||||
let screenshot = merged_screenshot_receiver.recv().await;
|
||||
|
||||
let rx = channel;
|
||||
if rx.is_none() {
|
||||
warn!("no channel for display_id: {}", display_id);
|
||||
continue;
|
||||
if let Err(err) = screenshot {
|
||||
match err {
|
||||
tokio::sync::broadcast::error::RecvError::Closed => {
|
||||
warn!("closed");
|
||||
continue;
|
||||
}
|
||||
tokio::sync::broadcast::error::RecvError::Lagged(_) => {
|
||||
warn!("lagged");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if led_strip_configs.len() == 0 {
|
||||
warn!("no led strip config for display_id: {}", display_id);
|
||||
continue;
|
||||
}
|
||||
let mut rx = rx.unwrap().to_owned();
|
||||
let screenshot = screenshot.unwrap();
|
||||
// log::info!("got screenshot: {:?}", screenshot.display_id);
|
||||
|
||||
if rx.changed().await.is_ok() {
|
||||
let screenshot = rx.borrow().clone();
|
||||
log::info!("screenshot updated: {:?}", display_id);
|
||||
screenshots.insert(screenshot.display_id, screenshot);
|
||||
|
||||
let points: Vec<_> = led_strip_configs
|
||||
.iter()
|
||||
.map(|config| screenshot.get_sample_points(&config))
|
||||
.flatten()
|
||||
.collect();
|
||||
if screenshots.len() == display_ids.len() {
|
||||
for display_id in display_ids {
|
||||
let led_strip_configs: Vec<_> = configs
|
||||
.strips
|
||||
.iter()
|
||||
.filter(|c| c.display_id == display_id)
|
||||
.collect();
|
||||
|
||||
let colors_config = config::SamplePointConfig { display_id, points };
|
||||
if led_strip_configs.len() == 0 {
|
||||
warn!("no led strip config for display_id: {}", display_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
colors_configs.push(colors_config);
|
||||
let screenshot = screenshots.get(&display_id).unwrap();
|
||||
log::debug!("screenshot updated: {:?}", display_id);
|
||||
|
||||
let points: Vec<_> = led_strip_configs
|
||||
.iter()
|
||||
.map(|config| screenshot.get_sample_points(&config))
|
||||
.collect();
|
||||
|
||||
let colors_config = DisplaySamplePointGroup { display_id, points };
|
||||
|
||||
colors_configs.push(colors_config);
|
||||
}
|
||||
|
||||
return Ok(AllColorConfig {
|
||||
sample_point_groups: colors_configs,
|
||||
mappers,
|
||||
// screenshot_receivers: local_rx_list,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return AllColorConfig {
|
||||
sample_point_groups: colors_configs,
|
||||
mappers,
|
||||
screenshot_receivers: local_rx_list,
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn clone_colors_receiver(&self) -> watch::Receiver<Vec<LedColor>> {
|
||||
pub async fn clone_colors_receiver(&self) -> watch::Receiver<Vec<u8>> {
|
||||
self.colors_rx.read().await.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AllColorConfig {
|
||||
pub sample_point_groups: Vec<SamplePointConfig>,
|
||||
pub sample_point_groups: Vec<DisplaySamplePointGroup>,
|
||||
pub mappers: Vec<config::SamplePointMapper>,
|
||||
pub screenshot_receivers: Vec<Arc<Mutex<Option<watch::Receiver<Screenshot>>>>>,
|
||||
// pub screenshot_receivers: Vec<watch::Receiver<Screenshot>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DisplaySamplePointGroup {
|
||||
pub display_id: u32,
|
||||
pub points: Vec<Vec<LedSamplePoints>>,
|
||||
}
|
||||
|
@ -57,21 +57,6 @@ fn list_display_info() -> Result<String, String> {
|
||||
Ok(json_str)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn subscribe_encoded_screenshot_updated(
|
||||
window: tauri::Window,
|
||||
display_id: u32,
|
||||
) -> Result<(), String> {
|
||||
let screenshot_manager = ScreenshotManager::global().await;
|
||||
screenshot_manager
|
||||
.subscribe_encoded_screenshot_updated(window, display_id)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("subscribe_encoded_screenshot_updated: {}", err);
|
||||
err.to_string()
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn read_led_strip_configs() -> Result<LedStripConfigGroup, String> {
|
||||
let config = ambient_light::LedStripConfigGroup::read_config()
|
||||
@ -121,7 +106,7 @@ async fn get_one_edge_colors(
|
||||
if let Some(rx) = channels.get(&display_id) {
|
||||
let rx = rx.clone();
|
||||
let screenshot = rx.borrow().clone();
|
||||
let bytes = screenshot.bytes.read().await;
|
||||
let bytes = screenshot.bytes.read().await.to_owned();
|
||||
let colors =
|
||||
Screenshot::get_one_edge_colors(&sample_points, &bytes, screenshot.bytes_per_row);
|
||||
Ok(colors)
|
||||
@ -201,7 +186,6 @@ async fn main() {
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
greet,
|
||||
list_display_info,
|
||||
subscribe_encoded_screenshot_updated,
|
||||
read_led_strip_configs,
|
||||
write_led_strip_configs,
|
||||
get_led_strips_sample_points,
|
||||
|
@ -1,8 +1,9 @@
|
||||
use std::iter;
|
||||
use std::cell::RefCell;
|
||||
use std::{iter, cell::Ref};
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::async_runtime::RwLock;
|
||||
use tauri::async_runtime::{RwLock, Mutex};
|
||||
|
||||
use crate::{ambient_light::LedStripConfig, led_color::LedColor};
|
||||
|
||||
@ -14,9 +15,10 @@ pub struct Screenshot {
|
||||
pub bytes_per_row: usize,
|
||||
pub bytes: Arc<RwLock<Vec<u8>>>,
|
||||
pub scale_factor: f32,
|
||||
pub sample_points: ScreenSamplePoints,
|
||||
}
|
||||
|
||||
static SINGLE_AXIS_POINTS: usize = 5;
|
||||
|
||||
impl Screenshot {
|
||||
pub fn new(
|
||||
display_id: u32,
|
||||
@ -25,7 +27,6 @@ impl Screenshot {
|
||||
bytes_per_row: usize,
|
||||
bytes: Vec<u8>,
|
||||
scale_factor: f32,
|
||||
sample_points: ScreenSamplePoints,
|
||||
) -> Self {
|
||||
Self {
|
||||
display_id,
|
||||
@ -34,7 +35,6 @@ impl Screenshot {
|
||||
bytes_per_row,
|
||||
bytes: Arc::new(RwLock::new(bytes)),
|
||||
scale_factor,
|
||||
sample_points,
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,10 +44,10 @@ impl Screenshot {
|
||||
|
||||
match config.border {
|
||||
crate::ambient_light::Border::Top => {
|
||||
Self::get_one_edge_sample_points(height / 8, width, config.len, 5)
|
||||
Self::get_one_edge_sample_points(height / 18, width, config.len, SINGLE_AXIS_POINTS)
|
||||
}
|
||||
crate::ambient_light::Border::Bottom => {
|
||||
let points = Self::get_one_edge_sample_points(height / 9, width, config.len, 5);
|
||||
let points = Self::get_one_edge_sample_points(height / 18, width, config.len, SINGLE_AXIS_POINTS);
|
||||
points
|
||||
.into_iter()
|
||||
.map(|groups| -> Vec<Point> {
|
||||
@ -56,7 +56,7 @@ impl Screenshot {
|
||||
.collect()
|
||||
}
|
||||
crate::ambient_light::Border::Left => {
|
||||
let points = Self::get_one_edge_sample_points(width / 16, height, config.len, 5);
|
||||
let points = Self::get_one_edge_sample_points(width / 32, height, config.len, SINGLE_AXIS_POINTS);
|
||||
points
|
||||
.into_iter()
|
||||
.map(|groups| -> Vec<Point> {
|
||||
@ -65,7 +65,7 @@ impl Screenshot {
|
||||
.collect()
|
||||
}
|
||||
crate::ambient_light::Border::Right => {
|
||||
let points = Self::get_one_edge_sample_points(width / 16, height, config.len, 5);
|
||||
let points = Self::get_one_edge_sample_points(width / 32, height, config.len, SINGLE_AXIS_POINTS);
|
||||
points
|
||||
.into_iter()
|
||||
.map(|groups| -> Vec<Point> {
|
||||
@ -112,46 +112,6 @@ impl Screenshot {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub async fn get_colors(&self) -> DisplayColorsOfLedStrips {
|
||||
let bitmap = self.bytes.read().await;
|
||||
|
||||
let top =
|
||||
Self::get_one_edge_colors(&self.sample_points.top, bitmap.as_ref(), self.bytes_per_row)
|
||||
.into_iter()
|
||||
.flat_map(|color| color.get_rgb())
|
||||
.collect();
|
||||
let bottom = Self::get_one_edge_colors(
|
||||
&self.sample_points.bottom,
|
||||
bitmap.as_ref(),
|
||||
self.bytes_per_row,
|
||||
)
|
||||
.into_iter()
|
||||
.flat_map(|color| color.get_rgb())
|
||||
.collect();
|
||||
let left = Self::get_one_edge_colors(
|
||||
&self.sample_points.left,
|
||||
bitmap.as_ref(),
|
||||
self.bytes_per_row,
|
||||
)
|
||||
.into_iter()
|
||||
.flat_map(|color| color.get_rgb())
|
||||
.collect();
|
||||
let right = Self::get_one_edge_colors(
|
||||
&self.sample_points.right,
|
||||
bitmap.as_ref(),
|
||||
self.bytes_per_row,
|
||||
)
|
||||
.into_iter()
|
||||
.flat_map(|color| color.get_rgb())
|
||||
.collect();
|
||||
DisplayColorsOfLedStrips {
|
||||
top,
|
||||
bottom,
|
||||
left,
|
||||
right,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_one_edge_colors(
|
||||
sample_points_of_leds: &Vec<LedSamplePoints>,
|
||||
bitmap: &Vec<u8>,
|
||||
@ -176,6 +136,30 @@ impl Screenshot {
|
||||
colors
|
||||
}
|
||||
|
||||
pub fn get_one_edge_colors_by_cg_image(
|
||||
sample_points_of_leds: &Vec<LedSamplePoints>,
|
||||
bitmap: core_foundation::data::CFData,
|
||||
bytes_per_row: usize,
|
||||
) -> Vec<LedColor> {
|
||||
let mut colors = vec![];
|
||||
for led_points in sample_points_of_leds {
|
||||
let mut r = 0.0;
|
||||
let mut g = 0.0;
|
||||
let mut b = 0.0;
|
||||
let len = led_points.len() as f64;
|
||||
for (x, y) in led_points {
|
||||
// log::info!("x: {}, y: {}, bytes_per_row: {}", x, y, bytes_per_row);
|
||||
let position = x * 4 + y * bytes_per_row;
|
||||
b += bitmap[position] as f64;
|
||||
g += bitmap[position + 1] as f64;
|
||||
r += bitmap[position + 2] as f64;
|
||||
}
|
||||
let color = LedColor::new((r / len) as u8, (g / len) as u8, (b / len) as u8);
|
||||
colors.push(color);
|
||||
}
|
||||
colors
|
||||
}
|
||||
|
||||
pub async fn get_colors_by_sample_points(
|
||||
&self,
|
||||
points: &Vec<LedSamplePoints>,
|
||||
|
@ -1,21 +1,24 @@
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use core_graphics::display::{
|
||||
kCGNullWindowID, kCGWindowImageDefault, kCGWindowListOptionOnScreenOnly, CGDisplay,
|
||||
};
|
||||
use paris::{error, info, warn};
|
||||
use tauri::{async_runtime::RwLock, Window};
|
||||
use tokio::sync::{watch, OnceCell};
|
||||
use core_graphics::geometry::{CGPoint, CGRect, CGSize};
|
||||
use paris::warn;
|
||||
use tauri::async_runtime::RwLock;
|
||||
use tokio::sync::{broadcast, watch, OnceCell};
|
||||
use tokio::time::{self, Duration};
|
||||
|
||||
use crate::screenshot::LedSamplePoints;
|
||||
use crate::{
|
||||
ambient_light::{SamplePointConfig, SamplePointMapper},
|
||||
led_color::LedColor,
|
||||
screenshot::{ScreenSamplePoints, Screenshot, ScreenshotPayload},
|
||||
screenshot::{ScreenSamplePoints, Screenshot},
|
||||
};
|
||||
|
||||
pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Screenshot> {
|
||||
log::debug!("take_screenshot");
|
||||
// let start_at = std::time::Instant::now();
|
||||
|
||||
let cg_display = CGDisplay::new(display_id);
|
||||
let cg_image = CGDisplay::screenshot(
|
||||
@ -25,7 +28,6 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
|
||||
kCGWindowImageDefault,
|
||||
)
|
||||
.ok_or_else(|| anyhow::anyhow!("Display#{}: take screenshot failed", display_id))?;
|
||||
// println!("take screenshot took {}ms", start_at.elapsed().as_millis());
|
||||
|
||||
let buffer = cg_image.data();
|
||||
let bytes_per_row = cg_image.bytes_per_row();
|
||||
@ -33,8 +35,7 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
|
||||
let height = cg_image.height();
|
||||
let width = cg_image.width();
|
||||
|
||||
let mut bytes = vec![0u8; buffer.len() as usize];
|
||||
bytes.copy_from_slice(&buffer);
|
||||
let bytes = buffer.bytes().to_owned();
|
||||
|
||||
Ok(Screenshot::new(
|
||||
display_id,
|
||||
@ -43,18 +44,77 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
|
||||
bytes_per_row,
|
||||
bytes,
|
||||
scale_factor,
|
||||
ScreenSamplePoints {
|
||||
top: vec![],
|
||||
bottom: vec![],
|
||||
left: vec![],
|
||||
right: vec![],
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_display_colors(
|
||||
display_id: u32,
|
||||
sample_points: &Vec<Vec<LedSamplePoints>>,
|
||||
) -> anyhow::Result<Vec<LedColor>> {
|
||||
log::debug!("take_screenshot");
|
||||
let cg_display = CGDisplay::new(display_id);
|
||||
|
||||
let mut colors = vec![];
|
||||
let start_at = std::time::Instant::now();
|
||||
for points in sample_points {
|
||||
if points.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
let start_x = points[0][0].0;
|
||||
let start_y = points[0][0].1;
|
||||
let end_x = points.last().unwrap().last().unwrap().0;
|
||||
let end_y = points.last().unwrap().last().unwrap().1;
|
||||
|
||||
let (start_x, end_x) = (usize::min(start_x, end_x), usize::max(start_x, end_x));
|
||||
let (start_y, end_y) = (usize::min(start_y, end_y), usize::max(start_y, end_y));
|
||||
|
||||
let origin = CGPoint {
|
||||
x: start_x as f64 + cg_display.bounds().origin.x,
|
||||
y: start_y as f64 + cg_display.bounds().origin.y,
|
||||
};
|
||||
let size = CGSize {
|
||||
width: (end_x - start_x + 1) as f64,
|
||||
height: (end_y - start_y + 1) as f64,
|
||||
};
|
||||
|
||||
let cg_image = CGDisplay::screenshot(
|
||||
CGRect::new(&origin, &size),
|
||||
kCGWindowListOptionOnScreenOnly,
|
||||
kCGNullWindowID,
|
||||
kCGWindowImageDefault,
|
||||
)
|
||||
.ok_or_else(|| anyhow::anyhow!("Display#{}: take screenshot failed", display_id))?;
|
||||
|
||||
let bitmap = cg_image.data();
|
||||
|
||||
let points = points
|
||||
.iter()
|
||||
.map(|points| {
|
||||
points
|
||||
.iter()
|
||||
.map(|(x, y)| (*x - start_x, *y - start_y))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut part_colors =
|
||||
Screenshot::get_one_edge_colors_by_cg_image(&points, bitmap, cg_image.bytes_per_row());
|
||||
colors.append(&mut part_colors);
|
||||
}
|
||||
|
||||
// if display_id == 4849664 {
|
||||
// log::info!(
|
||||
// "======= get_display_colors {} took {}ms",
|
||||
// display_id,
|
||||
// start_at.elapsed().as_millis()
|
||||
// );
|
||||
// }
|
||||
Ok(colors)
|
||||
}
|
||||
|
||||
pub struct ScreenshotManager {
|
||||
pub channels: Arc<RwLock<HashMap<u32, watch::Receiver<Screenshot>>>>,
|
||||
encode_listeners: Arc<RwLock<HashMap<u32, Vec<Window>>>>,
|
||||
merged_screenshot_tx: Arc<RwLock<broadcast::Sender<Screenshot>>>,
|
||||
}
|
||||
|
||||
impl ScreenshotManager {
|
||||
@ -64,10 +124,10 @@ impl ScreenshotManager {
|
||||
SCREENSHOT_MANAGER
|
||||
.get_or_init(|| async {
|
||||
let channels = Arc::new(RwLock::new(HashMap::new()));
|
||||
let encode_listeners = Arc::new(RwLock::new(HashMap::new()));
|
||||
let (merged_screenshot_tx, _) = broadcast::channel::<Screenshot>(2);
|
||||
Self {
|
||||
channels,
|
||||
encode_listeners,
|
||||
merged_screenshot_tx: Arc::new(RwLock::new(merged_screenshot_tx)),
|
||||
}
|
||||
})
|
||||
.await
|
||||
@ -83,6 +143,7 @@ impl ScreenshotManager {
|
||||
|
||||
fn start_one(&self, display_id: u32, scale_factor: f32) -> anyhow::Result<()> {
|
||||
let channels = self.channels.to_owned();
|
||||
let merged_screenshot_tx = self.merged_screenshot_tx.clone();
|
||||
tokio::spawn(async move {
|
||||
let screenshot = take_screenshot(display_id, scale_factor);
|
||||
|
||||
@ -90,125 +151,51 @@ impl ScreenshotManager {
|
||||
warn!("take_screenshot_loop: {}", screenshot.err().unwrap());
|
||||
return;
|
||||
}
|
||||
let mut interval = time::interval(Duration::from_millis(3300));
|
||||
let mut start = tokio::time::Instant::now();
|
||||
|
||||
let screenshot = screenshot.unwrap();
|
||||
let (tx, rx) = watch::channel(screenshot);
|
||||
let (screenshot_tx, screenshot_rx) = watch::channel(screenshot);
|
||||
{
|
||||
let channels = channels.clone();
|
||||
let mut channels = channels.write().await;
|
||||
channels.insert(display_id, rx);
|
||||
channels.insert(display_id, screenshot_rx.clone());
|
||||
}
|
||||
|
||||
let merged_screenshot_tx = merged_screenshot_tx.read().await.clone();
|
||||
|
||||
loop {
|
||||
Self::take_screenshot_loop(display_id, scale_factor, &tx).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
|
||||
start = tokio::time::Instant::now();
|
||||
Self::take_screenshot_loop(
|
||||
display_id,
|
||||
scale_factor,
|
||||
&screenshot_tx,
|
||||
&merged_screenshot_tx,
|
||||
)
|
||||
.await;
|
||||
interval.tick().await;
|
||||
tokio::time::sleep(Duration::from_millis(1)).await;
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn subscribe_encoded_screenshot_updated(
|
||||
&self,
|
||||
window: Window,
|
||||
display_id: u32,
|
||||
) -> anyhow::Result<()> {
|
||||
let channels = self.channels.to_owned();
|
||||
let encode_listeners = self.encode_listeners.to_owned();
|
||||
// log::info!("subscribe_encoded_screenshot_updated. {}", display_id);
|
||||
|
||||
{
|
||||
let encode_listeners = encode_listeners.read().await;
|
||||
let listening_windows = encode_listeners.get(&display_id);
|
||||
if listening_windows.is_some() && listening_windows.unwrap().contains(&window) {
|
||||
log::debug!("subscribe_encoded_screenshot_updated: already listening. display#{}, window#{}", display_id, window.label());
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
{
|
||||
encode_listeners
|
||||
.write()
|
||||
.await
|
||||
.entry(display_id)
|
||||
.or_default()
|
||||
.push(window);
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
info!("subscribe_encoded_screenshot_updated: start");
|
||||
let channels = channels.read().await;
|
||||
let rx = channels.get(&display_id);
|
||||
if rx.is_none() {
|
||||
error!(
|
||||
"subscribe_encoded_screenshot_updated: can not find display_id {}",
|
||||
display_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
let mut rx = rx.unwrap().clone();
|
||||
loop {
|
||||
if let Err(err) = rx.changed().await {
|
||||
error!(
|
||||
"subscribe_encoded_screenshot_updated: can not wait rx {}",
|
||||
err
|
||||
);
|
||||
break;
|
||||
}
|
||||
let encode_listeners = encode_listeners.read().await;
|
||||
let windows = encode_listeners.get(&display_id);
|
||||
if windows.is_none() || windows.unwrap().is_empty() {
|
||||
info!("subscribe_encoded_screenshot_updated: no listener, stop");
|
||||
break;
|
||||
}
|
||||
let screenshot = rx.borrow().clone();
|
||||
// let base64_image = Self::encode_screenshot_to_base64(&screenshot).await;
|
||||
let height = screenshot.height;
|
||||
let width = screenshot.width;
|
||||
|
||||
// if base64_image.is_err() {
|
||||
// error!(
|
||||
// "subscribe_encoded_screenshot_updated: encode_screenshot_to_base64 error {}",
|
||||
// base64_image.err().unwrap()
|
||||
// );
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// let base64_image = base64_image.unwrap();
|
||||
for window in windows.unwrap().into_iter() {
|
||||
// let base64_image = base64_image.clone();
|
||||
let payload = ScreenshotPayload {
|
||||
display_id,
|
||||
// base64_image,
|
||||
height,
|
||||
width,
|
||||
};
|
||||
if let Err(err) = window.emit("encoded-screenshot-updated", payload) {
|
||||
error!("subscribe_encoded_screenshot_updated: emit error {}", err)
|
||||
} else {
|
||||
log::debug!(
|
||||
"subscribe_encoded_screenshot_updated: emit success. display#{}",
|
||||
display_id
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn unsubscribe_encoded_screenshot_updated(&self, display_id: u32) -> anyhow::Result<()> {
|
||||
let channels = self.channels.to_owned();
|
||||
let mut channels = channels.write().await;
|
||||
channels.remove(&display_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn take_screenshot_loop(
|
||||
display_id: u32,
|
||||
scale_factor: f32,
|
||||
tx: &watch::Sender<Screenshot>,
|
||||
screenshot_tx: &watch::Sender<Screenshot>,
|
||||
merged_screenshot_tx: &broadcast::Sender<Screenshot>,
|
||||
) {
|
||||
let screenshot = take_screenshot(display_id, scale_factor);
|
||||
if let Ok(screenshot) = screenshot {
|
||||
tx.send(screenshot).unwrap();
|
||||
match merged_screenshot_tx.send(screenshot.clone()) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
// warn!("take_screenshot_loop: merged_screenshot_tx.send failed. display#{}. err: {}", display_id, err);
|
||||
}
|
||||
}
|
||||
screenshot_tx.send(screenshot).unwrap();
|
||||
// log::info!("take_screenshot_loop: send success. display#{}", display_id)
|
||||
} else {
|
||||
warn!("take_screenshot_loop: {}", screenshot.err().unwrap());
|
||||
@ -218,7 +205,7 @@ impl ScreenshotManager {
|
||||
pub async fn get_all_colors(
|
||||
&self,
|
||||
configs: &Vec<SamplePointConfig>,
|
||||
screenshots: &Vec<Screenshot>,
|
||||
screenshots: &Vec<&Screenshot>,
|
||||
) -> Vec<LedColor> {
|
||||
let mut all_colors = vec![];
|
||||
|
||||
@ -232,10 +219,7 @@ impl ScreenshotManager {
|
||||
all_colors
|
||||
}
|
||||
|
||||
pub async fn get_sorted_colors(
|
||||
colors: &Vec<LedColor>,
|
||||
mappers: &Vec<SamplePointMapper>,
|
||||
) -> Vec<u8> {
|
||||
pub fn get_sorted_colors(colors: &Vec<u8>, mappers: &Vec<SamplePointMapper>) -> Vec<u8> {
|
||||
let total_leds = mappers
|
||||
.iter()
|
||||
.map(|mapper| usize::max(mapper.start, mapper.end))
|
||||
@ -255,36 +239,36 @@ impl ScreenshotManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if color_index + group.start.abs_diff(group.end) > colors.len() {
|
||||
if color_index + group.start.abs_diff(group.end) * 3 > colors.len(){
|
||||
warn!(
|
||||
"get_sorted_colors: color_index out of range. color_index: {}, strip len: {}, colors.len(): {}",
|
||||
color_index,
|
||||
color_index / 3,
|
||||
group.start.abs_diff(group.end),
|
||||
colors.len()
|
||||
colors.len() / 3
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if group.end > group.start {
|
||||
for i in group.start..group.end {
|
||||
let rgb = colors[color_index].get_rgb();
|
||||
color_index += 1;
|
||||
|
||||
global_colors[i * 3] = rgb[0];
|
||||
global_colors[i * 3 + 1] = rgb[1];
|
||||
global_colors[i * 3 + 2] = rgb[2];
|
||||
global_colors[i * 3] = colors[color_index +0];
|
||||
global_colors[i * 3 + 1] = colors[color_index +1];
|
||||
global_colors[i * 3 + 2] = colors[color_index +2];
|
||||
color_index += 3;
|
||||
}
|
||||
} else {
|
||||
for i in (group.end..group.start).rev() {
|
||||
let rgb = colors[color_index].get_rgb();
|
||||
color_index += 1;
|
||||
|
||||
global_colors[i * 3] = rgb[0];
|
||||
global_colors[i * 3 + 1] = rgb[1];
|
||||
global_colors[i * 3 + 2] = rgb[2];
|
||||
global_colors[i * 3] = colors[color_index +0];
|
||||
global_colors[i * 3 + 1] = colors[color_index +1];
|
||||
global_colors[i * 3 + 2] = colors[color_index +2];
|
||||
color_index += 3;
|
||||
}
|
||||
}
|
||||
});
|
||||
global_colors
|
||||
}
|
||||
|
||||
pub async fn clone_merged_screenshot_rx(&self) -> broadcast::Receiver<Screenshot> {
|
||||
self.merged_screenshot_tx.read().await.subscribe()
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createContext, createEffect, onCleanup } from 'solid-js';
|
||||
import { createEffect, onCleanup } from 'solid-js';
|
||||
import { invoke } from '@tauri-apps/api/tauri';
|
||||
import { DisplayView } from './components/display-view';
|
||||
import { DisplayListContainer } from './components/display-list-container';
|
||||
@ -44,7 +44,7 @@ function App() {
|
||||
|
||||
// listen to led_colors_changed event
|
||||
createEffect(() => {
|
||||
const unlisten = listen<Array<string>>('led_colors_changed', (event) => {
|
||||
const unlisten = listen<Uint8ClampedArray>('led_colors_changed', (event) => {
|
||||
const colors = event.payload;
|
||||
|
||||
setLedStripStore({
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
@ -8,7 +7,6 @@ import {
|
||||
createSignal,
|
||||
For,
|
||||
JSX,
|
||||
onCleanup,
|
||||
splitProps,
|
||||
useContext,
|
||||
} from 'solid-js';
|
||||
@ -77,9 +75,15 @@ export const LedStripPart: Component<LedStripPartProps> = (props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const offset = mapper.pos;
|
||||
const offset = mapper.pos * 3;
|
||||
|
||||
const colors = new Array(localProps.config.len).fill(null).map((_, i) => {
|
||||
const index = offset + i * 3;
|
||||
return `rgb(${ledStripStore.colors[index]}, ${ledStripStore.colors[index + 1]}, ${
|
||||
ledStripStore.colors[index + 2]
|
||||
})`;
|
||||
});
|
||||
|
||||
const colors = ledStripStore.colors.slice(offset, offset + localProps.config.len);
|
||||
setColors(colors);
|
||||
});
|
||||
|
||||
|
@ -115,31 +115,21 @@ const SorterItem: Component<{ strip: LedStripConfig; mapper: LedStripPixelMapper
|
||||
// update fullLeds
|
||||
createEffect(() => {
|
||||
const fullLeds = new Array(ledStripStore.totalLedCount).fill(null);
|
||||
|
||||
// if (props.mapper.start < props.mapper.end) {
|
||||
// for (
|
||||
// let i = props.mapper.start, j = props.mapper.pos;
|
||||
// i < props.mapper.end;
|
||||
// i++, j++
|
||||
// ) {
|
||||
// fullLeds[i] = ledStripStore.colors[j];
|
||||
// }
|
||||
// } else {
|
||||
// for (
|
||||
// let i = props.mapper.start, j = props.mapper.pos;
|
||||
// i >= props.mapper.end;
|
||||
// i--, j++
|
||||
// ) {
|
||||
// fullLeds[i] = ledStripStore.colors[j];
|
||||
// }
|
||||
// }
|
||||
// 优化上面的代码
|
||||
const colors = ledStripStore.colors;
|
||||
|
||||
const { start, end, pos } = props.mapper;
|
||||
const isForward = start < end;
|
||||
const step = isForward ? 1 : -1;
|
||||
for (let i = start, j = pos; i !== end; i += step, j++) {
|
||||
fullLeds[i] = ledStripStore.colors[j];
|
||||
let c1 = `rgb(${Math.floor(colors[j * 3] * 0.8)}, ${Math.floor(
|
||||
colors[j * 3 + 1] * 0.8,
|
||||
)}, ${Math.floor(colors[j * 3 + 2] * 0.8)})`;
|
||||
let c2 = `rgb(${Math.min(Math.floor(colors[j * 3] * 1.2), 255)}, ${Math.min(
|
||||
Math.floor(colors[j * 3 + 1] * 1.2),
|
||||
255,
|
||||
)}, ${Math.min(Math.floor(colors[j * 3 + 2] * 1.2), 255)})`;
|
||||
|
||||
fullLeds[i] = `linear-gradient(70deg, ${c1} 10%, ${c2})`;
|
||||
}
|
||||
|
||||
setFullLeds(fullLeds);
|
||||
@ -197,7 +187,6 @@ const SorterResult: Component = () => {
|
||||
|
||||
return `linear-gradient(70deg, ${c1} 10%, ${c2})`;
|
||||
});
|
||||
console.log(fullLeds);
|
||||
setFullLeds(fullLeds);
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri';
|
||||
import {
|
||||
Component,
|
||||
@ -30,6 +29,7 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
width: number;
|
||||
height: number;
|
||||
} | null>(null);
|
||||
const [hidden, setHidden] = createSignal(false);
|
||||
|
||||
const resetSize = () => {
|
||||
const aspectRatio = canvas.width / canvas.height;
|
||||
@ -103,6 +103,10 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
|
||||
(async () => {
|
||||
while (!stopped) {
|
||||
if (hidden()) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
continue;
|
||||
}
|
||||
await frame();
|
||||
}
|
||||
})();
|
||||
@ -128,6 +132,26 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
});
|
||||
});
|
||||
|
||||
// update hidden
|
||||
createEffect(() => {
|
||||
const hide = () => {
|
||||
setHidden(true);
|
||||
console.log('hide');
|
||||
};
|
||||
const show = () => {
|
||||
setHidden(false);
|
||||
console.log('show');
|
||||
};
|
||||
|
||||
window.addEventListener('focus', show);
|
||||
window.addEventListener('blur', hide);
|
||||
|
||||
onCleanup(() => {
|
||||
window.removeEventListener('focus', show);
|
||||
window.removeEventListener('blur', hide);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={root!}
|
||||
|
@ -4,7 +4,7 @@ import { LedStripConfig, LedStripPixelMapper } from '../models/led-strip-config'
|
||||
export const [ledStripStore, setLedStripStore] = createStore({
|
||||
strips: new Array<LedStripConfig>(),
|
||||
mappers: new Array<LedStripPixelMapper>(),
|
||||
colors: new Array<string>(),
|
||||
colors: new Uint8ClampedArray(),
|
||||
sortedColors: new Uint8ClampedArray(),
|
||||
get totalLedCount() {
|
||||
return Math.max(0, ...ledStripStore.mappers.map((m) => Math.max(m.start, m.end)));
|
||||
|
Loading…
Reference in New Issue
Block a user