webcam support is Better but not there loll
This commit is contained in:
parent
3bc7021e18
commit
9e836927c4
8 changed files with 343 additions and 239 deletions
258
Cargo.lock
generated
258
Cargo.lock
generated
|
@ -44,15 +44,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alsa"
|
||||
version = "0.7.0"
|
||||
|
@ -237,7 +228,7 @@ checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
|
|||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"miniz_oxide 0.6.2",
|
||||
"object",
|
||||
|
@ -254,8 +245,8 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
|||
name = "baseview"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cocoa",
|
||||
"core-foundation",
|
||||
"cocoa 0.24.1",
|
||||
"core-foundation 0.9.3",
|
||||
"keyboard-types",
|
||||
"nix 0.22.3",
|
||||
"objc",
|
||||
|
@ -400,6 +391,12 @@ dependencies = [
|
|||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -425,16 +422,6 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c044c781163c001b913cd018fc95a628c50d0d2dfea8bca77dad71edb16e37"
|
||||
dependencies = [
|
||||
"clang-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.6.1"
|
||||
|
@ -494,6 +481,21 @@ version = "0.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c49e86fc36d5704151f5996b7b3795385f50ce09e3be0f47a0cfde869681cf8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block",
|
||||
"core-foundation 0.7.0",
|
||||
"core-graphics 0.19.2",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.24.1"
|
||||
|
@ -503,8 +505,8 @@ dependencies = [
|
|||
"bitflags",
|
||||
"block",
|
||||
"cocoa-foundation",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"core-foundation 0.9.3",
|
||||
"core-graphics 0.22.3",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"objc",
|
||||
|
@ -518,7 +520,7 @@ checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"block",
|
||||
"core-foundation",
|
||||
"core-foundation 0.9.3",
|
||||
"core-graphics-types",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
|
@ -563,6 +565,16 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
|
||||
dependencies = [
|
||||
"core-foundation-sys 0.7.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
|
@ -579,12 +591,30 @@ version = "0.6.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation 0.7.0",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics"
|
||||
version = "0.22.3"
|
||||
|
@ -592,7 +622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation 0.9.3",
|
||||
"core-graphics-types",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
|
@ -605,11 +635,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation 0.9.3",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-media-sys"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "273bf3fc5bf51fd06a7766a84788c1540b6527130a0bce39e00567d6ab9f31f1"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"core-foundation-sys 0.7.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-video-sys"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"core-foundation-sys 0.7.0",
|
||||
"core-graphics 0.19.2",
|
||||
"libc",
|
||||
"metal 0.18.0",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coreaudio-rs"
|
||||
version = "0.11.2"
|
||||
|
@ -636,7 +691,7 @@ version = "0.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a7847ca018a67204508b77cb9e6de670125075f7464fff5f673023378fa34f5"
|
||||
dependencies = [
|
||||
"core-foundation",
|
||||
"core-foundation 0.9.3",
|
||||
"core-foundation-sys 0.8.4",
|
||||
"coremidi-sys",
|
||||
]
|
||||
|
@ -681,7 +736,7 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -690,7 +745,7 @@ version = "0.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
|
@ -704,7 +759,7 @@ version = "0.5.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
|
@ -714,7 +769,7 @@ version = "0.8.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
@ -726,7 +781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
"memoffset 0.8.0",
|
||||
"scopeguard",
|
||||
|
@ -738,7 +793,7 @@ version = "0.3.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
|
@ -748,7 +803,7 @@ version = "0.8.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1111,7 +1166,7 @@ version = "0.2.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
|
@ -1137,7 +1192,7 @@ version = "0.5.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd745b0cb1a207756e8fabacf5623066ad6aa543ea0be4bab34e897e6bbe24f9"
|
||||
dependencies = [
|
||||
"core-foundation",
|
||||
"core-foundation 0.9.3",
|
||||
"io-kit-sys",
|
||||
"js-sys",
|
||||
"libc",
|
||||
|
@ -1367,7 +1422,7 @@ version = "0.1.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
|
@ -1546,7 +1601,7 @@ version = "0.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -1591,7 +1646,7 @@ version = "0.4.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1612,12 +1667,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
|
@ -1651,6 +1700,21 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "metal"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e198a0ee42bdbe9ef2c09d0b9426f3b2b47d90d93a4a9b0395c4cea605e92dc0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block",
|
||||
"cocoa 0.20.2",
|
||||
"core-graphics 0.19.2",
|
||||
"foreign-types",
|
||||
"log",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "metal"
|
||||
version = "0.24.0"
|
||||
|
@ -1857,10 +1921,10 @@ dependencies = [
|
|||
"backtrace",
|
||||
"baseview",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"clap",
|
||||
"clap-sys",
|
||||
"core-foundation",
|
||||
"core-foundation 0.9.3",
|
||||
"cpal",
|
||||
"crossbeam",
|
||||
"jack",
|
||||
|
@ -1910,7 +1974,7 @@ checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
]
|
||||
|
@ -1922,7 +1986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
]
|
||||
|
@ -1935,7 +1999,7 @@ checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
|
|||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
]
|
||||
|
@ -1947,7 +2011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"static_assertions",
|
||||
]
|
||||
|
@ -1960,13 +2024,28 @@ checksum = "143c7e68ab35542605825430efb8acaf977de40d4256ebfeed6324b35a6ba89a"
|
|||
dependencies = [
|
||||
"flume",
|
||||
"image",
|
||||
"nokhwa-bindings-macos",
|
||||
"nokhwa-core",
|
||||
"opencv",
|
||||
"paste",
|
||||
"rgb",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nokhwa-bindings-macos"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cb2044194c8cc0286a0bfae023caeb434c32900ffe3f3d08565362306437be8"
|
||||
dependencies = [
|
||||
"block",
|
||||
"cocoa-foundation",
|
||||
"core-media-sys",
|
||||
"core-video-sys",
|
||||
"flume",
|
||||
"nokhwa-core",
|
||||
"objc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nokhwa-core"
|
||||
version = "0.1.0"
|
||||
|
@ -1976,7 +2055,6 @@ dependencies = [
|
|||
"bytes",
|
||||
"image",
|
||||
"mozjpeg",
|
||||
"opencv",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -2144,49 +2222,13 @@ version = "1.17.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "opencv"
|
||||
version = "0.74.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33bd2442759725e3ce38862b32acb1999de51e9f3f4310f3b01b7f0623091d74"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"clang",
|
||||
"dunce",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"opencv-binding-generator",
|
||||
"pkg-config",
|
||||
"rgb",
|
||||
"semver",
|
||||
"shlex",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opencv-binding-generator"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "082d8259f4a7ea455e3553f9a1bc817520eb465a153bbf5ec0cb0b73590ee28d"
|
||||
dependencies = [
|
||||
"clang",
|
||||
"clang-sys",
|
||||
"dunce",
|
||||
"maplit",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "orbclient"
|
||||
version = "0.3.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e9829e16c5e112e94efb5e2ad1fe17f8c1c99bb0fcdc8c65c44e935d904767d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"redox_syscall 0.2.16",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
|
@ -2217,7 +2259,7 @@ version = "0.9.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.2.16",
|
||||
"smallvec",
|
||||
|
@ -2448,8 +2490,6 @@ version = "1.8.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
|
@ -2593,12 +2633,6 @@ dependencies = [
|
|||
"tiny-skia",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
|
||||
[[package]]
|
||||
name = "send_wrapper"
|
||||
version = "0.6.0"
|
||||
|
@ -2864,7 +2898,7 @@ dependencies = [
|
|||
"arrayref",
|
||||
"arrayvec",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"png",
|
||||
"tiny-skia-path",
|
||||
]
|
||||
|
@ -2974,12 +3008,6 @@ version = "1.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
|
@ -3064,7 +3092,7 @@ version = "0.2.84"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
|
@ -3089,7 +3117,7 @@ version = "0.4.34"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
|
@ -3236,7 +3264,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d745a1b6d91d85c33defbb29f0eee0450e1d2614d987e14bf6baf26009d132d7"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"log",
|
||||
"naga 0.11.0",
|
||||
|
@ -3323,7 +3351,7 @@ dependencies = [
|
|||
"khronos-egl",
|
||||
"libloading",
|
||||
"log",
|
||||
"metal",
|
||||
"metal 0.24.0",
|
||||
"naga 0.10.0",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
|
@ -3365,7 +3393,7 @@ dependencies = [
|
|||
"libc",
|
||||
"libloading",
|
||||
"log",
|
||||
"metal",
|
||||
"metal 0.24.0",
|
||||
"naga 0.11.0",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
|
@ -3637,8 +3665,8 @@ dependencies = [
|
|||
"android-activity",
|
||||
"bitflags",
|
||||
"cfg_aliases",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"core-foundation 0.9.3",
|
||||
"core-graphics 0.22.3",
|
||||
"dispatch",
|
||||
"instant",
|
||||
"libc",
|
||||
|
|
|
@ -14,7 +14,7 @@ gilrs = "0.10"
|
|||
cpal = "0.15"
|
||||
futures = "0.3"
|
||||
ctrlc = "3.2.5"
|
||||
nokhwa = { version = "0.10.3", features = ["input-opencv"], optional = true }
|
||||
nokhwa = { version = "0.10.3", features = ["input-avfoundation"], optional = true }
|
||||
send_wrapper = { version = "0.6.0", optional = true }
|
||||
winit = "0.28"
|
||||
winit_input_helper = "0.14"
|
||||
|
|
|
@ -8,40 +8,95 @@ use send_wrapper::SendWrapper;
|
|||
|
||||
pub struct Webcam {
|
||||
camera: SendWrapper<Camera>,
|
||||
buffer: [u8; 128 * 128],
|
||||
scaled_buffer: [u8; 128 * 128],
|
||||
}
|
||||
|
||||
impl Webcam {
|
||||
pub fn new() -> Self {
|
||||
let format = RequestedFormat::new::<pixel_format::LumaAFormat>(
|
||||
let format = RequestedFormat::new::<pixel_format::RgbFormat>(
|
||||
RequestedFormatType::AbsoluteHighestResolution,
|
||||
);
|
||||
|
||||
let mut camera = SendWrapper::new(
|
||||
Camera::new(CameraIndex::Index(0), format).expect("Couldn't open camera"),
|
||||
);
|
||||
camera
|
||||
.set_frame_format(nokhwa::utils::FrameFormat::YUYV)
|
||||
.expect("couldnt set frame format to yuyv");
|
||||
|
||||
if let Ok(formats) = camera.compatible_fourcc() {
|
||||
if formats.is_empty() {
|
||||
println!("no compatible frame formats listed");
|
||||
}
|
||||
for f in formats {
|
||||
println!("compatible frame format: {f:?}");
|
||||
}
|
||||
} else {
|
||||
println!("couldnt get frame formats")
|
||||
}
|
||||
|
||||
println!("current camera format: {:?}", camera.camera_format());
|
||||
|
||||
if let Ok(formats) = camera.compatible_camera_formats() {
|
||||
if formats.is_empty() {
|
||||
println!("camera formats is empty");
|
||||
}
|
||||
for f in formats {
|
||||
println!("supports camera format {f:?}");
|
||||
}
|
||||
} else {
|
||||
println!("couldnt get camera formats");
|
||||
}
|
||||
|
||||
Self {
|
||||
camera: SendWrapper::new(Camera::new(CameraIndex::Index(0), format).unwrap()),
|
||||
buffer: [0; 128 * 128],
|
||||
camera,
|
||||
scaled_buffer: [0; 128 * 128],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PocketCamera for Webcam {
|
||||
fn get_image(&mut self) -> [u8; 128 * 128] {
|
||||
self.buffer
|
||||
self.scaled_buffer
|
||||
}
|
||||
|
||||
fn begin_capture(&mut self) {
|
||||
let height = self.camera.resolution().height() as usize;
|
||||
let _height = self.camera.resolution().height() as usize;
|
||||
let width = self.camera.resolution().width() as usize;
|
||||
let frame = self.camera.frame_raw().expect("couldn't get frame");
|
||||
// let frame = self.camera.frame_raw().expect("couldn't get frame");
|
||||
self.camera
|
||||
.set_frame_format(nokhwa::utils::FrameFormat::RAWRGB)
|
||||
.unwrap();
|
||||
let frame = self.camera.frame().expect("couldn't get frame");
|
||||
println!("source format: {:?}", frame.source_frame_format());
|
||||
|
||||
let decoded = frame
|
||||
.decode_image::<pixel_format::RgbFormat>()
|
||||
.expect("couldn't decode image");
|
||||
let pixels = decoded.pixels().collect::<Vec<_>>();
|
||||
|
||||
for y in 0..128 {
|
||||
for x in 0..128 {
|
||||
self.buffer[y * 128 + x] = frame[((y * width) + x) * 2];
|
||||
self.scaled_buffer[y * 128 + x] = pixels[((y * width) + x) * 2][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
self.camera.open_stream().expect("couldn't open stream");
|
||||
self.camera
|
||||
.set_frame_format(nokhwa::utils::FrameFormat::YUYV)
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"opened stream! current format: {:?}",
|
||||
self.camera.camera_format()
|
||||
);
|
||||
// let format = RequestedFormat::new::<pixel_format::LumaFormat>(
|
||||
// RequestedFormatType::AbsoluteHighestResolution,
|
||||
// );
|
||||
// self.camera
|
||||
// .set_camera_requset(format)
|
||||
// .expect("couldn't set format again");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use gb_emu_lib::{
|
||||
connect::{EmulatorMessage, NoCamera},
|
||||
EmulatorCore,
|
||||
};
|
||||
use gb_emu_lib::connect::{EmulatorCoreTrait, EmulatorMessage};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{self, Write},
|
||||
|
@ -9,8 +6,6 @@ use std::{
|
|||
sync::mpsc::Receiver,
|
||||
};
|
||||
|
||||
use crate::window::WindowRenderer;
|
||||
|
||||
pub enum CommandErr {
|
||||
InvalidCommand,
|
||||
InvalidArgument,
|
||||
|
@ -52,7 +47,7 @@ impl FromStr for Commands {
|
|||
}
|
||||
|
||||
pub struct Debugger {
|
||||
core: EmulatorCore<[u8; 4], WindowRenderer, NoCamera>,
|
||||
core: Box<dyn EmulatorCoreTrait>,
|
||||
debug_receiver: Receiver<EmulatorMessage>,
|
||||
stepping: bool,
|
||||
last_command: String,
|
||||
|
@ -62,7 +57,7 @@ pub struct Debugger {
|
|||
|
||||
impl Debugger {
|
||||
pub fn new(
|
||||
core: EmulatorCore<[u8; 4], WindowRenderer, NoCamera>,
|
||||
core: Box<dyn EmulatorCoreTrait>,
|
||||
debug_receiver: Receiver<EmulatorMessage>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -5,7 +5,9 @@ use camera::Webcam;
|
|||
use clap::{ArgGroup, Parser};
|
||||
use debug::Debugger;
|
||||
use gb_emu_lib::{
|
||||
connect::{EmulatorMessage, EmulatorOptions, NoCamera, RomFile, SerialTarget},
|
||||
connect::{
|
||||
EmulatorCoreTrait, EmulatorMessage, EmulatorOptions, NoCamera, RomFile, SerialTarget,
|
||||
},
|
||||
EmulatorCore,
|
||||
};
|
||||
use gilrs::Gilrs;
|
||||
|
@ -65,6 +67,9 @@ struct Args {
|
|||
/// Show BootROM
|
||||
#[arg(long)]
|
||||
show_bootrom: bool,
|
||||
// /// Use webcam as Pocket Camera emulation
|
||||
// #[arg(short, long)]
|
||||
// camera: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -93,38 +98,54 @@ fn main() {
|
|||
|
||||
let (output, _stream) = audio::create_output(args.mute);
|
||||
|
||||
#[cfg(feature = "webcam")]
|
||||
let camera = Webcam::new();
|
||||
#[cfg(not(feature = "webcam"))]
|
||||
let camera = NoCamera::default();
|
||||
let window = WindowRenderer::new(factor, Some(Gilrs::new().unwrap()));
|
||||
let rom = RomFile::Path(args.rom);
|
||||
|
||||
let options = EmulatorOptions::new_with_camera(
|
||||
WindowRenderer::new(factor, Some(Gilrs::new().unwrap())),
|
||||
RomFile::Path(args.rom),
|
||||
output,
|
||||
camera,
|
||||
)
|
||||
.with_save_path(args.save)
|
||||
.with_serial_target(if args.connect_serial {
|
||||
SerialTarget::Stdout
|
||||
let options = EmulatorOptions::new(window, rom, output)
|
||||
.with_save_path(args.save)
|
||||
.with_serial_target(if args.connect_serial {
|
||||
SerialTarget::Stdout
|
||||
} else {
|
||||
SerialTarget::None
|
||||
})
|
||||
.with_bootrom(args.bootrom.map(RomFile::Path), args.show_bootrom)
|
||||
.with_no_save(args.no_save)
|
||||
.with_tile_window(tile_window)
|
||||
.with_cgb_mode(!args.dmg);
|
||||
|
||||
// let core: Box<dyn EmulatorCoreTrait> = if args.camera {
|
||||
// Box::new(EmulatorCore::init(receiver, options, Webcam::new()))
|
||||
// } else {
|
||||
// Box::new(EmulatorCore::init(receiver, options, NoCamera::default()))
|
||||
// };
|
||||
|
||||
#[cfg(not(feature = "camera"))]
|
||||
let core: Box<dyn EmulatorCoreTrait> =
|
||||
Box::new(EmulatorCore::init(receiver, options, NoCamera::default()));
|
||||
#[cfg(feature = "camera")]
|
||||
let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new()));
|
||||
|
||||
let mut handler = if args.debug {
|
||||
EmulatorHandler::Debug(Debugger::new(core, debug_receiver))
|
||||
} else {
|
||||
SerialTarget::None
|
||||
})
|
||||
.with_bootrom(args.bootrom.map(RomFile::Path), args.show_bootrom)
|
||||
.with_no_save(args.no_save)
|
||||
.with_tile_window(tile_window)
|
||||
.with_cgb_mode(!args.dmg);
|
||||
EmulatorHandler::Normal(core)
|
||||
};
|
||||
|
||||
let mut core = EmulatorCore::init(receiver, options);
|
||||
loop {
|
||||
handler.run();
|
||||
}
|
||||
}
|
||||
|
||||
if args.debug {
|
||||
let mut debugger = Debugger::new(core, debug_receiver);
|
||||
loop {
|
||||
debugger.step();
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
core.run();
|
||||
enum EmulatorHandler {
|
||||
Debug(Debugger),
|
||||
Normal(Box<dyn EmulatorCoreTrait>),
|
||||
}
|
||||
|
||||
impl EmulatorHandler {
|
||||
fn run(&mut self) {
|
||||
match self {
|
||||
EmulatorHandler::Debug(ref mut debugger) => debugger.step(),
|
||||
EmulatorHandler::Normal(ref mut core) => core.run(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ use async_ringbuf::AsyncHeapConsumer;
|
|||
use futures::executor;
|
||||
use gb_emu_lib::{
|
||||
connect::{
|
||||
AudioOutput, DownsampleType, EmulatorMessage, EmulatorOptions, JoypadButtons, NoCamera,
|
||||
RomFile, SerialTarget,
|
||||
AudioOutput, DownsampleType, EmulatorCoreTrait, EmulatorMessage, EmulatorOptions,
|
||||
JoypadButtons, NoCamera, RomFile, SerialTarget,
|
||||
},
|
||||
EmulatorCore,
|
||||
};
|
||||
|
@ -266,7 +266,7 @@ impl Plugin for GameboyEmu {
|
|||
.with_serial_target(serial_target)
|
||||
.force_no_save();
|
||||
|
||||
EmulatorCore::init(receiver, options)
|
||||
EmulatorCore::init(receiver, options, NoCamera::default())
|
||||
};
|
||||
|
||||
emulator_core.run_until_buffer_full();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::processor::memory::mmio::gpu::Colour;
|
||||
pub use crate::processor::memory::mmio::gpu::Colour;
|
||||
pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState};
|
||||
pub use crate::processor::memory::mmio::serial::SerialTarget;
|
||||
pub use crate::processor::CpuSaveState;
|
||||
|
@ -155,18 +155,17 @@ where
|
|||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct EmulatorOptions<ColourFormat, R, C>
|
||||
pub struct EmulatorOptions<ColourFormat, R>
|
||||
where
|
||||
ColourFormat: From<Colour> + Clone,
|
||||
R: Renderer<ColourFormat>,
|
||||
C: PocketCamera + Send + 'static,
|
||||
{
|
||||
pub(crate) window: R,
|
||||
pub(crate) tile_window: Option<R>,
|
||||
pub(crate) rom: RomFile,
|
||||
pub(crate) output: AudioOutput,
|
||||
pub(crate) save_path: Option<String>,
|
||||
pub(crate) camera: C,
|
||||
|
||||
pub(crate) no_save: bool,
|
||||
pub(crate) bootrom: Option<RomFile>,
|
||||
pub(crate) show_bootrom: bool,
|
||||
|
@ -175,30 +174,18 @@ where
|
|||
spooky: PhantomData<ColourFormat>,
|
||||
}
|
||||
|
||||
impl<ColourFormat, R> EmulatorOptions<ColourFormat, R, NoCamera>
|
||||
impl<ColourFormat, R> EmulatorOptions<ColourFormat, R>
|
||||
where
|
||||
ColourFormat: From<Colour> + Clone,
|
||||
R: Renderer<ColourFormat>,
|
||||
{
|
||||
pub fn new(window: R, rom: RomFile, output: AudioOutput) -> Self {
|
||||
Self::new_with_camera(window, rom, output, NoCamera::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<ColourFormat, R, C> EmulatorOptions<ColourFormat, R, C>
|
||||
where
|
||||
ColourFormat: From<Colour> + Clone,
|
||||
R: Renderer<ColourFormat>,
|
||||
C: PocketCamera + Send + 'static,
|
||||
{
|
||||
pub fn new_with_camera(window: R, rom: RomFile, output: AudioOutput, camera: C) -> Self {
|
||||
Self {
|
||||
window,
|
||||
tile_window: None,
|
||||
rom,
|
||||
output,
|
||||
save_path: None,
|
||||
camera,
|
||||
no_save: false,
|
||||
bootrom: None,
|
||||
show_bootrom: false,
|
||||
|
@ -249,3 +236,14 @@ where
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EmulatorCoreTrait {
|
||||
fn replace_output(&mut self, new: AudioOutput);
|
||||
fn cycle_count(&self) -> usize;
|
||||
fn pc(&self) -> u16;
|
||||
fn print_reg(&self) -> String;
|
||||
fn get_memory(&self, address: u16) -> u8;
|
||||
fn run(&mut self);
|
||||
fn run_stepped(&mut self, step_size: usize);
|
||||
fn run_until_buffer_full(&mut self);
|
||||
}
|
||||
|
|
123
lib/src/lib.rs
123
lib/src/lib.rs
|
@ -5,8 +5,8 @@ use crate::{
|
|||
util::pause,
|
||||
};
|
||||
use connect::{
|
||||
AudioOutput, CameraWrapper, CameraWrapperRef, EmulatorMessage, EmulatorOptions, NoCamera,
|
||||
PocketCamera, Renderer, RomFile, SerialTarget,
|
||||
AudioOutput, CameraWrapper, CameraWrapperRef, EmulatorCoreTrait, EmulatorMessage,
|
||||
EmulatorOptions, NoCamera, PocketCamera, Renderer, RomFile, SerialTarget,
|
||||
};
|
||||
use processor::{
|
||||
memory::{mmio::gpu::Colour, rom::CgbRomType, OutputTargets, Rom},
|
||||
|
@ -49,9 +49,10 @@ where
|
|||
{
|
||||
pub fn init(
|
||||
receiver: Receiver<EmulatorMessage>,
|
||||
mut options: EmulatorOptions<ColourFormat, R, C>,
|
||||
mut options: EmulatorOptions<ColourFormat, R>,
|
||||
camera: C,
|
||||
) -> Self {
|
||||
let camera: CameraWrapperRef<C> = Arc::new(Mutex::new(CameraWrapper::new(options.camera)));
|
||||
let camera: CameraWrapperRef<C> = Arc::new(Mutex::new(CameraWrapper::new(camera)));
|
||||
|
||||
let rom = match options.rom {
|
||||
RomFile::Path(path) => {
|
||||
|
@ -136,33 +137,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn replace_output(&mut self, new: AudioOutput) {
|
||||
self.cpu.memory.replace_output(new);
|
||||
}
|
||||
|
||||
pub fn cycle_count(&self) -> usize {
|
||||
self.cpu.cycle_count
|
||||
}
|
||||
|
||||
pub fn pc(&self) -> u16 {
|
||||
self.cpu.reg.pc
|
||||
}
|
||||
|
||||
pub fn print_reg(&self) -> String {
|
||||
format!(
|
||||
"A:{:0>2X}, F:{}, BC:{:0>4X}, DE:{:0>4X}, HL:{:0>4X}, SP:{:0>4X}, PC:{:0>4X}\nLast instruction: {:0>2X} from 0x{:0>4X}",
|
||||
self.cpu.reg.get_8(processor::Reg8::A),
|
||||
self.print_flags(),
|
||||
self.cpu.reg.bc,
|
||||
self.cpu.reg.de,
|
||||
self.cpu.reg.hl,
|
||||
self.cpu.reg.sp,
|
||||
self.cpu.reg.pc,
|
||||
self.cpu.last_instruction,
|
||||
self.cpu.last_instruction_addr
|
||||
)
|
||||
}
|
||||
|
||||
fn print_flags(&self) -> String {
|
||||
format!(
|
||||
"{}{}{}{}",
|
||||
|
@ -189,32 +163,6 @@ where
|
|||
)
|
||||
}
|
||||
|
||||
pub fn get_memory(&self, address: u16) -> u8 {
|
||||
self.cpu.memory.get(address)
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
self.process_messages();
|
||||
self.run_cycle();
|
||||
}
|
||||
|
||||
pub fn run_stepped(&mut self, step_size: usize) {
|
||||
loop {
|
||||
self.process_messages();
|
||||
for _ in 0..step_size {
|
||||
self.run_cycle();
|
||||
}
|
||||
stdout().flush().unwrap();
|
||||
pause();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_until_buffer_full(&mut self) {
|
||||
while !self.cpu.memory.is_audio_buffer_full() {
|
||||
self.run();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_cycle(&mut self) {
|
||||
self.cpu.exec_next();
|
||||
}
|
||||
|
@ -229,7 +177,6 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_save_state(&self) -> CpuSaveState<ColourFormat, R>
|
||||
where
|
||||
ColourFormat: From<Colour> + Clone,
|
||||
|
@ -239,6 +186,66 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<ColourFormat, R, C> EmulatorCoreTrait for EmulatorCore<ColourFormat, R, C>
|
||||
where
|
||||
ColourFormat: From<Colour> + Clone,
|
||||
R: Renderer<ColourFormat>,
|
||||
C: PocketCamera + Send + 'static,
|
||||
{
|
||||
fn replace_output(&mut self, new: AudioOutput) {
|
||||
self.cpu.memory.replace_output(new);
|
||||
}
|
||||
|
||||
fn cycle_count(&self) -> usize {
|
||||
self.cpu.cycle_count
|
||||
}
|
||||
|
||||
fn pc(&self) -> u16 {
|
||||
self.cpu.reg.pc
|
||||
}
|
||||
|
||||
fn print_reg(&self) -> String {
|
||||
format!(
|
||||
"A:{:0>2X}, F:{}, BC:{:0>4X}, DE:{:0>4X}, HL:{:0>4X}, SP:{:0>4X}, PC:{:0>4X}\nLast instruction: {:0>2X} from 0x{:0>4X}",
|
||||
self.cpu.reg.get_8(processor::Reg8::A),
|
||||
self.print_flags(),
|
||||
self.cpu.reg.bc,
|
||||
self.cpu.reg.de,
|
||||
self.cpu.reg.hl,
|
||||
self.cpu.reg.sp,
|
||||
self.cpu.reg.pc,
|
||||
self.cpu.last_instruction,
|
||||
self.cpu.last_instruction_addr
|
||||
)
|
||||
}
|
||||
|
||||
fn get_memory(&self, address: u16) -> u8 {
|
||||
self.cpu.memory.get(address)
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
self.process_messages();
|
||||
self.run_cycle();
|
||||
}
|
||||
|
||||
fn run_stepped(&mut self, step_size: usize) {
|
||||
loop {
|
||||
self.process_messages();
|
||||
for _ in 0..step_size {
|
||||
self.run_cycle();
|
||||
}
|
||||
stdout().flush().unwrap();
|
||||
pause();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_until_buffer_full(&mut self) {
|
||||
while !self.cpu.memory.is_audio_buffer_full() {
|
||||
self.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<ColourFormat, R> EmulatorCore<ColourFormat, R, NoCamera>
|
||||
where
|
||||
ColourFormat: From<Colour> + Clone,
|
||||
|
|
Loading…
Add table
Reference in a new issue