mirror of https://github.com/wayvr-org/wayvr.git
Merge remote-tracking branch 'upstream/main'
# Conflicts: # Cargo.lock # dash-frontend/src/tab/settings/mod.rs # wayvr/Cargo.toml # wayvr/src/backend/wayvr/mod.rs # wayvr/src/overlays/keyboard/mod.rs
This commit is contained in:
commit
26231500e3
|
|
@ -1 +1 @@
|
|||
open_collective: wlx-team
|
||||
open_collective: wayvr-org
|
||||
|
|
|
|||
|
|
@ -154,16 +154,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotate-snippets"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.14",
|
||||
"yansi-term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
|
|
@ -721,21 +711,18 @@ checksum = "f09c37bc0e9f0924b7dae9988265ef3c76c88538f41a3b06caf4bed07cee5226"
|
|||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.69.5"
|
||||
version = "0.72.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
|
||||
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"bitflags 2.10.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.12.1",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash 1.1.0",
|
||||
"rustc-hash 2.1.1",
|
||||
"shlex",
|
||||
"syn 2.0.113",
|
||||
]
|
||||
|
|
@ -801,16 +788,6 @@ dependencies = [
|
|||
"piper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.8.0"
|
||||
|
|
@ -1038,17 +1015,6 @@ dependencies = [
|
|||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz-build",
|
||||
"phf 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.10.4"
|
||||
|
|
@ -1056,18 +1022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"phf 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz-build"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1"
|
||||
dependencies = [
|
||||
"parse-zoneinfo",
|
||||
"phf 0.11.3",
|
||||
"phf_codegen",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1149,40 +1104,6 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codes-agency"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809a7790247f949cc35a1f9b497fd13a79ee330e34f049c6e837685363d20faf"
|
||||
dependencies = [
|
||||
"codes-common",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codes-common"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2b16b1037e7111ea4a4bd4a2b48733b990a76ecc321539858a5ab534792b287"
|
||||
dependencies = [
|
||||
"csv",
|
||||
"tera",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codes-iso-639"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30dbd0357410908cd59147abbb5d40a9b9a5d9261285c272844cf53dbaa6f7a8"
|
||||
dependencies = [
|
||||
"codes-agency",
|
||||
"codes-common",
|
||||
"csv",
|
||||
"serde",
|
||||
"tera",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.13.1"
|
||||
|
|
@ -1494,27 +1415,6 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938"
|
||||
dependencies = [
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cursor-icon"
|
||||
version = "1.2.0"
|
||||
|
|
@ -1727,12 +1627,6 @@ dependencies = [
|
|||
"syn 2.0.113",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deunicode"
|
||||
version = "1.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
|
@ -2456,30 +2350,6 @@ version = "0.3.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"log",
|
||||
"regex-automata 0.4.13",
|
||||
"regex-syntax 0.8.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globwalk"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"ignore",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grid"
|
||||
version = "1.0.0"
|
||||
|
|
@ -2664,15 +2534,6 @@ version = "1.10.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.3.0"
|
||||
|
|
@ -2911,22 +2772,6 @@ dependencies = [
|
|||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"globset",
|
||||
"log",
|
||||
"memchr",
|
||||
"regex-automata 0.4.13",
|
||||
"same-file",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.9"
|
||||
|
|
@ -3117,15 +2962,6 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
|
|
@ -3268,12 +3104,6 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
|
|
@ -3346,8 +3176,7 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
|||
[[package]]
|
||||
name = "libmonado"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0df3183760954d877b894d42f5c6954837d8a7d7bd5f042409c4175afeaf24ea"
|
||||
source = "git+https://github.com/oo8dev/libmonado-rs.git?rev=fc39940a64dea2df080a0d2c974c7d651006241f#fc39940a64dea2df080a0d2c974c7d651006241f"
|
||||
dependencies = [
|
||||
"dlopen2",
|
||||
"flagset",
|
||||
|
|
@ -3373,7 +3202,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "libspa"
|
||||
version = "0.8.0"
|
||||
source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=ba32202c3c391004c3bb533b58fa75a50e47ff57#ba32202c3c391004c3bb533b58fa75a50e47ff57"
|
||||
source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=6ef2cd1af41e5e8844c63c37ea9a01f0758079b0#6ef2cd1af41e5e8844c63c37ea9a01f0758079b0"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"cc",
|
||||
|
|
@ -3389,7 +3218,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "libspa-sys"
|
||||
version = "0.8.0"
|
||||
source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=ba32202c3c391004c3bb533b58fa75a50e47ff57#ba32202c3c391004c3bb533b58fa75a50e47ff57"
|
||||
source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=6ef2cd1af41e5e8844c63c37ea9a01f0758079b0#6ef2cd1af41e5e8844c63c37ea9a01f0758079b0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
|
|
@ -3643,6 +3472,12 @@ dependencies = [
|
|||
"pxfm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.14"
|
||||
|
|
@ -4467,15 +4302,6 @@ dependencies = [
|
|||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-zoneinfo"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
|
|
@ -4573,51 +4399,13 @@ dependencies = [
|
|||
"indexmap 2.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
|
||||
dependencies = [
|
||||
"phf_shared 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4681,7 +4469,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pipewire"
|
||||
version = "0.8.0"
|
||||
source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=ba32202c3c391004c3bb533b58fa75a50e47ff57#ba32202c3c391004c3bb533b58fa75a50e47ff57"
|
||||
source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=6ef2cd1af41e5e8844c63c37ea9a01f0758079b0#6ef2cd1af41e5e8844c63c37ea9a01f0758079b0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.10.0",
|
||||
|
|
@ -4697,7 +4485,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pipewire-sys"
|
||||
version = "0.8.0"
|
||||
source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=ba32202c3c391004c3bb533b58fa75a50e47ff57#ba32202c3c391004c3bb533b58fa75a50e47ff57"
|
||||
source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=6ef2cd1af41e5e8844c63c37ea9a01f0758079b0#6ef2cd1af41e5e8844c63c37ea9a01f0758079b0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"libspa-sys",
|
||||
|
|
@ -4879,6 +4667,64 @@ dependencies = [
|
|||
"syn 2.0.113",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-build"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"multimap",
|
||||
"petgraph",
|
||||
"prettyplease",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn 2.0.113",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.113",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost_build"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"prost-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pure-rust-locales"
|
||||
version = "0.8.2"
|
||||
|
|
@ -5878,16 +5724,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slug"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724"
|
||||
dependencies = [
|
||||
"deunicode",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
|
|
@ -6311,28 +6147,6 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tera"
|
||||
version = "1.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8004bca281f2d32df3bacd59bc67b312cb4c70cea46cbd79dbe8ac5ed206722"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz 0.9.0",
|
||||
"globwalk",
|
||||
"humansize",
|
||||
"lazy_static",
|
||||
"percent-encoding",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slug",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
|
|
@ -7363,9 +7177,8 @@ dependencies = [
|
|||
"ash",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"chrono-tz 0.10.4",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
"codes-iso-639",
|
||||
"config",
|
||||
"dash-frontend",
|
||||
"dbus",
|
||||
|
|
@ -7385,6 +7198,7 @@ dependencies = [
|
|||
"mint",
|
||||
"openxr",
|
||||
"ovr_overlay",
|
||||
"prost",
|
||||
"pure-rust-locales",
|
||||
"regex",
|
||||
"rosc",
|
||||
|
|
@ -7397,6 +7211,7 @@ dependencies = [
|
|||
"slotmap",
|
||||
"smallvec",
|
||||
"smithay",
|
||||
"smol",
|
||||
"strum",
|
||||
"super-swipe-type",
|
||||
"sysinfo",
|
||||
|
|
@ -8320,15 +8135,6 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "yansi-term"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yazi"
|
||||
version = "0.2.1"
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ members = [
|
|||
"dash-frontend",
|
||||
"wayvr-ipc",
|
||||
"wayvrctl",
|
||||
"scripts/prost_build",
|
||||
]
|
||||
resolver = "3"
|
||||
|
||||
|
|
@ -38,6 +39,7 @@ vulkano = { git = "https://github.com/galister/vulkano.git", rev = "cf7f92867928
|
|||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.100"
|
||||
smol = "2.0.2"
|
||||
glam = { version = "0.30.9", features = ["mint", "serde"] }
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
xdg = "3.0.0"
|
||||
|
|
|
|||
35
README.md
35
README.md
|
|
@ -1,4 +1,4 @@
|
|||

|
||||

|
||||
|
||||
# WayVR (previously WlxOverlay-S)
|
||||
|
||||
|
|
@ -8,13 +8,13 @@ WayVR lets you access your desktop screens while in VR, and even launch apps dir
|
|||
|
||||
In comparison to similar overlays, WayVR aims to run alongside VR games and experiences while having as little performance impact as possible. The UI appearance and rendering techniques are kept as simple and efficient as possible, while still allowing a high degree of customizability.
|
||||
|
||||

|
||||

|
||||
|
||||
## Join the Linux VR Community
|
||||
|
||||
We are available on either **Discord** or **Matrix space**:
|
||||
|
||||
[](https://discord.gg/EHAYe3tTYa) [](https://matrix.to/#/#linux-vr-adventures:matrix.org)
|
||||
[](https://discord.gg/EHAYe3tTYa) [](https://matrix.to/#/#linux-vr-adventures:matrix.org)
|
||||
|
||||
Questions/issues specific to WayVR will be handled in the `wayvr` chat room. Feel free to ask anything.
|
||||
|
||||
|
|
@ -24,10 +24,10 @@ Questions/issues specific to WayVR will be handled in the `wayvr` chat room. Fee
|
|||
|
||||
There are multiple ways to install WayVR:
|
||||
|
||||
1. AppImage: Download from [Releases](https://github.com/wlx-team/wayvr/releases)
|
||||
1. AUR package: [wayvr](https://aur.archlinux.org/packages/wayvr) or [wayvr-git](https://aur.archlinux.org/packages/wayvr-git)
|
||||
1. AppImage: Download from [Releases](https://github.com/wayvr-org/wayvr/releases)
|
||||
1. AUR package: [wayvr](https://aur.archlinux.org/packages/wayvr) or [wayvr-git](https://aur.archlinux.org/packages/wayvr-git)
|
||||
1. Nix package: [wayvr](https://search.nixos.org/packages?channel=unstable&show=wayvr&query=wayvr) or [unstable package from nixpkgs-xr](https://github.com/nix-community/nixpkgs-xr)
|
||||
1. [Building from source](https://github.com/wlx-team/wayvr/wiki/Building-from-Source).
|
||||
1. [Docs: Building from source](https://wayvr.org/docs/basics/building-from-source/).
|
||||
|
||||
### General Setup
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ There are multiple ways to install WayVR:
|
|||
|
||||
For users specifically running **SteamVR via Steam Flatpak**, follow these steps:
|
||||
|
||||
1. Grab the latest AppImage from [Releases](https://github.com/wlx-team/wayvr/releases).
|
||||
1. Grab the latest AppImage from [Releases](https://github.com/wayvr-org/wayvr/releases).
|
||||
1. `WayVR-*.AppImage --appimage-extract`
|
||||
1. `chmod +x squashfs-root/AppRun`
|
||||
1. Move the newly created `squashfs-root` folder to a location accessible by the Steam Flatpak.
|
||||
|
|
@ -64,7 +64,7 @@ In case screens were selected in the wrong order:
|
|||
**Envision users**: Go to the Plugins menu and select the WayVR plugin. This will download and run the AppImage version of the overlay.
|
||||
To run a standalone installation (for instance, from the AUR), create a bash script containing `wayvr --openxr --show` and then set this bash script as a custom Envision plugin.
|
||||
|
||||
This will show a home environment with headset passthrough enabled by default or a [customizable background](https://github.com/wlx-team/wayvr/wiki/OpenXR-Skybox)!
|
||||
This will show a home environment with headset passthrough enabled by default or a [customizable background](https://wayvr.org/docs/various/openxr-skybox/)!
|
||||
|
||||
**SteamVR users**: WayVR will register itself for auto-start, so there is no need to start it every time. Disclaimer: SteamVR will sometimes disregard this and not start WayVR anyway.
|
||||
|
||||
|
|
@ -131,23 +131,23 @@ Typing
|
|||
- While using the ORANGE laser, all keystrokes will have SHIFT applied.
|
||||
- Purple laser is customizable via the settings, no modifier by default.
|
||||
|
||||
**Modifier Keys are sticky**. They will remain pressed until eiter:
|
||||
**Modifier Keys are sticky**. They will remain pressed until either:
|
||||
|
||||
- a non-modifier key is pressed
|
||||
- the modifier is toggled off by clicking again
|
||||
- the keyboard is hidden (including via show-hide)
|
||||
|
||||
### Default Bindings
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
### Changing Bindings
|
||||
|
||||
SteamVR: Simply change the bindings from the SteamVR bindings section. If WayVR doesn't show up on the list, select any other title and then press back on the top left. (SteamVR is weird like that sometimes)
|
||||
|
||||
OpenXR (Monado/WiVRn): See [Wiki: OpenXR Bindings](https://github.com/wlx-team/wayvr/wiki/OpenXR-Bindings)
|
||||
OpenXR (Monado/WiVRn): See [Docs: OpenXR Bindings](https://wayvr.org/docs/various/openxr-bindings/)
|
||||
|
||||
If your controllers are not supported, please reach out. \
|
||||
We would like to work with you and include additional bindings.
|
||||
|
|
@ -155,9 +155,10 @@ We would like to work with you and include additional bindings.
|
|||
## Customization
|
||||
|
||||
See these relevant wiki pages:
|
||||
- For all available config options, check [Wiki: Configuration](https://github.com/wlx-team/wayvr/wiki/Configuration)
|
||||
- Looking to customize look & feel, or add functionality? See [Wiki: Customization](https://github.com/wlx-team/wayvr/wiki/Customization)
|
||||
- Looking to change the OpenXR background? See [Wiki: OpenXR Skybox](https://github.com/wlx-team/wayvr/wiki/OpenXR-Skybox)
|
||||
|
||||
- For all available config options, check [Docs: Configuration](https://wayvr.org/docs/basics/configuration/)
|
||||
- Looking to customize look & feel, or add functionality? See [Docs: Customization](https://wayvr.org/docs/basics/customization/)
|
||||
- Looking to change the OpenXR background? See [Docs: OpenXR Skybox](https://wayvr.org/docs/various/openxr-skybox/)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
@ -165,7 +166,7 @@ When an error is detected, we often print tips for fixing it into the log file.
|
|||
|
||||
Logs will be at `/tmp/wayvr.log` for most distros.
|
||||
|
||||
Check [here](https://github.com/wlx-team/wayvr/wiki/Troubleshooting) for tips.
|
||||
Check [here](https://wayvr.org/docs/various/troubleshooting/) for tips.
|
||||
|
||||
## Known Issues
|
||||
|
||||
|
|
|
|||
|
|
@ -23,12 +23,12 @@ strum.workspace = true
|
|||
|
||||
chrono = "0.4.42"
|
||||
keyvalues-parser = { git = "https://github.com/CosmicHorrorDev/vdf-rs.git", rev = "fc6dcbea9eb13cacb98dea40063f6f56cde6e145" }
|
||||
smol = "2.0.2"
|
||||
smol = { workspace = true }
|
||||
hyper = { version = "1.8.1", features = ["client", "http1", "http2"] }
|
||||
http-body-util = "0.1.3"
|
||||
async-native-tls = "0.5.0"
|
||||
smol-hyper = "0.1.1"
|
||||
|
||||
[features]
|
||||
default = ["monado" ]
|
||||
default = ["monado"]
|
||||
monado = []
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE -->
|
||||
<path fill="currentColor" d="M12 19q1.65 0 2.825-1.175T16 15v-4q0-1.65-1.175-2.825T12 7T9.175 8.175T8 11v4q0 1.65 1.175 2.825T12 19m-2-3h4v-2h-4zm0-4h4v-2h-4zm2 9q-1.625 0-3.012-.8T6.8 18H4v-2h2.1q-.075-.5-.088-1T6 14H4v-2h2q0-.5.012-1t.088-1H4V8h2.8q.35-.575.788-1.075T8.6 6.05L7 4.4L8.4 3l2.15 2.15q.7-.225 1.425-.225t1.425.225L15.6 3L17 4.4l-1.65 1.65q.575.375 1.038.862T17.2 8H20v2h-2.1q.075.5.088 1T18 12h2v2h-2q0 .5-.013 1t-.087 1H20v2h-2.8q-.8 1.4-2.187 2.2T12 21" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 682 B |
|
|
@ -154,4 +154,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</elements>
|
||||
</layout>
|
||||
</layout>
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
gap="8"
|
||||
round="8" />
|
||||
|
||||
<!-- src, text, translation -->
|
||||
<!-- src_builtin, text, translation -->
|
||||
<template name="GroupBoxTitle">
|
||||
<div flex_direction="row" align_items="center" gap="8">
|
||||
<sprite src="${src}" src_builtin="${src_builtin}" width="24" height="24" />
|
||||
<sprite src_builtin="${src_builtin}" width="24" height="24" />
|
||||
<label text="${text}" translation="${translation}" weight="bold" size="18" />
|
||||
</div>
|
||||
<rectangle color="#FFFFFF44" width="100%" height="2" />
|
||||
|
|
|
|||
|
|
@ -1,37 +1,14 @@
|
|||
<layout>
|
||||
<include src="../t_group_box.xml" />
|
||||
|
||||
<!-- key: str, value: str -->
|
||||
<template name="BoolFlag">
|
||||
<div flex_direction="row" gap="4">
|
||||
<label text="${key}" />
|
||||
<label weight="bold" text="${value}" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- name, checked, flag_* -->
|
||||
<template name="Cell">
|
||||
<rectangle macro="group_box">
|
||||
<CheckBox id="checkbox" text="${name}" checked="${checked}" />
|
||||
<div flex_direction="row" gap="8">
|
||||
<BoolFlag key="Active:" value="${flag_active}" />
|
||||
<BoolFlag key="Focused:" value="${flag_focused}" />
|
||||
<BoolFlag key="IO active:" value="${flag_io_active}" />
|
||||
<BoolFlag key="Overlay:" value="${flag_overlay}" />
|
||||
<BoolFlag key="Primary:" value="${flag_primary}" />
|
||||
<BoolFlag key="Visible:" value="${flag_visible}" />
|
||||
</div>
|
||||
</rectangle>
|
||||
</template>
|
||||
|
||||
<elements>
|
||||
<label translation="DISPLAY_BRIGHTNESS" />
|
||||
<Slider id="slider_brightness" width="300" height="24" min_value="0" max_value="140" />
|
||||
|
||||
<label translation="PROCESS_LIST" />
|
||||
<div id="list_parent" flex_direction="column" gap="8">
|
||||
<!-- filled at runtime -->
|
||||
<div gap="4">
|
||||
<Tabs id="tabs">
|
||||
<Tab name="general_settings" translation="GENERAL_SETTINGS" sprite_src_builtin="dashboard/settings.svg" />
|
||||
<Tab name="process_list" translation="PROCESS_LIST" sprite_src_builtin="dashboard/cpu.svg" />
|
||||
<Tab name="debug_timings" translation="DEBUG_INFO" sprite_src_builtin="dashboard/not_a_bug.svg" />
|
||||
</Tabs>
|
||||
<div gap="4" id="content" width="100%">
|
||||
<!-- filled-in at runtime -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</elements>
|
||||
</layout>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<layout>
|
||||
<include src="../t_group_box.xml" />
|
||||
|
||||
<!-- name, limit_min, limit_max -->
|
||||
<template name="DebugGraph">
|
||||
<rectangle flex_direction="column" align_items="center" gap="8" border="2" color="#ffffff11" color2="#ffffff22" gradient="vertical" border_color="#ffffff33" round="4" padding="4">
|
||||
<label text="${name}" size="9" weight="bold" />
|
||||
<BarGraph id="graph" width="180" height="70" unit="ms" limit_min="${limit_min}" limit_max="${limit_max}" capacity="50" />
|
||||
</rectangle>
|
||||
</template>
|
||||
|
||||
<!-- text -->
|
||||
<template name="SessionButton">
|
||||
<Button id="button" text="${text}" />
|
||||
</template>
|
||||
|
||||
<elements>
|
||||
<rectangle macro="group_box">
|
||||
<GroupBoxTitle src_builtin="dashboard/apps.svg" text="Sessions" />
|
||||
<div id="session_list_parent" flex_wrap="wrap" flex_direction="row" gap="8" />
|
||||
<GroupBoxTitle src_builtin="dashboard/not_a_bug.svg" text="Debug timings" />
|
||||
<div id="timings_parent" flex_wrap="wrap" flex_direction="row" gap="8">
|
||||
<label text="Please select which session you want to monitor" />
|
||||
</div>
|
||||
</rectangle>
|
||||
</elements>
|
||||
</layout>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<layout>
|
||||
<include src="../t_group_box.xml" />
|
||||
|
||||
<elements>
|
||||
<rectangle macro="group_box">
|
||||
<GroupBoxTitle src_builtin="dashboard/settings.svg" translation="GENERAL_SETTINGS" />
|
||||
|
||||
<label translation="DISPLAY_BRIGHTNESS" />
|
||||
<Slider id="slider_brightness" width="300" height="24" min_value="0" max_value="140" />
|
||||
</rectangle>
|
||||
</elements>
|
||||
</layout>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<layout>
|
||||
<include src="../t_group_box.xml" />
|
||||
|
||||
<!-- key: str, value: str -->
|
||||
<template name="BoolFlag">
|
||||
<div flex_direction="row" gap="4">
|
||||
<label text="${key}" />
|
||||
<label weight="bold" text="${value}" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- name, checked, flag_* -->
|
||||
<template name="Cell">
|
||||
<rectangle macro="group_box">
|
||||
<CheckBox id="checkbox" text="${name}" checked="${checked}" />
|
||||
<div flex_direction="row" gap="8" flex_wrap="wrap">
|
||||
<BoolFlag key="Active:" value="${flag_active}" />
|
||||
<BoolFlag key="Focused:" value="${flag_focused}" />
|
||||
<BoolFlag key="IO active:" value="${flag_io_active}" />
|
||||
<BoolFlag key="Overlay:" value="${flag_overlay}" />
|
||||
<BoolFlag key="Primary:" value="${flag_primary}" />
|
||||
<BoolFlag key="Visible:" value="${flag_visible}" />
|
||||
</div>
|
||||
</rectangle>
|
||||
</template>
|
||||
|
||||
<elements>
|
||||
<rectangle macro="group_box">
|
||||
<GroupBoxTitle src_builtin="dashboard/cpu.svg" translation="PROCESS_LIST" />
|
||||
<div id="list_parent" flex_direction="column" gap="8">
|
||||
<!-- filled at runtime -->
|
||||
</div>
|
||||
</rectangle>
|
||||
</elements>
|
||||
</layout>
|
||||
|
|
@ -54,7 +54,9 @@
|
|||
<Tab name="autostart_apps" translation="APP_SETTINGS.AUTOSTART_APPS" sprite_src_builtin="dashboard/apps.svg" />
|
||||
<Tab name="troubleshooting" translation="APP_SETTINGS.TROUBLESHOOTING" sprite_src_builtin="dashboard/cpu.svg" />
|
||||
</Tabs>
|
||||
<div flex_wrap="wrap" justify_content="stretch" gap="4" id="settings_root" width="100%" />
|
||||
<div flex_wrap="wrap" justify_content="stretch" gap="4" id="settings_root" width="100%">
|
||||
<!-- filled-in at runtime -->
|
||||
</div>
|
||||
</div>
|
||||
</elements>
|
||||
</layout>
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
<layout>
|
||||
<include src="../t_group_box.xml" />
|
||||
<include src_builtin="../t_group_box.xml" />
|
||||
|
||||
<!-- device_name, device_icon -->
|
||||
<template name="DeviceSlider">
|
||||
<rectangle macro="group_box">
|
||||
<div width="100%" align_items="center" justify_content="center" gap="8">
|
||||
<sprite src="${device_icon}" width="16" height="16" />
|
||||
<sprite src_builtin="${device_icon}" width="16" height="16" />
|
||||
<label text="${device_name}" margin_right="8" size="12" weight="bold" />
|
||||
</div>
|
||||
<div width="100%" align_items="center">
|
||||
<CheckBox id="checkbox" />
|
||||
<Button sprite_src="${volume_icon}" id="btn_mute" width="32" />
|
||||
<Button sprite_src_builtin="${volume_icon}" id="btn_mute" width="32" />
|
||||
<Slider id="slider" flex_grow="1" height="16" min_value="0" max_value="150" margin_left="8" />
|
||||
</div>
|
||||
</rectangle>
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
flex_grow="1"
|
||||
id="${id}"
|
||||
translation="${translation}"
|
||||
sprite_src="${src}">
|
||||
sprite_src_builtin="${src}">
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
|
|
@ -67,4 +67,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</elements>
|
||||
</layout>
|
||||
</layout>
|
||||
|
|
|
|||
|
|
@ -161,5 +161,6 @@
|
|||
"PROCESS": {
|
||||
"STOP": "Stopp",
|
||||
"FORCE_KILL": "Erzwinge Beenden"
|
||||
}
|
||||
},
|
||||
"DEBUG_INFO": "Debug-Informationen"
|
||||
}
|
||||
|
|
@ -135,6 +135,7 @@
|
|||
"VOLUME": "Volume"
|
||||
},
|
||||
"CLOSE_WINDOW": "Close window",
|
||||
"DEBUG_INFO": "Debug info",
|
||||
"DISPLAY_BRIGHTNESS": "Display brightness",
|
||||
"FAILED_TO_LAUNCH_APPLICATION": "Failed to launch a application:",
|
||||
"GAME_LAUNCHED": "Game launched",
|
||||
|
|
|
|||
|
|
@ -161,5 +161,6 @@
|
|||
"PROCESS": {
|
||||
"STOP": "Detener",
|
||||
"FORCE_KILL": "Forzar cierre"
|
||||
}
|
||||
},
|
||||
"DEBUG_INFO": "Información de depuración"
|
||||
}
|
||||
|
|
@ -161,5 +161,6 @@
|
|||
"PROCESS": {
|
||||
"STOP": "Interrompi",
|
||||
"FORCE_KILL": "Uccidi forzatamente"
|
||||
}
|
||||
},
|
||||
"DEBUG_INFO": "Informazioni di debug"
|
||||
}
|
||||
|
|
@ -161,5 +161,6 @@
|
|||
"PROCESS": {
|
||||
"STOP": "停止",
|
||||
"FORCE_KILL": "強制終了"
|
||||
}
|
||||
},
|
||||
"DEBUG_INFO": "デバッグ情報"
|
||||
}
|
||||
|
|
@ -161,5 +161,6 @@
|
|||
"PROCESS": {
|
||||
"STOP": "Zatrzymaj",
|
||||
"FORCE_KILL": "Wymuś zakończenie"
|
||||
}
|
||||
},
|
||||
"DEBUG_INFO": "Informacje debugowania"
|
||||
}
|
||||
|
|
@ -161,5 +161,6 @@
|
|||
"PROCESS": {
|
||||
"STOP": "停止",
|
||||
"FORCE_KILL": "强制关闭"
|
||||
}
|
||||
},
|
||||
"DEBUG_INFO": "调试信息"
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ use wgui::{
|
|||
windowing::window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra, WguiWindowPlacement},
|
||||
};
|
||||
use wlx_common::{
|
||||
async_executor::AsyncExecutor,
|
||||
audio,
|
||||
dash_interface::{BoxDashInterface, RecenterMode},
|
||||
locale::WayVRLangProvider,
|
||||
|
|
@ -29,7 +30,6 @@ use crate::{
|
|||
util::{
|
||||
popup_manager::{MountPopupParams, PopupManager, PopupManagerParams},
|
||||
toast_manager::ToastManager,
|
||||
various::AsyncExecutor,
|
||||
},
|
||||
views,
|
||||
};
|
||||
|
|
@ -45,7 +45,7 @@ pub type FrontendTasks = Tasks<FrontendTask>;
|
|||
|
||||
pub struct Frontend<T> {
|
||||
pub layout: Layout,
|
||||
globals: WguiGlobals,
|
||||
pub globals: WguiGlobals,
|
||||
|
||||
pub interface: BoxDashInterface<T>,
|
||||
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ impl AppList {
|
|||
let mut params = HashMap::<Rc<str>, Rc<str>>::new();
|
||||
params.insert("text".into(), category_name.into());
|
||||
|
||||
parser_state.parse_template(
|
||||
parser_state.realize_template(
|
||||
doc_params,
|
||||
"CategoryText",
|
||||
&mut frontend.layout,
|
||||
|
|
@ -318,7 +318,7 @@ impl AppList {
|
|||
);
|
||||
params.insert("name".into(), entry.app_name.clone());
|
||||
|
||||
let data = parser_state.parse_template(
|
||||
let data = parser_state.realize_template(
|
||||
doc_params,
|
||||
"AppEntry",
|
||||
&mut frontend.layout,
|
||||
|
|
|
|||
|
|
@ -2,24 +2,119 @@ use std::{collections::HashMap, marker::PhantomData, rc::Rc};
|
|||
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::{checkbox::ComponentCheckbox, slider::ComponentSlider},
|
||||
components::{
|
||||
bar_graph::{ComponentBarGraph, ValueCell},
|
||||
button::ComponentButton,
|
||||
checkbox::ComponentCheckbox,
|
||||
slider::ComponentSlider,
|
||||
tabs::ComponentTabs,
|
||||
},
|
||||
drawing::Color,
|
||||
globals::WguiGlobals,
|
||||
layout::WidgetID,
|
||||
layout::{Layout, WidgetID},
|
||||
parser::{self, Fetchable, ParseDocumentParams, ParserState},
|
||||
task::Tasks,
|
||||
};
|
||||
use wlx_common::dash_interface;
|
||||
use wlx_common::dash_interface::{self, MonadoDumpSessionFrame};
|
||||
|
||||
use crate::{
|
||||
frontend::Frontend,
|
||||
tab::{Tab, TabType},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone)]
|
||||
enum TabNameEnum {
|
||||
GeneralSettings,
|
||||
ProcessList,
|
||||
DebugTimings,
|
||||
}
|
||||
|
||||
impl TabNameEnum {
|
||||
fn from_string(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"general_settings" => Some(TabNameEnum::GeneralSettings),
|
||||
"process_list" => Some(TabNameEnum::ProcessList),
|
||||
"debug_timings" => Some(TabNameEnum::DebugTimings),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Task {
|
||||
Refresh,
|
||||
FocusClient(String),
|
||||
SetBrightness(f32),
|
||||
SetTab(TabNameEnum),
|
||||
|
||||
// `ProcessList` tab
|
||||
ProcessListRefresh,
|
||||
ProcessListFocusClient(String),
|
||||
|
||||
// `DebugTimings` tab
|
||||
DebugTimingsRefreshSessionList,
|
||||
DebugTimingsSetSessionId(i64),
|
||||
}
|
||||
|
||||
struct SubtabProcessList {
|
||||
id_list_parent: WidgetID,
|
||||
state: ParserState,
|
||||
cells: Vec<parser::ParserData>,
|
||||
}
|
||||
|
||||
struct SubtabGeneralSettings {
|
||||
#[allow(dead_code)]
|
||||
state: ParserState,
|
||||
}
|
||||
|
||||
struct DebugGraph {
|
||||
graph: Rc<ComponentBarGraph>,
|
||||
}
|
||||
|
||||
struct DebugSessionList {
|
||||
#[allow(dead_code)]
|
||||
buttons: Vec<Rc<ComponentButton>>,
|
||||
}
|
||||
|
||||
struct TimingsSession {
|
||||
resolved_name: Option<String>,
|
||||
last_frame: MonadoDumpSessionFrame,
|
||||
}
|
||||
|
||||
struct Graphs {
|
||||
predicted_display_time: DebugGraph,
|
||||
predicted_frame_time: DebugGraph,
|
||||
predicted_wake_up_time: DebugGraph,
|
||||
predicted_gpu_done_time: DebugGraph,
|
||||
predicted_display_period: DebugGraph,
|
||||
display_time: DebugGraph,
|
||||
when_predicted: DebugGraph,
|
||||
when_wait_woke: DebugGraph,
|
||||
when_begin: DebugGraph,
|
||||
when_delivered: DebugGraph,
|
||||
when_gpu_done: DebugGraph,
|
||||
}
|
||||
|
||||
type SessionsMap = HashMap<i64 /* session id */, TimingsSession>;
|
||||
|
||||
struct SubtabDebugTimings {
|
||||
#[allow(dead_code)]
|
||||
state: ParserState,
|
||||
|
||||
graphs: Option<Graphs>,
|
||||
session_list: DebugSessionList,
|
||||
selected_session_id: Option<i64>,
|
||||
|
||||
id_sessions_list_parent: WidgetID,
|
||||
id_timings_parent: WidgetID,
|
||||
|
||||
sessions: SessionsMap,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum Subtab {
|
||||
Empty,
|
||||
GeneralSettings(SubtabGeneralSettings),
|
||||
ProcessList(SubtabProcessList),
|
||||
DebugTimings(SubtabDebugTimings),
|
||||
}
|
||||
|
||||
pub struct TabMonado<T> {
|
||||
|
|
@ -29,10 +124,8 @@ pub struct TabMonado<T> {
|
|||
|
||||
marker: PhantomData<T>,
|
||||
|
||||
globals: WguiGlobals,
|
||||
id_list_parent: WidgetID,
|
||||
|
||||
cells: Vec<parser::ParserData>,
|
||||
id_content: WidgetID,
|
||||
subtab: Subtab,
|
||||
|
||||
ticks: u32,
|
||||
}
|
||||
|
|
@ -45,15 +138,64 @@ impl<T> Tab<T> for TabMonado<T> {
|
|||
fn update(&mut self, frontend: &mut Frontend<T>, _time_ms: u32, data: &mut T) -> anyhow::Result<()> {
|
||||
for task in self.tasks.drain() {
|
||||
match task {
|
||||
Task::Refresh => self.refresh(frontend, data)?,
|
||||
Task::FocusClient(name) => self.focus_client(frontend, data, name)?,
|
||||
Task::ProcessListRefresh => {
|
||||
if let Subtab::ProcessList(process_list) = &mut self.subtab {
|
||||
process_list.refresh(frontend, data, &self.tasks)?;
|
||||
}
|
||||
}
|
||||
Task::ProcessListFocusClient(client_name) => {
|
||||
if let Subtab::ProcessList(process_list) = &mut self.subtab {
|
||||
process_list.focus_client(frontend, data, client_name, &self.tasks)?;
|
||||
}
|
||||
}
|
||||
Task::DebugTimingsRefreshSessionList => {
|
||||
if let Subtab::DebugTimings(tab) = &mut self.subtab {
|
||||
tab.refresh_session_list(&mut frontend.layout, &self.tasks)?;
|
||||
}
|
||||
}
|
||||
Task::DebugTimingsSetSessionId(session_id) => {
|
||||
if let Subtab::DebugTimings(tab) = &mut self.subtab {
|
||||
tab.set_session_id(&mut frontend.layout, session_id)?;
|
||||
}
|
||||
}
|
||||
Task::SetBrightness(brightness) => self.set_brightness(frontend, data, brightness),
|
||||
Task::SetTab(tab) => {
|
||||
frontend.layout.remove_children(self.id_content);
|
||||
match tab {
|
||||
TabNameEnum::GeneralSettings => {
|
||||
self.subtab = Subtab::GeneralSettings(SubtabGeneralSettings::new(
|
||||
self.id_content,
|
||||
frontend,
|
||||
data,
|
||||
&self.tasks,
|
||||
)?)
|
||||
}
|
||||
TabNameEnum::ProcessList => {
|
||||
self.tasks.push(Task::ProcessListRefresh);
|
||||
self.subtab = Subtab::ProcessList(SubtabProcessList::new(self.id_content, frontend)?)
|
||||
}
|
||||
TabNameEnum::DebugTimings => {
|
||||
self.subtab = Subtab::DebugTimings(SubtabDebugTimings::new(self.id_content, frontend, &self.tasks)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// every few seconds
|
||||
if self.ticks.is_multiple_of(500) {
|
||||
self.tasks.push(Task::Refresh);
|
||||
match &mut self.subtab {
|
||||
Subtab::Empty => {}
|
||||
Subtab::GeneralSettings(_) => {}
|
||||
Subtab::ProcessList(_) => {
|
||||
// every few seconds
|
||||
if let Subtab::ProcessList(_) = &self.subtab
|
||||
&& self.ticks.is_multiple_of(500)
|
||||
{
|
||||
self.tasks.push(Task::ProcessListRefresh);
|
||||
}
|
||||
}
|
||||
Subtab::DebugTimings(timings) => {
|
||||
timings.update(&self.tasks, data, frontend)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.ticks += 1;
|
||||
|
|
@ -62,7 +204,7 @@ impl<T> Tab<T> for TabMonado<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn doc_params(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> {
|
||||
fn doc_params_monado(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> {
|
||||
ParseDocumentParams {
|
||||
globals: globals.clone(),
|
||||
path: AssetPath::BuiltIn("gui/tab/monado.xml"),
|
||||
|
|
@ -70,6 +212,30 @@ fn doc_params(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn doc_params_tab_process_list(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> {
|
||||
ParseDocumentParams {
|
||||
globals: globals.clone(),
|
||||
path: AssetPath::BuiltIn("gui/tab/monado_tab_process_list.xml"),
|
||||
extra: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_params_tab_general_settings(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> {
|
||||
ParseDocumentParams {
|
||||
globals: globals.clone(),
|
||||
path: AssetPath::BuiltIn("gui/tab/monado_tab_general_settings.xml"),
|
||||
extra: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_params_tab_debug_timings(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> {
|
||||
ParseDocumentParams {
|
||||
globals: globals.clone(),
|
||||
path: AssetPath::BuiltIn("gui/tab/monado_tab_debug_timings.xml"),
|
||||
extra: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn yesno(n: bool) -> &'static str {
|
||||
match n {
|
||||
true => "yes",
|
||||
|
|
@ -77,29 +243,320 @@ fn yesno(n: bool) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> TabMonado<T> {
|
||||
pub fn new(frontend: &mut Frontend<T>, parent_id: WidgetID) -> anyhow::Result<Self> {
|
||||
let globals = frontend.layout.state.globals.clone();
|
||||
let state = wgui::parser::parse_from_assets(&doc_params(&globals), &mut frontend.layout, parent_id)?;
|
||||
impl SubtabGeneralSettings {
|
||||
fn new<T>(
|
||||
parent_id: WidgetID,
|
||||
frontend: &mut Frontend<T>,
|
||||
data: &mut T,
|
||||
tasks: &Tasks<Task>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let state = wgui::parser::parse_from_assets(
|
||||
&doc_params_tab_general_settings(&frontend.globals),
|
||||
&mut frontend.layout,
|
||||
parent_id,
|
||||
)?;
|
||||
|
||||
let id_list_parent = state.get_widget_id("list_parent")?;
|
||||
// get brightness
|
||||
let slider_brightness = state.fetch_component_as::<ComponentSlider>("slider_brightness")?;
|
||||
if let Some(brightness) = frontend.interface.monado_brightness_get(data) {
|
||||
let mut c = frontend.layout.start_common();
|
||||
slider_brightness.set_value(&mut c.common(), brightness * 100.0);
|
||||
c.finish()?;
|
||||
|
||||
let tasks = Tasks::<Task>::new();
|
||||
slider_brightness.on_value_changed({
|
||||
let tasks = tasks.clone();
|
||||
Box::new(move |_common, e| {
|
||||
tasks.push(Task::SetBrightness(e.value / 100.0));
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
tasks.push(Task::Refresh);
|
||||
Ok(Self { state })
|
||||
}
|
||||
}
|
||||
|
||||
fn mount_sessions_list(
|
||||
state: &mut ParserState,
|
||||
layout: &mut Layout,
|
||||
tasks: &Tasks<Task>,
|
||||
id_parent: WidgetID,
|
||||
sessions: &SessionsMap,
|
||||
) -> anyhow::Result<DebugSessionList> {
|
||||
let mut buttons = Vec::new();
|
||||
let globals = layout.state.globals.clone();
|
||||
layout.remove_children(id_parent);
|
||||
|
||||
for (session_id, session) in sessions {
|
||||
let mut params = HashMap::new();
|
||||
|
||||
params.insert(
|
||||
Rc::from("text"),
|
||||
Rc::from(format!(
|
||||
"{} (ID {})",
|
||||
session.resolved_name.as_ref().map_or("Unknown", |s| s.as_str()),
|
||||
session_id,
|
||||
)),
|
||||
);
|
||||
|
||||
let data = state.realize_template(
|
||||
&doc_params_tab_debug_timings(&globals),
|
||||
"SessionButton",
|
||||
layout,
|
||||
id_parent,
|
||||
params,
|
||||
)?;
|
||||
|
||||
let button = data.fetch_component_as::<ComponentButton>("button")?;
|
||||
|
||||
button.on_click({
|
||||
let tasks = tasks.clone();
|
||||
let session_id = *session_id;
|
||||
Rc::new(move |_, _| {
|
||||
tasks.push(Task::DebugTimingsSetSessionId(session_id));
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
buttons.push(button);
|
||||
}
|
||||
|
||||
Ok(DebugSessionList { buttons })
|
||||
}
|
||||
|
||||
fn mount_graph(
|
||||
state: &mut ParserState,
|
||||
layout: &mut Layout,
|
||||
id_parent: WidgetID,
|
||||
name: &'static str,
|
||||
limits: (f32, f32),
|
||||
) -> anyhow::Result<DebugGraph> {
|
||||
let globals = layout.state.globals.clone();
|
||||
let mut params = HashMap::new();
|
||||
params.insert(Rc::from("name"), Rc::from(name));
|
||||
params.insert(Rc::from("limit_min"), Rc::from(limits.0.to_string()));
|
||||
params.insert(Rc::from("limit_max"), Rc::from(limits.1.to_string()));
|
||||
|
||||
let data = state.realize_template(
|
||||
&doc_params_tab_debug_timings(&globals),
|
||||
"DebugGraph",
|
||||
layout,
|
||||
id_parent,
|
||||
params,
|
||||
)?;
|
||||
|
||||
let graph = data.fetch_component_as::<ComponentBarGraph>("graph")?;
|
||||
Ok(DebugGraph { graph })
|
||||
}
|
||||
|
||||
fn ns_to_ms(ns: i64) -> f32 {
|
||||
(ns / 1000) as f32 / 1000.0
|
||||
}
|
||||
|
||||
impl SubtabDebugTimings {
|
||||
fn new<T>(parent_id: WidgetID, frontend: &mut Frontend<T>, tasks: &Tasks<Task>) -> anyhow::Result<Self> {
|
||||
let mut state = wgui::parser::parse_from_assets(
|
||||
&doc_params_tab_debug_timings(&frontend.globals),
|
||||
&mut frontend.layout,
|
||||
parent_id,
|
||||
)?;
|
||||
|
||||
let id_timings_parent = state.get_widget_id("timings_parent")?;
|
||||
let id_sessions_list_parent = state.get_widget_id("session_list_parent")?;
|
||||
|
||||
let sessions = Default::default();
|
||||
|
||||
let session_list = mount_sessions_list(
|
||||
&mut state,
|
||||
&mut frontend.layout,
|
||||
tasks,
|
||||
id_sessions_list_parent,
|
||||
&sessions,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
marker: PhantomData,
|
||||
graphs: None,
|
||||
session_list,
|
||||
id_sessions_list_parent,
|
||||
id_timings_parent,
|
||||
sessions,
|
||||
selected_session_id: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_session_id(&mut self, layout: &mut Layout, session_id: i64) -> anyhow::Result<()> {
|
||||
layout.remove_children(self.id_timings_parent);
|
||||
|
||||
let mut graph = |name: &'static str, limits: (f32, f32)| -> anyhow::Result<DebugGraph> {
|
||||
mount_graph(&mut self.state, layout, self.id_timings_parent, name, limits)
|
||||
};
|
||||
|
||||
// populate graphs
|
||||
self.graphs = Some(Graphs {
|
||||
predicted_display_time: graph("Predicted display time", (0.0, 30.0))?,
|
||||
predicted_frame_time: graph("Predicted frame time", (0.0, 30.0))?,
|
||||
predicted_wake_up_time: graph("Predicted wake-up time", (0.0, 30.0))?,
|
||||
predicted_gpu_done_time: graph("Predicted GPU done time", (0.0, 30.0))?,
|
||||
predicted_display_period: graph("Predicted display period", (0.0, 30.0))?,
|
||||
display_time: graph("Display time", (0.0, 30.0))?,
|
||||
when_predicted: graph("When predicted", (0.0, 30.0))?,
|
||||
when_wait_woke: graph("When wait woke", (0.0, 30.0))?,
|
||||
when_begin: graph("When begin", (0.0, 30.0))?,
|
||||
when_delivered: graph("When delivered", (0.0, 30.0))?,
|
||||
when_gpu_done: graph("When GPU done", (0.0, 30.0))?,
|
||||
});
|
||||
|
||||
self.selected_session_id = Some(session_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn refresh_session_list(&mut self, layout: &mut Layout, tasks: &Tasks<Task>) -> anyhow::Result<()> {
|
||||
self.session_list = mount_sessions_list(
|
||||
&mut self.state,
|
||||
layout,
|
||||
tasks,
|
||||
globals,
|
||||
self.id_sessions_list_parent,
|
||||
&self.sessions,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update<T>(&mut self, tasks: &Tasks<Task>, data: &mut T, frontend: &mut Frontend<T>) -> anyhow::Result<()> {
|
||||
if !frontend.interface.monado_metrics_set_enabled(data, true) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let frames = frontend.interface.monado_metrics_dump_session_frames(data);
|
||||
if frames.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let col_green = Color::new(0.0, 1.0, 0.0, 1.0);
|
||||
|
||||
for frame in frames {
|
||||
//log::info!("{:?}", frame);
|
||||
|
||||
match self.sessions.get_mut(&frame.session_id) {
|
||||
Some(session) => {
|
||||
if let Some(graphs) = &mut self.graphs
|
||||
&& let Some(selected_session_id) = self.selected_session_id
|
||||
&& selected_session_id == frame.session_id
|
||||
{
|
||||
let predicted_display_time = ns_to_ms(session.last_frame.predicted_display_time_ns as i64);
|
||||
let predicted_frame_time = ns_to_ms(frame.predicted_frame_time_ns as i64);
|
||||
let predicted_wake_up_time =
|
||||
ns_to_ms(frame.predicted_wake_up_time_ns as i64 - session.last_frame.predicted_wake_up_time_ns as i64);
|
||||
let predicted_gpu_done_time =
|
||||
ns_to_ms(frame.predicted_gpu_done_time_ns as i64 - session.last_frame.predicted_gpu_done_time_ns as i64);
|
||||
let predicted_display_period = ns_to_ms(session.last_frame.predicted_display_period_ns as i64); // 6.944 ms for 144Hz
|
||||
let display_time = ns_to_ms(frame.display_time_ns as i64 - session.last_frame.display_time_ns as i64);
|
||||
let when_predicted = ns_to_ms(frame.when_predicted_ns as i64 - session.last_frame.when_predicted_ns as i64);
|
||||
let when_wait_woke = ns_to_ms(frame.when_wait_woke_ns as i64 - session.last_frame.when_wait_woke_ns as i64);
|
||||
let when_begin = ns_to_ms(frame.when_begin_ns as i64 - session.last_frame.when_begin_ns as i64);
|
||||
let when_delivered = ns_to_ms(frame.when_delivered_ns as i64 - session.last_frame.when_delivered_ns as i64);
|
||||
let when_gpu_done = ns_to_ms(frame.when_gpu_done_ns as i64 - session.last_frame.when_gpu_done_ns as i64);
|
||||
|
||||
graphs.predicted_display_time.graph.push_value(ValueCell {
|
||||
value: predicted_display_time,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.predicted_frame_time.graph.push_value(ValueCell {
|
||||
value: predicted_frame_time,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.predicted_wake_up_time.graph.push_value(ValueCell {
|
||||
value: predicted_wake_up_time,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.predicted_gpu_done_time.graph.push_value(ValueCell {
|
||||
value: predicted_gpu_done_time,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.predicted_display_period.graph.push_value(ValueCell {
|
||||
value: predicted_display_period,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.display_time.graph.push_value(ValueCell {
|
||||
value: display_time,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.when_predicted.graph.push_value(ValueCell {
|
||||
value: when_predicted,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.when_wait_woke.graph.push_value(ValueCell {
|
||||
value: when_wait_woke,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.when_begin.graph.push_value(ValueCell {
|
||||
value: when_begin,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.when_delivered.graph.push_value(ValueCell {
|
||||
value: when_delivered,
|
||||
color: col_green,
|
||||
});
|
||||
|
||||
graphs.when_gpu_done.graph.push_value(ValueCell {
|
||||
value: when_gpu_done,
|
||||
color: col_green,
|
||||
});
|
||||
}
|
||||
|
||||
session.last_frame = frame;
|
||||
}
|
||||
None => {
|
||||
self.sessions.insert(
|
||||
frame.session_id,
|
||||
TimingsSession {
|
||||
last_frame: frame,
|
||||
resolved_name: None, /* TODO! find client ID from session ID */
|
||||
},
|
||||
);
|
||||
tasks.push(Task::DebugTimingsRefreshSessionList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frontend.layout.mark_redraw();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SubtabProcessList {
|
||||
fn new<T>(parent_id: WidgetID, frontend: &mut Frontend<T>) -> anyhow::Result<Self> {
|
||||
let state = wgui::parser::parse_from_assets(
|
||||
&doc_params_tab_process_list(&frontend.globals),
|
||||
&mut frontend.layout,
|
||||
parent_id,
|
||||
)?;
|
||||
let id_list_parent = state.get_widget_id("list_parent")?;
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
id_list_parent,
|
||||
ticks: 0,
|
||||
cells: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn mount_client(&mut self, frontend: &mut Frontend<T>, client: &dash_interface::MonadoClient) -> anyhow::Result<()> {
|
||||
fn mount_client(
|
||||
&mut self,
|
||||
layout: &mut Layout,
|
||||
client: &dash_interface::MonadoClient,
|
||||
tasks: &Tasks<Task>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut par = HashMap::<Rc<str>, Rc<str>>::new();
|
||||
par.insert(
|
||||
"checked".into(),
|
||||
|
|
@ -109,7 +566,10 @@ impl<T> TabMonado<T> {
|
|||
Rc::from("0")
|
||||
},
|
||||
);
|
||||
par.insert("name".into(), client.name.clone().into());
|
||||
par.insert(
|
||||
"name".into(),
|
||||
format!("{} (Client ID: {})", client.name, client.id).into(),
|
||||
);
|
||||
par.insert("flag_active".into(), yesno(client.is_active).into());
|
||||
par.insert("flag_focused".into(), yesno(client.is_focused).into());
|
||||
par.insert("flag_io_active".into(), yesno(client.is_io_active).into());
|
||||
|
|
@ -117,21 +577,23 @@ impl<T> TabMonado<T> {
|
|||
par.insert("flag_primary".into(), yesno(client.is_primary).into());
|
||||
par.insert("flag_visible".into(), yesno(client.is_visible).into());
|
||||
|
||||
let state_cell = self.state.parse_template(
|
||||
&doc_params(&self.globals),
|
||||
let globals = layout.state.globals.clone();
|
||||
|
||||
let state_cell = self.state.realize_template(
|
||||
&doc_params_tab_process_list(&globals),
|
||||
"Cell",
|
||||
&mut frontend.layout,
|
||||
layout,
|
||||
self.id_list_parent,
|
||||
par,
|
||||
)?;
|
||||
|
||||
let checkbox = state_cell.fetch_component_as::<ComponentCheckbox>("checkbox")?;
|
||||
checkbox.on_toggle({
|
||||
let tasks = self.tasks.clone();
|
||||
let tasks = tasks.clone();
|
||||
let client_name = client.name.clone();
|
||||
Box::new(move |_common, e| {
|
||||
if e.checked {
|
||||
tasks.push(Task::FocusClient(client_name.clone()));
|
||||
tasks.push(Task::ProcessListFocusClient(client_name.clone()));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
|
@ -142,41 +604,63 @@ impl<T> TabMonado<T> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn refresh(&mut self, frontend: &mut Frontend<T>, data: &mut T) -> anyhow::Result<()> {
|
||||
fn focus_client<T>(
|
||||
&mut self,
|
||||
frontend: &mut Frontend<T>,
|
||||
data: &mut T,
|
||||
name: String,
|
||||
tasks: &Tasks<Task>,
|
||||
) -> anyhow::Result<()> {
|
||||
frontend.interface.monado_client_focus(data, &name)?;
|
||||
tasks.push(Task::ProcessListRefresh);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn refresh<T>(&mut self, frontend: &mut Frontend<T>, data: &mut T, tasks: &Tasks<Task>) -> anyhow::Result<()> {
|
||||
log::debug!("refreshing monado client list");
|
||||
|
||||
let clients = frontend.interface.monado_client_list(data)?;
|
||||
let clients = frontend.interface.monado_client_list(data, true)?;
|
||||
|
||||
frontend.layout.remove_children(self.id_list_parent);
|
||||
self.cells.clear();
|
||||
|
||||
for client in clients {
|
||||
self.mount_client(frontend, &client)?;
|
||||
}
|
||||
|
||||
// get brightness
|
||||
let slider_brightness = self.state.fetch_component_as::<ComponentSlider>("slider_brightness")?;
|
||||
if let Some(brightness) = frontend.interface.monado_brightness_get(data) {
|
||||
let mut c = frontend.layout.start_common();
|
||||
slider_brightness.set_value(&mut c.common(), brightness * 100.0);
|
||||
c.finish()?;
|
||||
|
||||
slider_brightness.on_value_changed({
|
||||
let tasks = self.tasks.clone();
|
||||
Box::new(move |_common, e| {
|
||||
tasks.push(Task::SetBrightness(e.value / 100.0));
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
self.mount_client(&mut frontend.layout, &client, tasks)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn focus_client(&mut self, frontend: &mut Frontend<T>, data: &mut T, name: String) -> anyhow::Result<()> {
|
||||
frontend.interface.monado_client_focus(data, &name)?;
|
||||
self.tasks.push(Task::Refresh);
|
||||
Ok(())
|
||||
impl<T> TabMonado<T> {
|
||||
pub fn new(frontend: &mut Frontend<T>, parent_id: WidgetID) -> anyhow::Result<Self> {
|
||||
let globals = frontend.layout.state.globals.clone();
|
||||
let state = wgui::parser::parse_from_assets(&doc_params_monado(&globals), &mut frontend.layout, parent_id)?;
|
||||
let id_content = state.get_widget_id("content")?;
|
||||
let tabs = state.fetch_component_as::<ComponentTabs>("tabs")?;
|
||||
|
||||
let tasks = Tasks::<Task>::new();
|
||||
|
||||
tabs.on_select({
|
||||
let tasks = tasks.clone();
|
||||
Rc::new(move |_common, evt| {
|
||||
if let Some(tab) = TabNameEnum::from_string(&evt.name) {
|
||||
tasks.push(Task::SetTab(tab));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
tasks.push(Task::SetTab(TabNameEnum::ProcessList));
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
marker: PhantomData,
|
||||
tasks,
|
||||
id_content,
|
||||
ticks: 0,
|
||||
subtab: Subtab::Empty,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_brightness(&mut self, frontend: &mut Frontend<T>, data: &mut T, brightness: f32) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,323 @@
|
|||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::tab::settings::{self, SettingType, Task, horiz_cell, mount_requires_restart};
|
||||
use wgui::{
|
||||
components::{
|
||||
button::{ButtonClickEvent, ComponentButton},
|
||||
checkbox::ComponentCheckbox,
|
||||
slider::ComponentSlider,
|
||||
},
|
||||
layout::{Layout, WidgetID},
|
||||
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||
task::Tasks,
|
||||
widget::label::WidgetLabel,
|
||||
windowing::context_menu,
|
||||
};
|
||||
use wlx_common::config::GeneralConfig;
|
||||
|
||||
pub fn options_category(
|
||||
mp: &mut MacroParams,
|
||||
parent: WidgetID,
|
||||
translation: &str,
|
||||
icon: &str,
|
||||
) -> anyhow::Result<WidgetID> {
|
||||
let id = mp.idx.to_string();
|
||||
mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("translation"), Rc::from(translation));
|
||||
params.insert(Rc::from("icon"), Rc::from(icon));
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
mp.parser_state
|
||||
.instantiate_template(mp.doc_params, "SettingsGroupBox", mp.layout, parent, params)?;
|
||||
|
||||
mp.parser_state.get_widget_id(&id)
|
||||
}
|
||||
|
||||
pub fn options_checkbox(mp: &mut MacroParams, parent: WidgetID, setting: SettingType) -> anyhow::Result<()> {
|
||||
let id = mp.idx.to_string();
|
||||
mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
match setting.get_translation() {
|
||||
Ok(translation) => params.insert(Rc::from("translation"), translation.into()),
|
||||
Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()),
|
||||
};
|
||||
|
||||
if let Some(tooltip) = setting.get_tooltip() {
|
||||
params.insert(Rc::from("tooltip"), Rc::from(tooltip));
|
||||
}
|
||||
|
||||
let checked = if *setting.mut_bool(mp.config) { "1" } else { "0" };
|
||||
params.insert(Rc::from("checked"), Rc::from(checked));
|
||||
|
||||
let id_cell = horiz_cell(mp.layout, parent)?;
|
||||
|
||||
mp.parser_state
|
||||
.instantiate_template(mp.doc_params, "CheckBoxSetting", mp.layout, id_cell, params)?;
|
||||
|
||||
if setting.requires_restart() {
|
||||
mount_requires_restart(mp.layout, id_cell)?;
|
||||
}
|
||||
|
||||
let checkbox = mp.parser_state.fetch_component_as::<ComponentCheckbox>(&id)?;
|
||||
checkbox.on_toggle(Box::new({
|
||||
let tasks = mp.tasks.clone();
|
||||
move |_common, e| {
|
||||
tasks.push(Task::UpdateBool(setting, e.checked));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn options_slider_f32(
|
||||
mp: &mut MacroParams,
|
||||
parent: WidgetID,
|
||||
setting: SettingType,
|
||||
min: f32,
|
||||
max: f32,
|
||||
step: f32,
|
||||
) -> anyhow::Result<()> {
|
||||
let id = mp.idx.to_string();
|
||||
mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
match setting.get_translation() {
|
||||
Ok(translation) => params.insert(Rc::from("translation"), translation.into()),
|
||||
Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()),
|
||||
};
|
||||
|
||||
if let Some(tooltip) = setting.get_tooltip() {
|
||||
params.insert(Rc::from("tooltip"), Rc::from(tooltip));
|
||||
}
|
||||
|
||||
let value = setting.mut_f32(mp.config).to_string();
|
||||
params.insert(Rc::from("value"), Rc::from(value));
|
||||
params.insert(Rc::from("min"), Rc::from(min.to_string()));
|
||||
params.insert(Rc::from("max"), Rc::from(max.to_string()));
|
||||
params.insert(Rc::from("step"), Rc::from(step.to_string()));
|
||||
|
||||
let id_cell = horiz_cell(mp.layout, parent)?;
|
||||
|
||||
mp.parser_state
|
||||
.instantiate_template(mp.doc_params, "SliderSetting", mp.layout, id_cell, params)?;
|
||||
|
||||
if setting.requires_restart() {
|
||||
mount_requires_restart(mp.layout, id_cell)?;
|
||||
}
|
||||
|
||||
let slider = mp.parser_state.fetch_component_as::<ComponentSlider>(&id)?;
|
||||
slider.on_value_changed(Box::new({
|
||||
let tasks = mp.tasks.clone();
|
||||
move |_common, e| {
|
||||
tasks.push(Task::UpdateFloat(setting, e.value));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn options_slider_i32(
|
||||
mp: &mut MacroParams,
|
||||
parent: WidgetID,
|
||||
setting: SettingType,
|
||||
min: i32,
|
||||
max: i32,
|
||||
step: i32,
|
||||
) -> anyhow::Result<()> {
|
||||
let id = mp.idx.to_string();
|
||||
mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
match setting.get_translation() {
|
||||
Ok(translation) => params.insert(Rc::from("translation"), translation.into()),
|
||||
Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()),
|
||||
};
|
||||
|
||||
if let Some(tooltip) = setting.get_tooltip() {
|
||||
params.insert(Rc::from("tooltip"), Rc::from(tooltip));
|
||||
}
|
||||
|
||||
let id_cell = horiz_cell(mp.layout, parent)?;
|
||||
|
||||
let value = setting.mut_i32(mp.config).to_string();
|
||||
params.insert(Rc::from("value"), Rc::from(value));
|
||||
params.insert(Rc::from("min"), Rc::from(min.to_string()));
|
||||
params.insert(Rc::from("max"), Rc::from(max.to_string()));
|
||||
params.insert(Rc::from("step"), Rc::from(step.to_string()));
|
||||
|
||||
mp.parser_state
|
||||
.instantiate_template(mp.doc_params, "SliderSetting", mp.layout, id_cell, params)?;
|
||||
|
||||
if setting.requires_restart() {
|
||||
mount_requires_restart(mp.layout, id_cell)?;
|
||||
}
|
||||
|
||||
let slider = mp.parser_state.fetch_component_as::<ComponentSlider>(&id)?;
|
||||
slider.on_value_changed(Box::new({
|
||||
let tasks = mp.tasks.clone();
|
||||
move |_common, e| {
|
||||
tasks.push(Task::UpdateInt(setting, e.value as i32));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn options_dropdown<EnumType>(
|
||||
mp: &mut MacroParams,
|
||||
parent: WidgetID,
|
||||
setting: &'static SettingType,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
EnumType: strum::VariantArray + strum::EnumProperty + std::convert::AsRef<str> + Copy + 'static,
|
||||
{
|
||||
let id = mp.idx.to_string();
|
||||
mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
match setting.get_translation() {
|
||||
Ok(translation) => params.insert(Rc::from("translation"), translation.into()),
|
||||
Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()),
|
||||
};
|
||||
|
||||
if let Some(tooltip) = setting.get_tooltip() {
|
||||
params.insert(Rc::from("tooltip"), Rc::from(tooltip));
|
||||
}
|
||||
|
||||
let id_cell = horiz_cell(mp.layout, parent)?;
|
||||
|
||||
mp.parser_state
|
||||
.instantiate_template(mp.doc_params, "DropdownButton", mp.layout, id_cell, params)?;
|
||||
|
||||
if setting.requires_restart() {
|
||||
mount_requires_restart(mp.layout, id_cell)?;
|
||||
}
|
||||
|
||||
let setting_str = setting.as_ref();
|
||||
let title = setting.get_enum_title(mp.config);
|
||||
|
||||
{
|
||||
let mut label = mp
|
||||
.parser_state
|
||||
.fetch_widget_as::<WidgetLabel>(&mp.layout.state, &format!("{id}_value"))?;
|
||||
label.set_text_simple(&mut mp.layout.state.globals.get(), title);
|
||||
}
|
||||
|
||||
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
||||
btn.on_click(Rc::new({
|
||||
let tasks = mp.tasks.clone();
|
||||
move |_common, e: ButtonClickEvent| {
|
||||
tasks.push(Task::OpenContextMenu(
|
||||
e.mouse_pos_absolute.unwrap_or_default(),
|
||||
EnumType::VARIANTS
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
if item.get_bool("Hidden").unwrap_or(false) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let value = item.as_ref();
|
||||
let title = SettingType::get_enum_title_inner(*item);
|
||||
let tooltip = SettingType::get_enum_tooltip_inner(*item);
|
||||
|
||||
let text = &title.text;
|
||||
let translated = if title.translated { "1" } else { "0" };
|
||||
|
||||
Some(context_menu::Cell {
|
||||
action_name: Some(format!("{setting_str};{id};{value};{text};{translated}").into()),
|
||||
title,
|
||||
tooltip,
|
||||
attribs: vec![],
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn options_danger_button(
|
||||
mp: &mut MacroParams,
|
||||
parent: WidgetID,
|
||||
translation: &str,
|
||||
icon: &str,
|
||||
task: Task,
|
||||
) -> anyhow::Result<()> {
|
||||
let id = mp.idx.to_string();
|
||||
mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
params.insert(Rc::from("translation"), Rc::from(translation));
|
||||
params.insert(Rc::from("icon"), Rc::from(icon));
|
||||
|
||||
mp.parser_state
|
||||
.instantiate_template(mp.doc_params, "DangerButton", mp.layout, parent, params)?;
|
||||
|
||||
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
||||
btn.on_click(Rc::new({
|
||||
let tasks = mp.tasks.clone();
|
||||
move |_common, _e| {
|
||||
tasks.push(task.clone());
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn options_autostart_app(
|
||||
mp: &mut MacroParams,
|
||||
parent: WidgetID,
|
||||
text: &str,
|
||||
ids: &mut Vec<Rc<str>>,
|
||||
) -> anyhow::Result<()> {
|
||||
let id = mp.idx.to_string();
|
||||
mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
params.insert(Rc::from("text"), Rc::from(text));
|
||||
|
||||
mp.parser_state
|
||||
.instantiate_template(mp.doc_params, "AutostartApp", mp.layout, parent, params)?;
|
||||
|
||||
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
||||
let id: Rc<str> = Rc::from(id);
|
||||
|
||||
ids.push(id.clone());
|
||||
|
||||
btn.on_click(Rc::new({
|
||||
let tasks = mp.tasks.clone();
|
||||
move |_common, _e| {
|
||||
tasks.push(Task::RemoveAutostartApp(id.clone()));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct MacroParams<'a> {
|
||||
pub layout: &'a mut Layout,
|
||||
pub parser_state: &'a mut ParserState,
|
||||
pub doc_params: &'a ParseDocumentParams<'a>,
|
||||
pub config: &'a mut GeneralConfig,
|
||||
pub tasks: Tasks<settings::Task>,
|
||||
pub idx: usize,
|
||||
}
|
||||
|
|
@ -1,15 +1,9 @@
|
|||
use std::{collections::HashMap, marker::PhantomData, rc::Rc, str::FromStr};
|
||||
|
||||
use glam::Vec2;
|
||||
use strum::{AsRefStr, EnumProperty, EnumString, VariantArray};
|
||||
use std::{marker::PhantomData, rc::Rc, str::FromStr};
|
||||
use strum::{AsRefStr, EnumProperty, EnumString};
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::{
|
||||
button::{ButtonClickEvent, ComponentButton},
|
||||
checkbox::ComponentCheckbox,
|
||||
slider::ComponentSlider,
|
||||
tabs::ComponentTabs,
|
||||
},
|
||||
components::tabs::ComponentTabs,
|
||||
drawing,
|
||||
event::{CallbackDataCommon, EventAlterables},
|
||||
globals::WguiGlobals,
|
||||
|
|
@ -30,9 +24,17 @@ use wlx_common::{config::GeneralConfig, config_io::ConfigRoot, dash_interface::R
|
|||
|
||||
use crate::{
|
||||
frontend::{Frontend, FrontendTask},
|
||||
tab::{Tab, TabType},
|
||||
tab::{Tab, TabType, settings::macros::MacroParams},
|
||||
};
|
||||
|
||||
mod macros;
|
||||
mod tab_autostart_apps;
|
||||
mod tab_controls;
|
||||
mod tab_features;
|
||||
mod tab_look_and_feel;
|
||||
mod tab_misc;
|
||||
mod tab_troubleshooting;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum TabNameEnum {
|
||||
LookAndFeel,
|
||||
|
|
@ -57,6 +59,7 @@ impl TabNameEnum {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Task {
|
||||
UpdateBool(SettingType, bool),
|
||||
UpdateFloat(SettingType, f32),
|
||||
|
|
@ -488,287 +491,6 @@ fn mount_requires_restart(layout: &mut Layout, parent: WidgetID) -> anyhow::Resu
|
|||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! category {
|
||||
($pe:expr, $root:expr, $translation:expr, $icon:expr) => {{
|
||||
let id = $pe.idx.to_string();
|
||||
$pe.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("translation"), Rc::from($translation));
|
||||
params.insert(Rc::from("icon"), Rc::from($icon));
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
$pe
|
||||
.parser_state
|
||||
.instantiate_template($pe.doc_params, "SettingsGroupBox", $pe.layout, $root, params)?;
|
||||
|
||||
$pe.parser_state.get_widget_id(&id)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! checkbox {
|
||||
($mp:expr, $root:expr, $setting:expr) => {
|
||||
let id = $mp.idx.to_string();
|
||||
$mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
match $setting.get_translation() {
|
||||
Ok(translation) => params.insert(Rc::from("translation"), translation.into()),
|
||||
Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()),
|
||||
};
|
||||
|
||||
if let Some(tooltip) = $setting.get_tooltip() {
|
||||
params.insert(Rc::from("tooltip"), Rc::from(tooltip));
|
||||
}
|
||||
|
||||
let checked = if *$setting.mut_bool($mp.config) { "1" } else { "0" };
|
||||
params.insert(Rc::from("checked"), Rc::from(checked));
|
||||
|
||||
let id_cell = horiz_cell($mp.layout, $root)?;
|
||||
|
||||
$mp
|
||||
.parser_state
|
||||
.instantiate_template($mp.doc_params, "CheckBoxSetting", $mp.layout, id_cell, params)?;
|
||||
|
||||
if $setting.requires_restart() {
|
||||
mount_requires_restart($mp.layout, id_cell)?;
|
||||
}
|
||||
|
||||
let checkbox = $mp.parser_state.fetch_component_as::<ComponentCheckbox>(&id)?;
|
||||
checkbox.on_toggle(Box::new({
|
||||
let tasks = $mp.tasks.clone();
|
||||
move |_common, e| {
|
||||
tasks.push(Task::UpdateBool($setting, e.checked));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! slider_f32 {
|
||||
($mp:expr, $root:expr, $setting:expr, $min:expr, $max:expr, $step:expr) => {
|
||||
let id = $mp.idx.to_string();
|
||||
$mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
match $setting.get_translation() {
|
||||
Ok(translation) => params.insert(Rc::from("translation"), translation.into()),
|
||||
Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()),
|
||||
};
|
||||
|
||||
if let Some(tooltip) = $setting.get_tooltip() {
|
||||
params.insert(Rc::from("tooltip"), Rc::from(tooltip));
|
||||
}
|
||||
|
||||
let value = $setting.mut_f32($mp.config).to_string();
|
||||
params.insert(Rc::from("value"), Rc::from(value));
|
||||
params.insert(Rc::from("min"), Rc::from($min.to_string()));
|
||||
params.insert(Rc::from("max"), Rc::from($max.to_string()));
|
||||
params.insert(Rc::from("step"), Rc::from($step.to_string()));
|
||||
|
||||
let id_cell = horiz_cell($mp.layout, $root)?;
|
||||
|
||||
$mp
|
||||
.parser_state
|
||||
.instantiate_template($mp.doc_params, "SliderSetting", $mp.layout, id_cell, params)?;
|
||||
|
||||
if $setting.requires_restart() {
|
||||
mount_requires_restart($mp.layout, id_cell)?;
|
||||
}
|
||||
|
||||
let slider = $mp.parser_state.fetch_component_as::<ComponentSlider>(&id)?;
|
||||
slider.on_value_changed(Box::new({
|
||||
let tasks = $mp.tasks.clone();
|
||||
move |_common, e| {
|
||||
tasks.push(Task::UpdateFloat($setting, e.value));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! slider_i32 {
|
||||
($mp:expr, $root:expr, $setting:expr, $min:expr, $max:expr, $step:expr) => {
|
||||
let id = $mp.idx.to_string();
|
||||
$mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
match $setting.get_translation() {
|
||||
Ok(translation) => params.insert(Rc::from("translation"), translation.into()),
|
||||
Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()),
|
||||
};
|
||||
|
||||
if let Some(tooltip) = $setting.get_tooltip() {
|
||||
params.insert(Rc::from("tooltip"), Rc::from(tooltip));
|
||||
}
|
||||
|
||||
let id_cell = horiz_cell($mp.layout, $root)?;
|
||||
|
||||
let value = $setting.mut_i32($mp.config).to_string();
|
||||
params.insert(Rc::from("value"), Rc::from(value));
|
||||
params.insert(Rc::from("min"), Rc::from($min.to_string()));
|
||||
params.insert(Rc::from("max"), Rc::from($max.to_string()));
|
||||
params.insert(Rc::from("step"), Rc::from($step.to_string()));
|
||||
|
||||
$mp
|
||||
.parser_state
|
||||
.instantiate_template($mp.doc_params, "SliderSetting", $mp.layout, id_cell, params)?;
|
||||
|
||||
if $setting.requires_restart() {
|
||||
mount_requires_restart($mp.layout, id_cell)?;
|
||||
}
|
||||
|
||||
let slider = $mp.parser_state.fetch_component_as::<ComponentSlider>(&id)?;
|
||||
slider.on_value_changed(Box::new({
|
||||
let tasks = $mp.tasks.clone();
|
||||
move |_common, e| {
|
||||
tasks.push(Task::UpdateInt($setting, e.value as i32));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! dropdown {
|
||||
($mp:expr /* `MacroParams` struct */, $root:expr, $setting:expr, $options:expr) => {
|
||||
let id = $mp.idx.to_string();
|
||||
$mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
|
||||
match $setting.get_translation() {
|
||||
Ok(translation) => params.insert(Rc::from("translation"), translation.into()),
|
||||
Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()),
|
||||
};
|
||||
|
||||
if let Some(tooltip) = $setting.get_tooltip() {
|
||||
params.insert(Rc::from("tooltip"), Rc::from(tooltip));
|
||||
}
|
||||
|
||||
let id_cell = horiz_cell($mp.layout, $root)?;
|
||||
|
||||
$mp
|
||||
.parser_state
|
||||
.instantiate_template($mp.doc_params, "DropdownButton", $mp.layout, id_cell, params)?;
|
||||
|
||||
if $setting.requires_restart() {
|
||||
mount_requires_restart($mp.layout, id_cell)?;
|
||||
}
|
||||
|
||||
let setting_str = $setting.as_ref();
|
||||
let title = $setting.get_enum_title($mp.config);
|
||||
|
||||
{
|
||||
let mut label = $mp
|
||||
.parser_state
|
||||
.fetch_widget_as::<WidgetLabel>(&$mp.layout.state, &format!("{id}_value"))?;
|
||||
label.set_text_simple(&mut $mp.layout.state.globals.get(), title);
|
||||
}
|
||||
|
||||
let btn = $mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
||||
btn.on_click(Rc::new({
|
||||
let tasks = $mp.tasks.clone();
|
||||
move |_common, e: ButtonClickEvent| {
|
||||
tasks.push(Task::OpenContextMenu(
|
||||
e.mouse_pos_absolute.unwrap_or_default(),
|
||||
$options
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
if item.get_bool("Hidden").unwrap_or(false) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let value = item.as_ref();
|
||||
let title = SettingType::get_enum_title_inner(*item);
|
||||
let tooltip = SettingType::get_enum_tooltip_inner(*item);
|
||||
|
||||
let text = &title.text;
|
||||
let translated = if title.translated { "1" } else { "0" };
|
||||
|
||||
Some(context_menu::Cell {
|
||||
action_name: Some(format!("{setting_str};{id};{value};{text};{translated}").into()),
|
||||
title,
|
||||
tooltip,
|
||||
attribs: vec![],
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! danger_button {
|
||||
($mp:expr, $root:expr, $translation:expr, $icon:expr, $task:expr) => {
|
||||
let id = $mp.idx.to_string();
|
||||
$mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
params.insert(Rc::from("translation"), Rc::from($translation));
|
||||
params.insert(Rc::from("icon"), Rc::from($icon));
|
||||
|
||||
$mp
|
||||
.parser_state
|
||||
.instantiate_template($mp.doc_params, "DangerButton", $mp.layout, $root, params)?;
|
||||
|
||||
let btn = $mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
||||
btn.on_click(Rc::new({
|
||||
let tasks = $mp.tasks.clone();
|
||||
move |_common, _e| {
|
||||
tasks.push($task);
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! autostart_app {
|
||||
($mp:expr, $root:expr, $text:expr, $ids:expr) => {
|
||||
let id = $mp.idx.to_string();
|
||||
$mp.idx += 1;
|
||||
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
|
||||
params.insert(Rc::from("text"), Rc::from($text.as_str()));
|
||||
|
||||
$mp
|
||||
.parser_state
|
||||
.instantiate_template($mp.doc_params, "AutostartApp", $mp.layout, $root, params)?;
|
||||
|
||||
let btn = $mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
||||
let id: Rc<str> = Rc::from(id);
|
||||
|
||||
$ids.push(id.clone());
|
||||
|
||||
btn.on_click(Rc::new({
|
||||
let tasks = $mp.tasks.clone();
|
||||
move |_common, _e| {
|
||||
tasks.push(Task::RemoveAutostartApp(id.clone()));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
struct MacroParams<'a> {
|
||||
layout: &'a mut Layout,
|
||||
parser_state: &'a mut ParserState,
|
||||
doc_params: &'a ParseDocumentParams<'a>,
|
||||
config: &'a mut GeneralConfig,
|
||||
tasks: Tasks<Task>,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
fn doc_params(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> {
|
||||
ParseDocumentParams {
|
||||
globals: globals.clone(),
|
||||
|
|
@ -794,120 +516,22 @@ impl<T> TabSettings<T> {
|
|||
|
||||
match name {
|
||||
TabNameEnum::LookAndFeel => {
|
||||
let c = category!(mp, root, "APP_SETTINGS.LOOK_AND_FEEL", "dashboard/palette.svg")?;
|
||||
dropdown!(mp, c, SettingType::Language, wlx_common::locale::Language::VARIANTS);
|
||||
checkbox!(mp, c, SettingType::OpaqueBackground);
|
||||
checkbox!(mp, c, SettingType::HideUsername);
|
||||
checkbox!(mp, c, SettingType::HideGrabHelp);
|
||||
slider_f32!(mp, c, SettingType::UiAnimationSpeed, 0.5, 5.0, 0.1); // min, max, step
|
||||
slider_f32!(mp, c, SettingType::UiGradientIntensity, 0.0, 1.0, 0.05); // min, max, step
|
||||
slider_f32!(mp, c, SettingType::UiRoundMultiplier, 0.5, 5.0, 0.1);
|
||||
checkbox!(mp, c, SettingType::SetsOnWatch);
|
||||
checkbox!(mp, c, SettingType::UseSkybox);
|
||||
slider_f32!(mp, c, SettingType::GridOpacity, 0.0, 1.0, 0.05); // min, max, step
|
||||
checkbox!(mp, c, SettingType::UsePassthrough);
|
||||
checkbox!(mp, c, SettingType::Clock12h);
|
||||
tab_look_and_feel::mount(&mut mp, root)?;
|
||||
}
|
||||
TabNameEnum::Features => {
|
||||
let c = category!(mp, root, "APP_SETTINGS.FEATURES", "dashboard/options.svg")?;
|
||||
checkbox!(mp, c, SettingType::NotificationsEnabled);
|
||||
checkbox!(mp, c, SettingType::NotificationsSoundEnabled);
|
||||
checkbox!(mp, c, SettingType::KeyboardSoundEnabled);
|
||||
checkbox!(mp, c, SettingType::KeyboardSwipeToTypeEnabled);
|
||||
checkbox!(mp, c, SettingType::SpaceDragUnlocked);
|
||||
checkbox!(mp, c, SettingType::SpaceRotateUnlocked);
|
||||
slider_f32!(mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5);
|
||||
checkbox!(mp, c, SettingType::BlockGameInput);
|
||||
checkbox!(mp, c, SettingType::BlockGameInputIgnoreWatch);
|
||||
checkbox!(mp, c, SettingType::BlockPosesOnKbdInteraction);
|
||||
tab_features::mount(&mut mp, root)?;
|
||||
}
|
||||
TabNameEnum::Controls => {
|
||||
let c = category!(mp, root, "APP_SETTINGS.CONTROLS", "dashboard/controller.svg")?;
|
||||
dropdown!(
|
||||
mp,
|
||||
c,
|
||||
SettingType::KeyboardMiddleClick,
|
||||
wlx_common::config::AltModifier::VARIANTS
|
||||
);
|
||||
dropdown!(
|
||||
mp,
|
||||
c,
|
||||
SettingType::HandsfreePointer,
|
||||
wlx_common::config::HandsfreePointer::VARIANTS
|
||||
);
|
||||
checkbox!(mp, c, SettingType::FocusFollowsMouseMode);
|
||||
checkbox!(mp, c, SettingType::LeftHandedMouse);
|
||||
checkbox!(mp, c, SettingType::AllowSliding);
|
||||
checkbox!(mp, c, SettingType::InvertScrollDirectionX);
|
||||
checkbox!(mp, c, SettingType::InvertScrollDirectionY);
|
||||
slider_f32!(mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1);
|
||||
slider_f32!(mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1);
|
||||
slider_f32!(mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1);
|
||||
slider_f32!(mp, c, SettingType::XrClickSensitivity, 0.1, 1.0, 0.1);
|
||||
slider_f32!(mp, c, SettingType::XrClickSensitivityRelease, 0.1, 1.0, 0.1);
|
||||
slider_i32!(mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50);
|
||||
tab_controls::mount(&mut mp, root)?;
|
||||
}
|
||||
TabNameEnum::Misc => {
|
||||
let c = category!(mp, root, "APP_SETTINGS.MISC", "dashboard/blocks.svg")?;
|
||||
dropdown!(
|
||||
mp,
|
||||
c,
|
||||
SettingType::CaptureMethod,
|
||||
wlx_common::config::CaptureMethod::VARIANTS
|
||||
);
|
||||
checkbox!(mp, c, SettingType::XwaylandByDefault);
|
||||
checkbox!(mp, c, SettingType::UprightScreenFix);
|
||||
checkbox!(mp, c, SettingType::DoubleCursorFix);
|
||||
checkbox!(mp, c, SettingType::ScreenRenderDown);
|
||||
tab_misc::mount(&mut mp, root)?;
|
||||
}
|
||||
TabNameEnum::AutostartApps => {
|
||||
self.app_button_ids = vec![];
|
||||
|
||||
if !mp.config.autostart_apps.is_empty() {
|
||||
let c = category!(mp, root, "APP_SETTINGS.AUTOSTART_APPS", "dashboard/apps.svg")?;
|
||||
|
||||
for app in &mp.config.autostart_apps {
|
||||
autostart_app!(mp, c, app.name, self.app_button_ids);
|
||||
}
|
||||
}
|
||||
tab_autostart_apps::mount(&mut mp, root, &mut self.app_button_ids)?;
|
||||
}
|
||||
TabNameEnum::Troubleshooting => {
|
||||
let c = category!(mp, root, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/cpu.svg")?;
|
||||
danger_button!(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.RESET_PLAYSPACE",
|
||||
"dashboard/recenter.svg",
|
||||
Task::ResetPlayspace
|
||||
);
|
||||
danger_button!(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS",
|
||||
"dashboard/display.svg",
|
||||
Task::ClearPipewireTokens
|
||||
);
|
||||
danger_button!(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.CLEAR_SAVED_STATE",
|
||||
"dashboard/binary.svg",
|
||||
Task::ClearSavedState
|
||||
);
|
||||
danger_button!(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.DELETE_ALL_CONFIGS",
|
||||
"dashboard/circle.svg",
|
||||
Task::DeleteAllConfigs
|
||||
);
|
||||
danger_button!(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.RESTART_SOFTWARE",
|
||||
"dashboard/refresh.svg",
|
||||
Task::RestartSoftware
|
||||
);
|
||||
tab_troubleshooting::mount(&mut mp, root)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use crate::tab::settings::macros::{MacroParams, options_autostart_app, options_category};
|
||||
use wgui::layout::WidgetID;
|
||||
|
||||
pub fn mount(mp: &mut MacroParams, parent: WidgetID, app_button_ids: &mut Vec<Rc<str>>) -> anyhow::Result<()> {
|
||||
*app_button_ids = Vec::new();
|
||||
|
||||
if !mp.config.autostart_apps.is_empty() {
|
||||
let c = options_category(mp, parent, "APP_SETTINGS.AUTOSTART_APPS", "dashboard/apps.svg")?;
|
||||
|
||||
// todo: prevent clone
|
||||
let autostart_apps = mp.config.autostart_apps.clone();
|
||||
for app in autostart_apps {
|
||||
options_autostart_app(mp, c, &app.name, app_button_ids)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
use crate::tab::settings::{
|
||||
SettingType,
|
||||
macros::{MacroParams, options_category, options_checkbox, options_dropdown, options_slider_f32, options_slider_i32},
|
||||
};
|
||||
use wgui::layout::WidgetID;
|
||||
|
||||
pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
|
||||
let c = options_category(mp, parent, "APP_SETTINGS.CONTROLS", "dashboard/controller.svg")?;
|
||||
options_dropdown::<wlx_common::config::AltModifier>(mp, c, &SettingType::KeyboardMiddleClick)?;
|
||||
options_dropdown::<wlx_common::config::HandsfreePointer>(mp, c, &SettingType::HandsfreePointer)?;
|
||||
options_checkbox(mp, c, SettingType::FocusFollowsMouseMode)?;
|
||||
options_checkbox(mp, c, SettingType::LeftHandedMouse)?;
|
||||
options_checkbox(mp, c, SettingType::AllowSliding)?;
|
||||
options_checkbox(mp, c, SettingType::InvertScrollDirectionX)?;
|
||||
options_checkbox(mp, c, SettingType::InvertScrollDirectionY)?;
|
||||
options_slider_f32(mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1)?;
|
||||
options_slider_f32(mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1)?;
|
||||
options_slider_f32(mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1)?;
|
||||
options_slider_f32(mp, c, SettingType::XrClickSensitivity, 0.1, 1.0, 0.1)?;
|
||||
options_slider_f32(mp, c, SettingType::XrClickSensitivityRelease, 0.1, 1.0, 0.1)?;
|
||||
options_slider_i32(mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
use crate::tab::settings::{
|
||||
SettingType,
|
||||
macros::{MacroParams, options_category, options_checkbox, options_slider_f32},
|
||||
};
|
||||
use wgui::layout::WidgetID;
|
||||
|
||||
pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
|
||||
let c = options_category(mp, parent, "APP_SETTINGS.FEATURES", "dashboard/options.svg")?;
|
||||
options_checkbox(mp, c, SettingType::NotificationsEnabled)?;
|
||||
options_checkbox(mp, c, SettingType::NotificationsSoundEnabled)?;
|
||||
options_checkbox(mp, c, SettingType::KeyboardSoundEnabled)?;
|
||||
options_checkbox(mp, c, SettingType::KeyboardSwipeToTypeEnabled)?;
|
||||
options_checkbox(mp, c, SettingType::SpaceDragUnlocked)?;
|
||||
options_checkbox(mp, c, SettingType::SpaceRotateUnlocked)?;
|
||||
options_slider_f32(mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5)?;
|
||||
options_checkbox(mp, c, SettingType::BlockGameInput)?;
|
||||
options_checkbox(mp, c, SettingType::BlockGameInputIgnoreWatch)?;
|
||||
options_checkbox(mp, c, SettingType::BlockPosesOnKbdInteraction)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
use crate::tab::settings::{
|
||||
SettingType,
|
||||
macros::{MacroParams, options_category, options_checkbox, options_dropdown, options_slider_f32},
|
||||
};
|
||||
use wgui::layout::WidgetID;
|
||||
|
||||
pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
|
||||
let c = options_category(mp, parent, "APP_SETTINGS.LOOK_AND_FEEL", "dashboard/palette.svg")?;
|
||||
options_dropdown::<wlx_common::locale::Language>(mp, c, &SettingType::Language)?;
|
||||
options_checkbox(mp, c, SettingType::OpaqueBackground)?;
|
||||
options_checkbox(mp, c, SettingType::HideUsername)?;
|
||||
options_checkbox(mp, c, SettingType::HideGrabHelp)?;
|
||||
options_slider_f32(mp, c, SettingType::UiAnimationSpeed, 0.5, 5.0, 0.1)?; // min, max, step
|
||||
options_slider_f32(mp, c, SettingType::UiGradientIntensity, 0.0, 1.0, 0.05)?; // min, max, step
|
||||
options_slider_f32(mp, c, SettingType::UiRoundMultiplier, 0.5, 5.0, 0.1)?;
|
||||
options_checkbox(mp, c, SettingType::SetsOnWatch)?;
|
||||
options_checkbox(mp, c, SettingType::UseSkybox)?;
|
||||
options_slider_f32(mp, c, SettingType::GridOpacity, 0.0, 1.0, 0.05)?; // min, max, step
|
||||
options_checkbox(mp, c, SettingType::UsePassthrough)?;
|
||||
options_checkbox(mp, c, SettingType::Clock12h)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
use crate::tab::settings::{
|
||||
SettingType,
|
||||
macros::{MacroParams, options_category, options_checkbox, options_dropdown},
|
||||
};
|
||||
use wgui::layout::WidgetID;
|
||||
|
||||
pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
|
||||
let c = options_category(mp, parent, "APP_SETTINGS.MISC", "dashboard/blocks.svg")?;
|
||||
options_dropdown::<wlx_common::config::CaptureMethod>(mp, c, &SettingType::CaptureMethod)?;
|
||||
options_checkbox(mp, c, SettingType::XwaylandByDefault)?;
|
||||
options_checkbox(mp, c, SettingType::UprightScreenFix)?;
|
||||
options_checkbox(mp, c, SettingType::DoubleCursorFix)?;
|
||||
options_checkbox(mp, c, SettingType::ScreenRenderDown)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
use crate::tab::settings::{
|
||||
Task,
|
||||
macros::{MacroParams, options_category, options_danger_button},
|
||||
};
|
||||
use wgui::layout::WidgetID;
|
||||
|
||||
pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
|
||||
let c = options_category(mp, parent, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/cpu.svg")?;
|
||||
options_danger_button(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.RESET_PLAYSPACE",
|
||||
"dashboard/recenter.svg",
|
||||
Task::ResetPlayspace,
|
||||
)?;
|
||||
options_danger_button(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS",
|
||||
"dashboard/display.svg",
|
||||
Task::ClearPipewireTokens,
|
||||
)?;
|
||||
options_danger_button(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.CLEAR_SAVED_STATE",
|
||||
"dashboard/binary.svg",
|
||||
Task::ClearSavedState,
|
||||
)?;
|
||||
options_danger_button(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.DELETE_ALL_CONFIGS",
|
||||
"dashboard/circle.svg",
|
||||
Task::DeleteAllConfigs,
|
||||
)?;
|
||||
options_danger_button(
|
||||
mp,
|
||||
c,
|
||||
"APP_SETTINGS.RESTART_SOFTWARE",
|
||||
"dashboard/refresh.svg",
|
||||
Task::RestartSoftware,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
use wlx_common::cache_dir;
|
||||
use wlx_common::{async_executor::AsyncExecutor, cache_dir};
|
||||
|
||||
use crate::util::{http_client, steam_utils::AppID, various::AsyncExecutor};
|
||||
use crate::util::{http_client, steam_utils::AppID};
|
||||
|
||||
pub struct CoverArt {
|
||||
// can be empty in case if data couldn't be fetched (use a fallback image then)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ use smol::{net::TcpStream, prelude::*};
|
|||
use std::convert::TryInto;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use crate::util::various::AsyncExecutor;
|
||||
use wlx_common::async_executor::AsyncExecutor;
|
||||
pub struct HttpClientResponse {
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,4 @@ pub mod pactl_wrapper;
|
|||
pub mod popup_manager;
|
||||
pub mod steam_utils;
|
||||
pub mod toast_manager;
|
||||
pub mod various;
|
||||
pub mod wgui_simple;
|
||||
|
|
|
|||
|
|
@ -750,7 +750,7 @@ impl View {
|
|||
|
||||
let data = self
|
||||
.state
|
||||
.parse_template(&doc_params(&self.globals), "Card", params.layout, self.id_devices, par)?;
|
||||
.realize_template(&doc_params(&self.globals), "Card", params.layout, self.id_devices, par)?;
|
||||
|
||||
let btn_card = data.fetch_component_as::<ComponentButton>("btn_card")?;
|
||||
btn_card.on_click({
|
||||
|
|
@ -764,7 +764,6 @@ impl View {
|
|||
})
|
||||
});
|
||||
|
||||
log::info!("mount card TODO: {}", params.card.name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -794,7 +793,7 @@ impl View {
|
|||
},
|
||||
);
|
||||
|
||||
let data = self.state.parse_template(
|
||||
let data = self.state.realize_template(
|
||||
&doc_params(&self.globals),
|
||||
"DeviceSlider",
|
||||
params.layout,
|
||||
|
|
@ -941,7 +940,7 @@ impl View {
|
|||
layout.remove_children(self.id_devices);
|
||||
|
||||
{
|
||||
let data = self.state.parse_template(
|
||||
let data = self.state.realize_template(
|
||||
&doc_params(&self.globals),
|
||||
"SelectAudioProfileText",
|
||||
layout,
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ use wgui::{
|
|||
util::WLength,
|
||||
},
|
||||
};
|
||||
use wlx_common::async_executor::AsyncExecutor;
|
||||
|
||||
use crate::util::{
|
||||
cached_fetcher::{self, CoverArt},
|
||||
steam_utils::{self, AppID},
|
||||
various::AsyncExecutor,
|
||||
};
|
||||
|
||||
pub struct ViewCommon {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use crate::{
|
|||
util::{
|
||||
cached_fetcher::{self, CoverArt},
|
||||
steam_utils::{self, AppID, AppManifest},
|
||||
various::AsyncExecutor,
|
||||
},
|
||||
views::game_cover,
|
||||
};
|
||||
|
|
@ -19,6 +18,7 @@ use wgui::{
|
|||
task::Tasks,
|
||||
widget::{ConstructEssentials, label::WidgetLabel},
|
||||
};
|
||||
use wlx_common::async_executor::AsyncExecutor;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Task {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use wgui::{
|
|||
label::{WidgetLabel, WidgetLabelParams},
|
||||
},
|
||||
};
|
||||
use wlx_common::async_executor::AsyncExecutor;
|
||||
|
||||
use crate::{
|
||||
frontend::{FrontendTask, FrontendTasks},
|
||||
|
|
@ -20,7 +21,6 @@ use crate::{
|
|||
cached_fetcher::CoverArt,
|
||||
popup_manager::{MountPopupParams, PopupHandle},
|
||||
steam_utils::{self, AppID, SteamUtils},
|
||||
various::AsyncExecutor,
|
||||
},
|
||||
views::{self, game_cover, game_launcher},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ impl View {
|
|||
for game in games {
|
||||
let game_name = View::extract_name_from_appid(&game.app_id, &self.installed_games);
|
||||
|
||||
let t = self.state.parse_template(
|
||||
let t = self.state.realize_template(
|
||||
&doc_params(layout.state.globals.clone()),
|
||||
"RunningGameCell",
|
||||
layout,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "prost_build"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
prost-build = "0.14.3"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
use std::io::Result;
|
||||
fn main() -> Result<()> {
|
||||
prost_build::compile_protos(&["src/monado_metrics.proto"], &["src/"])?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package monado_metrics;
|
||||
|
||||
|
||||
message Version
|
||||
{
|
||||
uint32 major = 1;
|
||||
uint32 minor = 2;
|
||||
}
|
||||
|
||||
message SessionFrame
|
||||
{
|
||||
//! Which session this frame belongs to.
|
||||
int64 session_id = 1;
|
||||
|
||||
//! ID of frame.
|
||||
int64 frame_id = 2;
|
||||
|
||||
//! How long we thought the frame would take.
|
||||
uint64 predicted_frame_time_ns = 3;
|
||||
|
||||
//! When we predicted the app should wake up.
|
||||
uint64 predicted_wake_up_time_ns = 4;
|
||||
|
||||
//! When the client's GPU work should have completed.
|
||||
uint64 predicted_gpu_done_time_ns = 5;
|
||||
|
||||
//! When we predicted this frame to be shown.
|
||||
uint64 predicted_display_time_ns = 6;
|
||||
|
||||
//! The selected display period.
|
||||
uint64 predicted_display_period_ns = 7;
|
||||
|
||||
/*!
|
||||
* When the app told us to display this frame, can be different
|
||||
* then the predicted display time so we track that separately.
|
||||
*/
|
||||
uint64 display_time_ns = 8;
|
||||
|
||||
//! When the predict call was made (inside of xrWaitFrame).
|
||||
uint64 when_predicted_ns = 9;
|
||||
|
||||
//! When the waiting thread was woken up.
|
||||
uint64 when_wait_woke_ns = 10;
|
||||
|
||||
//! When xrBeginFrame was called.
|
||||
uint64 when_begin_ns = 11;
|
||||
|
||||
//! When the layer information was delivered, (inside of xrEndFrame).
|
||||
uint64 when_delivered_ns = 12;
|
||||
|
||||
//! When the scheduled GPU work was completed.
|
||||
uint64 when_gpu_done_ns = 13;
|
||||
|
||||
//! Was this frame discarded.
|
||||
bool discarded = 14;
|
||||
}
|
||||
|
||||
message Used
|
||||
{
|
||||
//! Owning session of the frame.
|
||||
int64 session_id = 1;
|
||||
|
||||
//! Which session frame was used by the compositor.
|
||||
int64 session_frame_id = 2;
|
||||
|
||||
//! The system frame that the session was used by.
|
||||
int64 system_frame_id = 3;
|
||||
|
||||
//! When the frame was latched.
|
||||
uint64 when_ns = 4;
|
||||
}
|
||||
|
||||
message SystemFrame
|
||||
{
|
||||
//! ID of frame.
|
||||
int64 frame_id = 1;
|
||||
|
||||
//! Projected pixels to photon time.
|
||||
uint64 predicted_display_time_ns = 2;
|
||||
|
||||
//! Current period of displaying of frames.
|
||||
uint64 predicted_display_period_ns = 3;
|
||||
|
||||
//! When the compositor should hand pixels to display hardware.
|
||||
uint64 desired_present_time_ns = 4;
|
||||
|
||||
//! The time that the compositor should wake up.
|
||||
uint64 wake_up_time_ns = 5;
|
||||
uint64 present_slop_ns = 6;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Out of band info about a GPU timing from a system compositor.
|
||||
*/
|
||||
message SystemGpuInfo
|
||||
{
|
||||
//! ID of frame.
|
||||
int64 frame_id = 1;
|
||||
|
||||
//! Start of GPU work.
|
||||
uint64 gpu_start_ns = 2;
|
||||
|
||||
//! End of GPU work.
|
||||
uint64 gpu_end_ns = 3;
|
||||
|
||||
//! When the information was gathered.
|
||||
uint64 when_ns = 4;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Information that comes from the vulkan present timing.
|
||||
*/
|
||||
message SystemPresentInfo
|
||||
{
|
||||
//! ID of frame.
|
||||
int64 frame_id = 1;
|
||||
|
||||
//! The total expected time for the compositor to complete a frame.
|
||||
uint64 expected_comp_time_ns = 2;
|
||||
|
||||
//! The time we predicted the compositor to wake up time.
|
||||
uint64 predicted_wake_up_time_ns = 3;
|
||||
|
||||
//! Predicted time for completion of the GPU work.
|
||||
uint64 predicted_done_time_ns = 4;
|
||||
|
||||
//! Predicted display time.
|
||||
uint64 predicted_display_time_ns = 5;
|
||||
|
||||
//! When was this frame predicted.
|
||||
uint64 when_predict_ns = 6;
|
||||
|
||||
//! When we last woke up the compositor after its equivalent of wait_frame.
|
||||
uint64 when_woke_ns = 7;
|
||||
|
||||
//! When the compositor started rendering a frame
|
||||
uint64 when_began_ns = 8;
|
||||
|
||||
//! When the compositor finished rendering a frame
|
||||
uint64 when_submitted_ns = 9;
|
||||
|
||||
//! When new frame timing info was last added.
|
||||
uint64 when_infoed_ns = 10;
|
||||
|
||||
//! When we wanted to start presenting.
|
||||
uint64 desired_present_time_ns = 11;
|
||||
|
||||
//! The slop used for this frame.
|
||||
uint64 present_slop_ns = 12;
|
||||
|
||||
//! Margin of GPU to present time.
|
||||
uint64 present_margin_ns = 13;
|
||||
|
||||
//! When the present time actually happened.
|
||||
uint64 actual_present_time_ns = 14;
|
||||
|
||||
//! The earliest we could have presented.
|
||||
uint64 earliest_present_time_ns = 15;
|
||||
}
|
||||
|
||||
|
||||
message Record
|
||||
{
|
||||
oneof record {
|
||||
Version version = 1;
|
||||
SessionFrame session_frame = 2;
|
||||
Used used = 3;
|
||||
SystemFrame system_frame = 4;
|
||||
SystemGpuInfo system_gpu_info = 5;
|
||||
SystemPresentInfo system_present_info = 6;
|
||||
};
|
||||
}
|
||||
|
|
@ -2,8 +2,4 @@
|
|||
|
||||
This repository contains the IPC specification and client implementation for the WayVR protocol. The primary purpose is to enable communication between applications and the WayVR server, offering a range of API functions.
|
||||
|
||||
[WayVR Server README](https://github.com/wlx-team/wayvr/tree/main/wayvr/contrib/wayvr)
|
||||
|
||||
## Usage
|
||||
|
||||
[Example WayVR IPC Client usage in WayVR Dashboard (Legacy)](https://github.com/olekolek1000/wayvr-dashboard/blob/master/src-tauri/src/frontend_ipc.rs)
|
||||
[Wayvrctl Documentation](https://wayvr.org/docs/various/integrations/wayvr-ipc/)
|
||||
|
|
|
|||
|
|
@ -223,13 +223,12 @@ impl WayVRClient {
|
|||
);
|
||||
}
|
||||
|
||||
if let PacketServer::WvrStateChanged(_) = &packet {
|
||||
if let Some(on_signal) = &mut client.on_signal {
|
||||
if (*on_signal)(&packet) {
|
||||
// Signal consumed
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if let PacketServer::WvrStateChanged(_) = &packet
|
||||
&& let Some(on_signal) = &mut client.on_signal
|
||||
&& (*on_signal)(&packet)
|
||||
{
|
||||
// Signal consumed
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// queue packet to read if it contains a serial response
|
||||
|
|
@ -415,7 +414,10 @@ impl WayVRClient {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fn_wlx_switch_set(client: WayVRClientMutex, set: Option<usize>) -> anyhow::Result<()> {
|
||||
pub async fn fn_wlx_switch_set(
|
||||
client: WayVRClientMutex,
|
||||
set: Option<usize>,
|
||||
) -> anyhow::Result<()> {
|
||||
send_only!(client, &PacketClient::WlxSwitchSet(set));
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ macro_rules! gen_id {
|
|||
//ThingVec
|
||||
#[allow(dead_code)]
|
||||
impl $container_name {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vec: Vec::new(),
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ repository = "https://github.com/wlx-team/wayvr"
|
|||
keywords = [
|
||||
"linux",
|
||||
"openvr",
|
||||
"openxr",
|
||||
"x11",
|
||||
"wayland",
|
||||
"openvr-overlay",
|
||||
"openxr",
|
||||
"openxr-overlay",
|
||||
"wayland",
|
||||
"x11",
|
||||
]
|
||||
categories = ["games"]
|
||||
|
||||
|
|
@ -32,20 +32,19 @@ glam = { workspace = true, features = ["mint", "serde"] }
|
|||
idmap = { workspace = true, features = ["serde"] }
|
||||
idmap-derive.workspace = true
|
||||
log.workspace = true
|
||||
rust-embed.workspace = true
|
||||
regex.workspace = true
|
||||
slotmap.workspace = true
|
||||
rust-embed.workspace = true
|
||||
serde = { workspace = true, features = ["rc"] }
|
||||
serde_json.workspace = true
|
||||
slotmap.workspace = true
|
||||
strum.workspace = true
|
||||
vulkano.workspace = true
|
||||
vulkano-shaders.workspace = true
|
||||
xdg.workspace = true
|
||||
|
||||
ash = "^0.38.0" # must match vulkano
|
||||
ash = "^0.38.0" # must match vulkano
|
||||
bytes = { version = "1.11.1" }
|
||||
chrono = { version = "0.4.42", features = [ "unstable-locales"]}
|
||||
pure-rust-locales = "0.8.2"
|
||||
chrono = { version = "0.4.42", features = ["unstable-locales"] }
|
||||
chrono-tz = "0.10.4"
|
||||
config = "0.15.19"
|
||||
dbus = { version = "0.9.9" }
|
||||
|
|
@ -58,14 +57,16 @@ interprocess = { version = "2.2.3" }
|
|||
json = { version = "0.12.4", optional = true }
|
||||
json5 = "1.3.0"
|
||||
libc = "0.2.178"
|
||||
libmonado = { version = "1.6.0", optional = true }
|
||||
libmonado = { git = "https://github.com/oo8dev/libmonado-rs.git", rev = "fc39940a64dea2df080a0d2c974c7d651006241f", optional = true }
|
||||
log-panics = { version = "2.1.0", features = ["with-backtrace"] }
|
||||
mint = "0.5.9"
|
||||
openxr = { version = "0.21.0", features = ["linked", "mint"], optional = true }
|
||||
ovr_overlay = { features = [
|
||||
ovr_overlay = { git = "https://github.com/galister/ovr_overlay_oyasumi", rev = "e477bd2a9e04293ea68c1e7529ef2cb131f32acc", features = [
|
||||
"ovr_input",
|
||||
"ovr_system",
|
||||
], git = "https://github.com/galister/ovr_overlay_oyasumi", rev = "e477bd2a9e04293ea68c1e7529ef2cb131f32acc", optional = true }
|
||||
], optional = true }
|
||||
prost = { version = "0.14.3", optional = true }
|
||||
pure-rust-locales = "0.8.2"
|
||||
rosc = { version = "0.11.4", optional = true }
|
||||
serde_json5 = "0.2.1"
|
||||
serde_yaml = "0.9.34"
|
||||
|
|
@ -74,36 +75,36 @@ smallvec = "1.15.1"
|
|||
smithay = { version = "0.7.0", default-features = false, features = [
|
||||
"backend_vulkan",
|
||||
"desktop",
|
||||
"xwayland",
|
||||
"wayland_frontend",
|
||||
"xwayland",
|
||||
] }
|
||||
smol = { workspace = true }
|
||||
sysinfo = { version = "0.37" }
|
||||
thiserror = "2.0"
|
||||
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
||||
tracing = "0.1.43"
|
||||
uuid = { version = "1.19.0", features = ["v4", "fast-rng"] }
|
||||
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
||||
uuid = { version = "1.19.0", features = ["fast-rng", "v4"] }
|
||||
wayland-client = { workspace = true }
|
||||
winit = { version = "0.30.12", optional = true }
|
||||
xcb = { version = "1.6.0", optional = true, features = [
|
||||
xcb = { version = "1.6.0", features = [
|
||||
"as-raw-xcb-connection",
|
||||
] }
|
||||
xkbcommon = { version = "0.8.0" } # 0.9.0 breaks keymap import on some distros
|
||||
codes-iso-639 = "0.1.5"
|
||||
], optional = true }
|
||||
xkbcommon = { version = "0.8.0" } # 0.9.0 breaks keymap import on some distros
|
||||
arboard = { version="3.6.1", features = ["wayland-data-control", "wl-clipboard-rs"] }
|
||||
super-swipe-type = "0.2.2"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
regex.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["openvr", "openxr", "osc", "x11", "wayland"]
|
||||
openvr = ["dep:ovr_overlay", "dep:json"]
|
||||
openxr = ["dep:openxr", "dep:libmonado"]
|
||||
default = ["openvr", "openxr", "osc", "wayland", "x11"]
|
||||
as-raw-xcb-connection = []
|
||||
feat-monado-metrics = []
|
||||
openvr = ["dep:json", "dep:ovr_overlay"]
|
||||
openxr = ["dep:libmonado", "dep:openxr", "dep:prost"]
|
||||
osc = ["dep:rosc"]
|
||||
x11 = ["dep:xcb", "wlx-capture/xshm", "xkbcommon/x11"]
|
||||
wayland = ["pipewire", "wlx-capture/wlr", "xkbcommon/wayland"]
|
||||
pipewire = ["wlx-capture/pipewire"]
|
||||
uidev = ["dep:winit"]
|
||||
wayland = ["pipewire", "wlx-capture/wlr", "xkbcommon/wayland"]
|
||||
x11 = ["dep:xcb", "wlx-capture/xshm", "xkbcommon/x11"]
|
||||
xcb = ["dep:xcb"]
|
||||
as-raw-xcb-connection = []
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/galister/wlx-overlay-s/refs/heads/guide/wayvr/logo.svg" height="120"/>
|
||||
</p>
|
||||
|
||||
# Overview
|
||||
|
||||
### Features
|
||||
|
||||
- Display Wayland applications without GPU overhead (zero-copy via dma-buf)
|
||||
- Mouse and keyboard input, with precision scrolling support
|
||||
- Tested on AMD and Nvidia
|
||||
|
||||
### Supported software
|
||||
|
||||
- Basically all Qt and GTK applications (they work out of the box)
|
||||
- Most XWayland applications via `cage`
|
||||
|
||||
### Launching external apps inside WayVR
|
||||
|
||||
To launch your app externally:
|
||||
|
||||
```sh
|
||||
DISPLAY= WAYLAND_DISPLAY=wayland-$(cat $XDG_RUNTIME_DIR/wayvr.disp) yourapp
|
||||
```
|
||||
|
||||
or (in the most cases):
|
||||
|
||||
```
|
||||
DISPLAY= WAYLAND_DISPLAY=wayland-20 yourapp
|
||||
```
|
||||
|
||||
Setting `DISPLAY` to an empty string forces various apps to use Wayland instead of X11.
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
### My application doesn't launch but others do!
|
||||
|
||||
Even though some applications support Wayland, some still check for the `DISPLAY` environment variable and an available X11 server, throwing an error. This can also be fixed by running `cage` on top of them.
|
||||
|
||||
### Floating windows
|
||||
|
||||
Context menus are not functional in most cases yet, including drag & drop support.
|
||||
|
|
@ -109,9 +109,10 @@
|
|||
"LAST_EXISTING_SET": "Dies ist das letzte vorhandene Set.",
|
||||
"EMPTY_SET": "Leeres Set!",
|
||||
"LETS_ADD_OVERLAYS": "Lass uns ein paar Overlays von der Uhr hinzufügen!",
|
||||
"FIXING_FLOOR": "Boden wird in 5 Sekunden fixiert...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "Lege einen Controller auf den Boden!",
|
||||
"CANNOT_ADD_SET": "Satz kann nicht hinzugefügt werden!",
|
||||
"MAXIMUM_SETS_REACHED": "Maximale Anzahl an Sets erreicht."
|
||||
}
|
||||
"MAXIMUM_SETS_REACHED": "Maximale Anzahl an Sets erreicht.",
|
||||
"FIXING_FLOOR_IN_X_SECS": "Boden wird in {SECONDS} Sekunde(n) repariert..."
|
||||
},
|
||||
"DONE": "Erledigt"
|
||||
}
|
||||
|
|
@ -4,41 +4,56 @@
|
|||
},
|
||||
"BAR": {
|
||||
"ADD_MIRROR": "Add a new mirror overlay",
|
||||
"EDIT_MODE_TOGGLE": "Toggle edit mode",
|
||||
"ADD_NEW_SET": "Add new set",
|
||||
"DELETE_CURRENT_SET": "Delete current set",
|
||||
"TOGGLE_VISIBILITY": "Toggle visibility",
|
||||
"RESET_POSITION": "Reset position",
|
||||
"RELOAD_FROM_DISK": "Reload XML from disk",
|
||||
"CLOSE_MIRROR": "Close mirror",
|
||||
"CLOSE_APP": "Close app",
|
||||
"CLOSE_MIRROR": "Close mirror",
|
||||
"DELETE_CURRENT_SET": "Delete current set",
|
||||
"EDIT_MODE_TOGGLE": "Toggle edit mode",
|
||||
"FORCE_CLOSE_APP": "Force close app",
|
||||
"HANDSFREE": {
|
||||
"TITLE": "Handsfree mode",
|
||||
"NONE": "Off",
|
||||
"HMD": "HMD + pinch",
|
||||
"EYE_ONLY": "Eye only",
|
||||
"EYE_TRACKING": "Eye + pinch",
|
||||
"HMD": "HMD + pinch",
|
||||
"HMD_ONLY": "HMD only",
|
||||
"EYE_ONLY": "Eye only"
|
||||
}
|
||||
"NONE": "Off",
|
||||
"TITLE": "Handsfree mode"
|
||||
},
|
||||
"RELOAD_FROM_DISK": "Reload XML from disk",
|
||||
"RESET_POSITION": "Reset position",
|
||||
"TOGGLE_VISIBILITY": "Toggle visibility"
|
||||
},
|
||||
"DEFAULT": "Default",
|
||||
"DISABLED": "Disabled",
|
||||
"DONE": "Done",
|
||||
"EDIT_MODE": {
|
||||
"ADJUST_CURVATURE": "Adjust curvature",
|
||||
"ALIGN_TO_HMD": "Align to HMD",
|
||||
"ALPHA_BLEND_MODE": "Alpha blend mode",
|
||||
"BLENDING_ADDITIVE": "Additive blending",
|
||||
"ANGLE_FADE": "Angle fade",
|
||||
"ANGLE_FADE_HELP": "Fade when not facing HMD",
|
||||
"BLENDING_ADDITIVE": "Additive blending",
|
||||
"BLOCK_INPUT": "Block game input",
|
||||
"CURVATURE": "Curvature",
|
||||
"DELETE": "Long press to remove from current set",
|
||||
"DISABLE_GRAB": "Disable grab",
|
||||
"GLOBAL": "Always visible",
|
||||
"HINT_POINT_WINDOW": "Point at a window to change its parameters.\nOnce done, leave edit mode using the button on the right.",
|
||||
"INTERPOLATION": "Interpolation",
|
||||
"ALIGN_TO_HMD": "Align to HMD",
|
||||
"KEYBOARD": "Keyboard",
|
||||
"LEAVE": "Leave edit mode",
|
||||
"LOCK_INTERACTION": "Lock interaction",
|
||||
"MOUSE": {
|
||||
"FLIP180": "Flipped 180°",
|
||||
"FLIP270": "Flipped 270°",
|
||||
"FLIP90": "Flipped 90°",
|
||||
"FLIPPED": "Flipped",
|
||||
"NORMAL": "Normal",
|
||||
"ROTATE180": "Rotated 180°",
|
||||
"ROTATE270": "Rotated 270°",
|
||||
"ROTATE90": "Rotated 90°",
|
||||
"TITLE": "Mouse fixes",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "If the cursor moves on a completely different screen,\nthe screens were likely selected wrong. See readme."
|
||||
},
|
||||
"MOVE_PRESS_AND_DRAG": "Move (press & drag)",
|
||||
"OPACITY": "Opacity",
|
||||
"POS_ANCHORED": "Anchored: Moves with center marker. Default.",
|
||||
|
|
@ -48,56 +63,42 @@
|
|||
"POS_HMD": "Follow the HMD.",
|
||||
"POS_STATIC": "Static: Not part of any set, no recenter.",
|
||||
"POSITIONING": "Positioning",
|
||||
"GLOBAL": "Always visible",
|
||||
"BLOCK_INPUT": "Block game input",
|
||||
"RESIZE_PRESS_AND_DRAG": "Resize (press & drag)",
|
||||
"STEREO_3D_MODE": {
|
||||
"ADJUST_MOUSE": "Adjust mouse",
|
||||
"FULL_FRAME": "Full 3D",
|
||||
"FULL_FRAME_BAT": "Full-BAT",
|
||||
"FULL_FRAME_SBS": "Full-SBS",
|
||||
"FULL_FRAME_TAB": "Full-TAB",
|
||||
"SPLIT_BOTTOM_TOP": "BOTTOM→TOP",
|
||||
"SPLIT_LEFT_RIGHT": "LEFT→RIGHT",
|
||||
"SPLIT_RIGHT_LEFT": "RIGHT→LEFT",
|
||||
"SPLIT_TOP_BOTTOM": "TOP→BOTTOM",
|
||||
"TITLE": "3D Stereo Mode",
|
||||
"FULL_FRAME": "Full 3D",
|
||||
"FULL_FRAME_SBS": "Full-SBS",
|
||||
"FULL_FRAME_TAB": "Full-TAB",
|
||||
"FULL_FRAME_BAT": "Full-BAT"
|
||||
},
|
||||
"MOUSE": {
|
||||
"TITLE": "Mouse fixes",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "If the cursor moves on a completely different screen,\nthe screens were likely selected wrong. See readme.",
|
||||
"NORMAL": "Normal",
|
||||
"ROTATE90": "Rotated 90°",
|
||||
"ROTATE180": "Rotated 180°",
|
||||
"ROTATE270": "Rotated 270°",
|
||||
"FLIPPED": "Flipped",
|
||||
"FLIP90": "Flipped 90°",
|
||||
"FLIP180": "Flipped 180°",
|
||||
"FLIP270": "Flipped 270°"
|
||||
"TITLE": "3D Stereo Mode"
|
||||
}
|
||||
},
|
||||
"GRAB": {
|
||||
"ADJUST_DISTANCE": "Adjust distance",
|
||||
"ADJUST_SIZE": "Adjust size",
|
||||
"UNRESTRICTED_MOVEMENT": "Unrestricted movement",
|
||||
"GRABBING_WATCH": "To swap hands, move the watch in front and grab it with the other hand.",
|
||||
"GRABBING_STATIC": "This overlay is Static and will stay in place, ignoring recenter.",
|
||||
"GRABBING_ANCHORED": "Anchored overlays all move together. Separate a single window by grabbing it with the other hand while still grabbing the anchor.",
|
||||
"GRABBING_ANCHORED_EDIT": "This overlay will stay anchored to the center marker.",
|
||||
"GRABBING_FLOATING": "This overlay is Floating and will stay in place, unless recentered.",
|
||||
"GRABBING_FOLLOW": "This overlay will follow the device it is attached to."
|
||||
"GRABBING_FOLLOW": "This overlay will follow the device it is attached to.",
|
||||
"GRABBING_STATIC": "This overlay is Static and will stay in place, ignoring recenter.",
|
||||
"GRABBING_WATCH": "To swap hands, move the watch in front and grab it with the other hand.",
|
||||
"UNRESTRICTED_MOVEMENT": "Unrestricted movement"
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "Notification",
|
||||
"ERROR": "Error",
|
||||
"CANNOT_ADD_SET": "Cannot add set!",
|
||||
"MAXIMUM_SETS_REACHED": "Maximum number of sets reached.",
|
||||
"CANNOT_REMOVE_SET": "Cannot remove set!",
|
||||
"NO_SET_SELECTED": "No set is selected.",
|
||||
"LAST_EXISTING_SET": "This is the last existing set.",
|
||||
"DEFAULT_TITLE": "Notification",
|
||||
"EMPTY_SET": "Empty set!",
|
||||
"ERROR": "Error",
|
||||
"FIXING_FLOOR_IN_X_SECS": "Fixing floor in {SECONDS} second(s)...",
|
||||
"LAST_EXISTING_SET": "This is the last existing set.",
|
||||
"LETS_ADD_OVERLAYS": "Let's add some overlays from the watch!",
|
||||
"FIXING_FLOOR": "Fixing floor in 5 seconds...",
|
||||
"MAXIMUM_SETS_REACHED": "Maximum number of sets reached.",
|
||||
"NO_SET_SELECTED": "No set is selected.",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "Place one controller on the floor!"
|
||||
},
|
||||
"WATCH": {
|
||||
|
|
|
|||
|
|
@ -109,9 +109,10 @@
|
|||
"LAST_EXISTING_SET": "Este es el último conjunto existente.",
|
||||
"EMPTY_SET": "¡Conjunto vacío!",
|
||||
"LETS_ADD_OVERLAYS": "¡Añadamos algunos overlays desde el reloj!",
|
||||
"FIXING_FLOOR": "Fijando el suelo en 5 segundos...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "¡Coloca un mando en el suelo!",
|
||||
"CANNOT_ADD_SET": "¡No se puede agregar el conjunto!",
|
||||
"MAXIMUM_SETS_REACHED": "Se ha alcanzado el número máximo de sets."
|
||||
}
|
||||
"MAXIMUM_SETS_REACHED": "Se ha alcanzado el número máximo de sets.",
|
||||
"FIXING_FLOOR_IN_X_SECS": "Corrigiendo el suelo en {SECONDS} segundo(s)..."
|
||||
},
|
||||
"DONE": "Hecho"
|
||||
}
|
||||
|
|
@ -97,8 +97,8 @@
|
|||
"LAST_EXISTING_SET": "Questo è l'ultimo set esistente.",
|
||||
"EMPTY_SET": "Set vuoto!",
|
||||
"LETS_ADD_OVERLAYS": "Aggiungiamo degli overlay dal Watch!",
|
||||
"FIXING_FLOOR": "Rifissaggio del pavimento tra 5 secondi...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "Posiziona un controller a terra!"
|
||||
"ONE_CONTROLLER_ON_FLOOR": "Posiziona un controller a terra!",
|
||||
"FIXING_FLOOR_IN_X_SECS": "Correzione del pavimento in {SECONDS} secondo/i..."
|
||||
},
|
||||
"WATCH": {
|
||||
"ADD_NEW_SET": "Aggiungi un nuovo set",
|
||||
|
|
@ -111,5 +111,6 @@
|
|||
"RECENTER": "Recentra l'area di gioco",
|
||||
"SWITCH_TO_SET": "Passa a set",
|
||||
"TOGGLE_FOR_CURRENT_SET": "Attiva/disattiva per il set corrente"
|
||||
}
|
||||
},
|
||||
"DONE": "Fatto"
|
||||
}
|
||||
|
|
@ -107,9 +107,10 @@
|
|||
"LAST_EXISTING_SET": "これが最後の設定です。",
|
||||
"EMPTY_SET": "空のセットです!",
|
||||
"LETS_ADD_OVERLAYS": "ウォッチからオーバーレイを追加しましょう!",
|
||||
"FIXING_FLOOR": "5秒後にフロアを固定します...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "コントローラーを床に置いてください!",
|
||||
"CANNOT_ADD_SET": "セットを追加できません!",
|
||||
"MAXIMUM_SETS_REACHED": "最大セット数に達しました。"
|
||||
}
|
||||
"MAXIMUM_SETS_REACHED": "最大セット数に達しました。",
|
||||
"FIXING_FLOOR_IN_X_SECS": "{SECONDS}秒で床を固定中..."
|
||||
},
|
||||
"DONE": "完了"
|
||||
}
|
||||
|
|
@ -107,9 +107,10 @@
|
|||
"LAST_EXISTING_SET": "To jest ostatni istniejący zestaw.",
|
||||
"EMPTY_SET": "Pusty zestaw!",
|
||||
"LETS_ADD_OVERLAYS": "Dodajmy kilka nakładek z zegarka!",
|
||||
"FIXING_FLOOR": "Naprawianie podłogi za 5 sekund...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "Umieść jeden kontroler na podłodze!",
|
||||
"CANNOT_ADD_SET": "Nie można dodać zestawu!",
|
||||
"MAXIMUM_SETS_REACHED": "Osiągnięto maksymalną liczbę zestawów."
|
||||
}
|
||||
"MAXIMUM_SETS_REACHED": "Osiągnięto maksymalną liczbę zestawów.",
|
||||
"FIXING_FLOOR_IN_X_SECS": "Naprawianie podłogi za {SECONDS} sekund(y)..."
|
||||
},
|
||||
"DONE": "Gotowe"
|
||||
}
|
||||
|
|
@ -97,8 +97,8 @@
|
|||
"LAST_EXISTING_SET": "这是最后一个现有的集合。",
|
||||
"EMPTY_SET": "空集合!",
|
||||
"LETS_ADD_OVERLAYS": "让我们从手表添加一些覆盖层吧!",
|
||||
"FIXING_FLOOR": "将在 5 秒内修复地面...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "请将一个控制器放在地面上!"
|
||||
"ONE_CONTROLLER_ON_FLOOR": "请将一个控制器放在地面上!",
|
||||
"FIXING_FLOOR_IN_X_SECS": "{SECONDS}秒内固定地面..."
|
||||
},
|
||||
"WATCH": {
|
||||
"ADD_NEW_SET": "添加新集合",
|
||||
|
|
@ -111,5 +111,6 @@
|
|||
"RECENTER": "重置游玩区中心 (Recenter)",
|
||||
"SWITCH_TO_SET": "切换到集合",
|
||||
"TOGGLE_FOR_CURRENT_SET": "切换当前集合"
|
||||
}
|
||||
},
|
||||
"DONE": "完成"
|
||||
}
|
||||
|
|
@ -208,8 +208,6 @@ pub struct InteractionState {
|
|||
pub grabbed: Option<GrabData>,
|
||||
pub clicked_id: Option<OverlayID>,
|
||||
pub hovered_id: Option<OverlayID>,
|
||||
pub next_push: Instant,
|
||||
pub haptics: Option<f32>,
|
||||
pub should_block_input: bool,
|
||||
pub should_block_poses: bool,
|
||||
}
|
||||
|
|
@ -221,8 +219,6 @@ impl Default for InteractionState {
|
|||
grabbed: None,
|
||||
clicked_id: None,
|
||||
hovered_id: None,
|
||||
next_push: Instant::now(),
|
||||
haptics: None,
|
||||
should_block_input: false,
|
||||
should_block_poses: false,
|
||||
}
|
||||
|
|
@ -292,7 +288,6 @@ pub struct PointerHit {
|
|||
pub mode: PointerMode,
|
||||
pub primary: bool,
|
||||
pub uv: Vec2,
|
||||
pub dist: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
@ -699,7 +694,6 @@ where
|
|||
mode,
|
||||
primary: false,
|
||||
uv,
|
||||
dist: hit.dist,
|
||||
};
|
||||
|
||||
let result = overlay.config.backend.on_hover(app, &pointer_hit);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ pub enum BackendError {
|
|||
#[error("OpenXR Error: {0:?}")]
|
||||
OpenXrError(#[from] ::openxr::sys::Result),
|
||||
#[error("Shutdown")]
|
||||
#[allow(dead_code)]
|
||||
Shutdown,
|
||||
#[allow(dead_code)]
|
||||
#[error("Restart")]
|
||||
Restart,
|
||||
#[error("Fatal: {0:?}")]
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ const PATH_SCROLL: &str = "/actions/default/in/Scroll";
|
|||
const PATH_SHOW_HIDE: &str = "/actions/default/in/ShowHide";
|
||||
const PATH_SPACE_DRAG: &str = "/actions/default/in/SpaceDrag";
|
||||
const PATH_SPACE_ROTATE: &str = "/actions/default/in/SpaceRotate";
|
||||
const PATH_SPACE_RESET: &str = "/actions/default/in/SpaceReset";
|
||||
const PATH_TOGGLE_DASHBOARD: &str = "/actions/default/in/ToggleDashboard";
|
||||
|
||||
const INPUT_ANY: InputValueHandle = InputValueHandle(ovr_overlay::sys::k_ulInvalidInputValueHandle);
|
||||
|
|
@ -55,6 +56,7 @@ pub(super) struct OpenVrInputSource {
|
|||
toggle_dashboard_hnd: ActionHandle,
|
||||
space_drag_hnd: ActionHandle,
|
||||
space_rotate_hnd: ActionHandle,
|
||||
space_reset_hnd: ActionHandle,
|
||||
click_modifier_right_hnd: ActionHandle,
|
||||
click_modifier_middle_hnd: ActionHandle,
|
||||
move_mouse_hnd: ActionHandle,
|
||||
|
|
@ -80,6 +82,7 @@ impl OpenVrInputSource {
|
|||
let toggle_dashboard_hnd = input.get_action_handle(PATH_TOGGLE_DASHBOARD)?;
|
||||
let space_drag_hnd = input.get_action_handle(PATH_SPACE_DRAG)?;
|
||||
let space_rotate_hnd = input.get_action_handle(PATH_SPACE_ROTATE)?;
|
||||
let space_reset_hnd = input.get_action_handle(PATH_SPACE_RESET)?;
|
||||
let click_modifier_right_hnd = input.get_action_handle(PATH_CLICK_MODIFIER_RIGHT)?;
|
||||
let click_modifier_middle_hnd = input.get_action_handle(PATH_CLICK_MODIFIER_MIDDLE)?;
|
||||
let move_mouse_hnd = input.get_action_handle(PATH_MOVE_MOUSE)?;
|
||||
|
|
@ -118,6 +121,7 @@ impl OpenVrInputSource {
|
|||
toggle_dashboard_hnd,
|
||||
space_drag_hnd,
|
||||
space_rotate_hnd,
|
||||
space_reset_hnd,
|
||||
click_modifier_right_hnd,
|
||||
click_modifier_middle_hnd,
|
||||
move_mouse_hnd,
|
||||
|
|
@ -158,7 +162,7 @@ impl OpenVrInputSource {
|
|||
// global input from overlays is enabled in SteamVR developer settings
|
||||
// (taken from https://github.com/ValveSoftware/openvr/issues/1236)
|
||||
nPriority: if should_block_input_left {
|
||||
0x01000000
|
||||
0x0100_0000
|
||||
} else {
|
||||
0x0
|
||||
},
|
||||
|
|
@ -170,7 +174,7 @@ impl OpenVrInputSource {
|
|||
ulSecondaryActionSet: 0,
|
||||
unPadding: 0,
|
||||
nPriority: if should_block_input_right {
|
||||
0x01000000
|
||||
0x0100_0000
|
||||
} else {
|
||||
0x0
|
||||
},
|
||||
|
|
@ -243,6 +247,11 @@ impl OpenVrInputSource {
|
|||
.map(|x| x.0.bState)
|
||||
.unwrap_or(false);
|
||||
|
||||
app_hand.now.space_reset = input
|
||||
.get_digital_action_data(self.space_reset_hnd, hand.input_hnd)
|
||||
.map(|x| x.0.bState)
|
||||
.unwrap_or(false);
|
||||
|
||||
app_hand.now.click_modifier_right = input
|
||||
.get_digital_action_data(self.click_modifier_right_hnd, hand.input_hnd)
|
||||
.map(|x| x.0.bState)
|
||||
|
|
|
|||
|
|
@ -83,9 +83,6 @@ impl PlayspaceMover {
|
|||
let offset = (space_transform.transform_vector3a(app.input_state.hmd.translation)
|
||||
- app.input_state.hmd.translation)
|
||||
* -1.0;
|
||||
let mut overlay_transform = Affine3A::from_rotation_y(-rel_y);
|
||||
|
||||
overlay_transform.translation = offset;
|
||||
space_transform.translation = offset;
|
||||
|
||||
data.pose *= space_transform;
|
||||
|
|
@ -178,6 +175,14 @@ impl PlayspaceMover {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for pointer in &app.input_state.pointers {
|
||||
if pointer.now.space_reset && !pointer.before.space_reset {
|
||||
self.reset_offset(chaperone_mgr, &app.input_state);
|
||||
log::info!("Space reset");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_offset(&mut self, chaperone_mgr: &mut ChaperoneSetupManager, input: &InputState) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl InputBlocker {
|
|||
}
|
||||
|
||||
pub fn update(&mut self, app: &mut AppState) {
|
||||
let Some(monado) = &mut app.monado else {
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return; // monado not available
|
||||
};
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ impl InputBlocker {
|
|||
} else {
|
||||
trace!("Unblocking input");
|
||||
}
|
||||
self.block_inputs(monado, should_block_inputs, should_block_poses);
|
||||
self.block_inputs(&mut monado.ipc, should_block_inputs, should_block_poses);
|
||||
}
|
||||
|
||||
self.inputs_blocked_last_frame = should_block_inputs;
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ use xr::OverlaySessionCreateFlagsEXTX;
|
|||
|
||||
macro_rules! next_chain_insert {
|
||||
($layer:expr, $payload:expr) => {{
|
||||
let payload_ptr = $payload.as_mut() as *mut _ as *mut xr::sys::BaseInStructure;
|
||||
let payload_ptr = std::ptr::from_mut($payload.as_mut()).cast::<xr::sys::BaseInStructure>();
|
||||
let new_elem = payload_ptr.as_mut().unwrap();
|
||||
let mut raw = $layer.into_raw();
|
||||
new_elem.next = raw.next as _;
|
||||
new_elem.next = raw.next.cast();
|
||||
raw.next = payload_ptr as *const _;
|
||||
raw
|
||||
}};
|
||||
|
|
@ -70,7 +70,7 @@ pub(super) fn init_xr() -> Result<(xr::Instance, xr::SystemId), anyhow::Error> {
|
|||
log::warn!("Missing XR_KHR_composition_layer_color_scale_bias extension.");
|
||||
}
|
||||
|
||||
let xr_mndx_system_buttons = "XR_MNDX_system_buttons".as_bytes().to_vec();
|
||||
let xr_mndx_system_buttons = b"XR_MNDX_system_buttons".to_vec();
|
||||
if available_extensions.other.contains(&xr_mndx_system_buttons) {
|
||||
enabled_extensions.other.push(xr_mndx_system_buttons);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ impl OpenXrInputSource {
|
|||
}
|
||||
|
||||
pub fn update_devices(app: &mut AppState) -> bool {
|
||||
let Some(monado) = &mut app.monado else {
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return false; // monado not available
|
||||
};
|
||||
|
||||
|
|
@ -297,7 +297,7 @@ impl OpenXrInputSource {
|
|||
let mut seen = Vec::<u32>::with_capacity(32);
|
||||
|
||||
for (mnd_role, wlx_role) in roles {
|
||||
let device = monado.device_from_role(mnd_role);
|
||||
let device = monado.ipc.device_from_role(mnd_role);
|
||||
if let Ok(mut device) = device
|
||||
&& !seen.contains(&device.index)
|
||||
{
|
||||
|
|
@ -305,7 +305,7 @@ impl OpenXrInputSource {
|
|||
Self::update_device_battery_status(&mut device, wlx_role, &mut app.input_state);
|
||||
}
|
||||
}
|
||||
if let Ok(devices) = monado.devices() {
|
||||
if let Ok(devices) = monado.ipc.devices() {
|
||||
for mut device in devices {
|
||||
if !seen.contains(&device.index) {
|
||||
let role = if device.name_id >= 4 && device.name_id <= 8 {
|
||||
|
|
@ -719,7 +719,7 @@ fn suggest_bindings(instance: &xr::Instance, hands: &[&OpenXrHandSource; 3]) {
|
|||
log::debug!(
|
||||
"Bindings for {} bound successfully.",
|
||||
&profile.profile[22..]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ mod blocker;
|
|||
mod helpers;
|
||||
mod input;
|
||||
mod lines;
|
||||
pub mod monado_state;
|
||||
mod overlay;
|
||||
mod playspace;
|
||||
mod skybox;
|
||||
|
|
@ -85,15 +86,18 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||
|
||||
let mut delete_queue = vec![];
|
||||
|
||||
app.monado_init();
|
||||
app.monado_state_init();
|
||||
|
||||
let mut playspace = app.monado.as_mut().and_then(|m| {
|
||||
playspace::PlayspaceMover::new(m)
|
||||
let mut playspace = app.monado_state.as_mut().and_then(|m| {
|
||||
playspace::PlayspaceMover::new(&mut m.ipc)
|
||||
.map_err(|e| log::warn!("Will not use Monado playspace mover: {e}"))
|
||||
.ok()
|
||||
});
|
||||
|
||||
let mut blocker = app.monado.as_ref().map(blocker::InputBlocker::new);
|
||||
let mut blocker = app
|
||||
.monado_state
|
||||
.as_ref()
|
||||
.map(|m| blocker::InputBlocker::new(&m.ipc));
|
||||
|
||||
let (session, mut frame_wait, mut frame_stream) = unsafe {
|
||||
let raw_session = helpers::create_overlay_session(
|
||||
|
|
@ -211,7 +215,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||
}
|
||||
}
|
||||
|
||||
if app.monado.is_some() && next_device_update <= Instant::now() {
|
||||
if app.monado_state.is_some() && next_device_update <= Instant::now() {
|
||||
let changed = OpenXrInputSource::update_devices(&mut app);
|
||||
if changed {
|
||||
overlays.devices_changed(&mut app)?;
|
||||
|
|
@ -370,7 +374,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||
{
|
||||
log::trace!("{}: hidden, skip render", o.config.name);
|
||||
continue;
|
||||
};
|
||||
}
|
||||
|
||||
if !o.data.init {
|
||||
log::trace!("{}: init", o.config.name);
|
||||
|
|
@ -403,6 +407,10 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||
futures.wait()?;
|
||||
// End rendering
|
||||
|
||||
if let Some(monado_state) = &mut app.monado_state {
|
||||
monado_state.update();
|
||||
}
|
||||
|
||||
// Layer composition
|
||||
let mut layers = vec![];
|
||||
if !main_session_visible && let Some(skybox) = skybox.as_mut() {
|
||||
|
|
@ -499,8 +507,8 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||
watch.config.active_state.as_mut().unwrap().transform = watch_transform;
|
||||
} // main_loop
|
||||
|
||||
if let (Some(blocker), Some(monado)) = (blocker, app.monado.as_mut()) {
|
||||
blocker.unblock(monado);
|
||||
if let (Some(blocker), Some(monado)) = (blocker, app.monado_state.as_mut()) {
|
||||
blocker.unblock(&mut monado.ipc);
|
||||
}
|
||||
|
||||
overlays.persist_layout(&mut app);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
#[cfg(feature = "feat-monado-metrics")]
|
||||
use crate::subsystem::monado_metrics::{self, metrics_fd::MonadoMetricsFd};
|
||||
|
||||
pub struct MonadoState {
|
||||
pub ipc: libmonado::Monado,
|
||||
|
||||
#[cfg(feature = "feat-monado-metrics")]
|
||||
pub metrics: Option<MonadoMetricsFd>,
|
||||
}
|
||||
|
||||
impl MonadoState {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
let ipc = libmonado::Monado::auto_connect().map_err(|s| anyhow::anyhow!("{s}"))?;
|
||||
let res = Self {
|
||||
ipc,
|
||||
#[cfg(feature = "feat-monado-metrics")]
|
||||
metrics: None,
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
#[cfg(feature = "feat-monado-metrics")]
|
||||
if let Some(metrics) = &mut self.metrics {
|
||||
metrics.update();
|
||||
|
||||
if metrics.is_full() {
|
||||
let _ = self.set_metrics_enabled(false); // disable metrics if they aren't used
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_metrics_enabled(&mut self, enabled: bool) -> anyhow::Result<()> {
|
||||
#[cfg(feature = "feat-monado-metrics")]
|
||||
{
|
||||
if enabled {
|
||||
if self.metrics.is_none() {
|
||||
log::info!("Starting Monado metrics");
|
||||
self.metrics = Some(monado_metrics::metrics_fd::MonadoMetricsFd::new(
|
||||
&mut self.ipc,
|
||||
)?);
|
||||
}
|
||||
} else {
|
||||
if self.metrics.is_some() {
|
||||
log::info!("Stopping Monado metrics");
|
||||
}
|
||||
self.metrics = None;
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "feat-monado-metrics"))]
|
||||
{
|
||||
#[allow(path_statements)]
|
||||
enabled;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -261,11 +261,11 @@
|
|||
right: "/user/hand/right/input/thumbstick/x"
|
||||
},
|
||||
show_hide: {
|
||||
left: "/user/hand/left/input/menu/click",
|
||||
left: "/user/hand/left/input/system/click",
|
||||
},
|
||||
space_drag: {
|
||||
double_click: true,
|
||||
right: "/user/hand/left/input/menu/click",
|
||||
right: "/user/hand/left/input/system/click",
|
||||
},
|
||||
click_modifier_right: {
|
||||
left: "/user/hand/left/input/trackpad/dpad_up",
|
||||
|
|
@ -305,11 +305,11 @@
|
|||
right: "/user/hand/right/input/thumbstick/x"
|
||||
},
|
||||
show_hide: {
|
||||
left: "/user/hand/left/input/menu/click",
|
||||
left: "/user/hand/left/input/system/click",
|
||||
},
|
||||
space_drag: {
|
||||
double_click: true,
|
||||
right: "/user/hand/left/input/menu/click",
|
||||
right: "/user/hand/left/input/system/click",
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -44,19 +44,19 @@ impl PlayspaceMover {
|
|||
}
|
||||
|
||||
pub fn handle_task(&mut self, app: &mut AppState, task: PlayspaceTask) {
|
||||
let Some(monado) = &mut app.monado else {
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return; // monado not available
|
||||
};
|
||||
|
||||
match task {
|
||||
PlayspaceTask::FixFloor => {
|
||||
self.fix_floor(&app.input_state, monado);
|
||||
self.fix_floor(&app.input_state, &mut monado.ipc);
|
||||
}
|
||||
PlayspaceTask::Reset => {
|
||||
self.reset_offset(monado);
|
||||
self.reset_offset(&mut monado.ipc);
|
||||
}
|
||||
PlayspaceTask::Recenter => {
|
||||
self.recenter(&app.input_state, monado);
|
||||
self.recenter(&app.input_state, &mut monado.ipc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ impl PlayspaceMover {
|
|||
overlays: &mut OverlayWindowManager<OpenXrOverlayData>,
|
||||
app: &mut AppState,
|
||||
) {
|
||||
let Some(monado) = &mut app.monado else {
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return; // monado not available
|
||||
};
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ impl PlayspaceMover {
|
|||
if pointer.now.space_reset {
|
||||
if !pointer.before.space_reset {
|
||||
log::info!("Space reset");
|
||||
self.reset_offset(monado);
|
||||
self.reset_offset(&mut monado.ipc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ impl PlayspaceMover {
|
|||
data.pose *= space_transform;
|
||||
data.hand_pose = new_hand;
|
||||
|
||||
apply_offset(data.pose, monado);
|
||||
apply_offset(data.pose, &mut monado.ipc);
|
||||
self.rotate = Some(data);
|
||||
} else {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
|
|
@ -167,7 +167,7 @@ impl PlayspaceMover {
|
|||
data.pose.translation += relative_pos;
|
||||
data.hand_pose = new_hand;
|
||||
|
||||
apply_offset(data.pose, monado);
|
||||
apply_offset(data.pose, &mut monado.ipc);
|
||||
self.drag = Some(data);
|
||||
} else {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ impl Skybox {
|
|||
sky: None,
|
||||
grid: None,
|
||||
grid_pose: translation_rotation_to_posef(Vec3A::ZERO, Quat::from_rotation_x(PI * -0.5)),
|
||||
grid_color_scale_bias_khr: grid_color_scale_bias_khr,
|
||||
grid_color_scale_bias_khr,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ pub enum InputTask {
|
|||
}
|
||||
|
||||
#[cfg(feature = "openvr")]
|
||||
#[allow(dead_code)]
|
||||
pub enum OpenVrTask {
|
||||
ColorGain(ColorChannel, f32),
|
||||
}
|
||||
|
|
@ -66,6 +67,7 @@ pub enum PlayspaceTask {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[allow(dead_code)]
|
||||
pub enum ModifyPanelCommand {
|
||||
SetText(String),
|
||||
SetColor(String),
|
||||
|
|
@ -110,6 +112,7 @@ pub enum OverlayTask {
|
|||
Drop(OverlaySelector),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum TaskType {
|
||||
Input(InputTask),
|
||||
Overlay(OverlayTask),
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use smithay::wayland::selection::{
|
|||
wlr_data_control as selection_wlr,
|
||||
};
|
||||
use smithay::wayland::shell::kde::decoration::{KdeDecorationHandler, KdeDecorationState};
|
||||
use smithay::wayland::shell::xdg::decoration::{XdgDecorationHandler, XdgDecorationState};
|
||||
use smithay::wayland::shell::xdg::decoration::XdgDecorationHandler;
|
||||
use smithay::wayland::shm::{ShmHandler, ShmState, with_buffer_contents};
|
||||
use smithay::wayland::single_pixel_buffer::get_single_pixel_buffer;
|
||||
use smithay::{
|
||||
|
|
@ -69,7 +69,6 @@ pub struct Application {
|
|||
pub primary_selection_state: PrimarySelectionState,
|
||||
pub ext_data_control_state: selection_ext::DataControlState,
|
||||
pub wlr_data_control_state: selection_wlr::DataControlState,
|
||||
pub xdg_decoration_state: XdgDecorationState,
|
||||
pub kde_decoration_state: KdeDecorationState,
|
||||
pub wayvr_tasks: SyncEventQueue<WayVRTask>,
|
||||
pub redraw_requests: HashSet<wayland_server::backend::ObjectId>,
|
||||
|
|
@ -78,10 +77,6 @@ pub struct Application {
|
|||
}
|
||||
|
||||
impl Application {
|
||||
pub fn check_redraw(&mut self, surface: &WlSurface) -> bool {
|
||||
self.redraw_requests.remove(&surface.id())
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self) {
|
||||
self.image_importer.cleanup();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ macro_rules! gen_id {
|
|||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn iter_mut(
|
||||
&mut self,
|
||||
) -> impl Iterator<Item = ($handle_name, &mut $instance_name)> {
|
||||
|
|
|
|||
|
|
@ -28,10 +28,7 @@ use smithay::{
|
|||
},
|
||||
shell::{
|
||||
kde::decoration::KdeDecorationState,
|
||||
xdg::{
|
||||
SurfaceCachedState, ToplevelSurface, XdgShellState, XdgToplevelSurfaceData,
|
||||
decoration::XdgDecorationState,
|
||||
},
|
||||
xdg::{SurfaceCachedState, ToplevelSurface, XdgShellState, XdgToplevelSurfaceData},
|
||||
},
|
||||
shm::ShmState,
|
||||
},
|
||||
|
|
@ -43,7 +40,6 @@ use std::{
|
|||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use time::get_millis;
|
||||
use vulkano::image::view::ImageView;
|
||||
use wayvr_ipc::{packet_client::PositionMode, packet_server};
|
||||
use wgui::gfx::WGfx;
|
||||
|
|
@ -68,8 +64,6 @@ use crate::{
|
|||
windowing::{OverlayID, OverlaySelector},
|
||||
};
|
||||
|
||||
const STR_INVALID_HANDLE_DISP: &str = "Invalid display handle";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WaylandEnv {
|
||||
pub display_num: u32,
|
||||
|
|
@ -90,6 +84,7 @@ pub struct ProcessWayVREnv {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct ExternalProcessRequest {
|
||||
#[allow(dead_code)]
|
||||
pub env: ProcessWayVREnv,
|
||||
pub client: wayland_server::Client,
|
||||
pub pid: u32,
|
||||
|
|
@ -105,23 +100,7 @@ pub enum WayVRTask {
|
|||
CloseWindowRequest(window::WindowHandle),
|
||||
}
|
||||
|
||||
pub enum BlitMethod {
|
||||
Dmabuf,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl BlitMethod {
|
||||
pub fn from_string(str: &str) -> Option<Self> {
|
||||
match str {
|
||||
"dmabuf" => Some(Self::Dmabuf),
|
||||
"software" => Some(Self::Software),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WvrServerState {
|
||||
time_start: u64,
|
||||
pub manager: client::WayVRCompositor,
|
||||
pub wm: window::WindowManager,
|
||||
pub processes: process::ProcessVec,
|
||||
|
|
@ -153,6 +132,9 @@ impl WvrServerState {
|
|||
gfx_extras: &WGfxExtras,
|
||||
signals: SyncEventQueue<WayVRSignal>,
|
||||
) -> anyhow::Result<Self> {
|
||||
const fn filter_allow_any(_: &wayland_server::Client) -> bool {
|
||||
true
|
||||
}
|
||||
log::info!("Initializing WayVR server");
|
||||
let display: wayland_server::Display<Application> = wayland_server::Display::new()?;
|
||||
let dh = display.handle();
|
||||
|
|
@ -164,9 +146,6 @@ impl WvrServerState {
|
|||
let primary_selection_state = PrimarySelectionState::new::<Application>(&dh);
|
||||
let mut seat = seat_state.new_wl_seat(&dh, "wayvr");
|
||||
|
||||
fn filter_allow_any(_: &wayland_server::Client) -> bool {
|
||||
true
|
||||
}
|
||||
let ext_data_control_state = selection_ext::DataControlState::new::<Application, _>(
|
||||
&dh,
|
||||
Some(&primary_selection_state),
|
||||
|
|
@ -178,7 +157,6 @@ impl WvrServerState {
|
|||
filter_allow_any,
|
||||
);
|
||||
|
||||
let xdg_decoration_state = XdgDecorationState::new::<Application>(&dh);
|
||||
let kde_decoration_state =
|
||||
KdeDecorationState::new::<Application>(&dh, kde_decoration::Mode::Server);
|
||||
|
||||
|
|
@ -255,7 +233,6 @@ impl WvrServerState {
|
|||
primary_selection_state,
|
||||
wlr_data_control_state,
|
||||
ext_data_control_state,
|
||||
xdg_decoration_state,
|
||||
kde_decoration_state,
|
||||
wayvr_tasks: tasks.clone(),
|
||||
redraw_requests: HashSet::new(),
|
||||
|
|
@ -263,10 +240,7 @@ impl WvrServerState {
|
|||
popup_manager: PopupManager::default(),
|
||||
};
|
||||
|
||||
let time_start = get_millis();
|
||||
|
||||
Ok(Self {
|
||||
time_start,
|
||||
manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer, seat)?,
|
||||
processes: ProcessVec::new(),
|
||||
wm: window::WindowManager::new(),
|
||||
|
|
@ -341,8 +315,8 @@ impl WvrServerState {
|
|||
|
||||
let (min_size, max_size) = with_states(toplevel.wl_surface(), |state| {
|
||||
let mut guard = state.cached_state.get::<SurfaceCachedState>();
|
||||
let mut min_size = guard.current().min_size;
|
||||
let mut max_size = guard.current().max_size;
|
||||
let (mut min_size, mut max_size) =
|
||||
{ (guard.current().min_size, guard.current().max_size) };
|
||||
|
||||
if min_size.is_empty() {
|
||||
min_size = Size::new(1, 1);
|
||||
|
|
@ -365,7 +339,7 @@ impl WvrServerState {
|
|||
size.clamp(min_size, max_size),
|
||||
p.pos_mode,
|
||||
Some(p.app_name.clone()),
|
||||
p.icon.as_ref().cloned(),
|
||||
p.icon.clone(),
|
||||
p.exec_path.ends_with("cage"),
|
||||
)
|
||||
}
|
||||
|
|
@ -396,14 +370,13 @@ impl WvrServerState {
|
|||
let mut needs_title = true;
|
||||
let (xdg_title, app_id): (Option<String>, Option<String>) =
|
||||
with_states(toplevel.wl_surface(), |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.map(|t| {
|
||||
states.data_map.get::<XdgToplevelSurfaceData>().map_or(
|
||||
(None, None),
|
||||
|t| {
|
||||
let t = t.lock().unwrap();
|
||||
(t.title.clone(), t.app_id.clone())
|
||||
})
|
||||
.unwrap_or((None, None))
|
||||
},
|
||||
)
|
||||
});
|
||||
if let Some(xdg_title) = xdg_title {
|
||||
needs_title = false;
|
||||
|
|
@ -429,7 +402,7 @@ impl WvrServerState {
|
|||
// Fall back to identicon
|
||||
let icon = match icon {
|
||||
Some(icon) => icon,
|
||||
None => DesktopFinder::create_icon(&*title)?.into(),
|
||||
None => DesktopFinder::create_icon(&title)?.into(),
|
||||
};
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Create(
|
||||
|
|
@ -573,7 +546,7 @@ impl WvrServerState {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some(oid) = self.window_to_overlay.get(&hnd).cloned() {
|
||||
if let Some(oid) = self.window_to_overlay.get(&hnd).copied() {
|
||||
tasks.enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id(
|
||||
oid,
|
||||
))));
|
||||
|
|
@ -591,7 +564,7 @@ impl WvrServerState {
|
|||
}
|
||||
|
||||
pub fn get_overlay_id(&self, window: window::WindowHandle) -> Option<OverlayID> {
|
||||
self.window_to_overlay.get(&window).cloned()
|
||||
self.window_to_overlay.get(&window).copied()
|
||||
}
|
||||
|
||||
pub fn send_mouse_move(&mut self, handle: window::WindowHandle, x: u32, y: u32) {
|
||||
|
|
@ -656,32 +629,12 @@ impl WvrServerState {
|
|||
self.cur_modifiers = modifiers;
|
||||
}
|
||||
|
||||
// Check if process with given arguments already exists
|
||||
pub fn process_query(
|
||||
&self,
|
||||
exec_path: &str,
|
||||
args: &[&str],
|
||||
_env: &[(&str, &str)],
|
||||
) -> Option<process::ProcessHandle> {
|
||||
for (idx, cell) in self.processes.vec.iter().enumerate() {
|
||||
if let Some(cell) = &cell
|
||||
&& let process::Process::Managed(process) = &cell.obj
|
||||
{
|
||||
if process.exec_path != exec_path || process.args != args {
|
||||
continue;
|
||||
}
|
||||
return Some(process::ProcessVec::get_handle(cell, idx));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn add_external_process(&mut self, pid: u32) -> process::ProcessHandle {
|
||||
self.processes
|
||||
.add(process::Process::External(process::ExternalProcess { pid }))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn spawn_process(
|
||||
&mut self,
|
||||
app_name: &str,
|
||||
|
|
@ -759,16 +712,12 @@ fn generate_auth_key() -> String {
|
|||
uuid.to_string()
|
||||
}
|
||||
|
||||
pub struct SpawnProcessResult {
|
||||
pub auth_key: String,
|
||||
pub child: std::process::Child,
|
||||
}
|
||||
|
||||
struct SurfaceBufWithImageContainer {
|
||||
inner: RefCell<SurfaceBufWithImage>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct SurfaceBufWithImage {
|
||||
pub image: Arc<ImageView>,
|
||||
pub transform: Transform,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ pub struct ExternalProcess {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Process {
|
||||
Managed(WayVRProcess), // Process spawned by WayVR
|
||||
External(ExternalProcess), // External process not directly controlled by us
|
||||
|
|
@ -58,11 +59,12 @@ impl Process {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_name(&self) -> String {
|
||||
match self {
|
||||
Self::Managed(p) => p
|
||||
.get_name()
|
||||
.or_else(|| p.exec_path.split('/').last().map(String::from))
|
||||
.or_else(|| p.exec_path.split('/').next_back().map(String::from))
|
||||
.unwrap_or_else(|| String::from("unknown")),
|
||||
Self::External(p) => p.get_name().unwrap_or_else(|| String::from("unknown")),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@ const FILES: [&str; 1] = ["keyboard.yaml"];
|
|||
#[repr(usize)]
|
||||
pub enum ConfigType {
|
||||
Keyboard,
|
||||
Watch,
|
||||
Settings,
|
||||
Anchor,
|
||||
}
|
||||
|
||||
pub fn load_known_yaml<T>(config_type: ConfigType) -> T
|
||||
|
|
@ -143,6 +140,7 @@ pub struct AutoSettings {
|
|||
pub context_menu_hold_and_release: bool,
|
||||
pub capture_method: CaptureMethod,
|
||||
pub keyboard_middle_click_mode: AltModifier,
|
||||
pub keyboard_swipe_to_type_enabled: bool,
|
||||
pub autostart_apps: Vec<WvrProcessLaunchParams>,
|
||||
pub handsfree_pointer: HandsfreePointer,
|
||||
pub language: Option<Language>,
|
||||
|
|
@ -194,6 +192,7 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
|
|||
context_menu_hold_and_release: config.context_menu_hold_and_release,
|
||||
capture_method: config.capture_method,
|
||||
keyboard_middle_click_mode: config.keyboard_middle_click_mode,
|
||||
keyboard_swipe_to_type_enabled: config.keyboard_swipe_to_type_enabled,
|
||||
autostart_apps: config.autostart_apps.clone(),
|
||||
handsfree_pointer: config.handsfree_pointer,
|
||||
language: config.language,
|
||||
|
|
|
|||
|
|
@ -352,16 +352,12 @@ pub fn export_dmabuf_image(
|
|||
Ok(ExportedDmabufImage {
|
||||
view: ImageView::new_default(image)?,
|
||||
fd,
|
||||
modifier: DrmModifier::from(modifier),
|
||||
modifier,
|
||||
offset: layout.offset as _,
|
||||
stride: layout.row_pitch as _,
|
||||
})
|
||||
}
|
||||
|
||||
fn align_to(value: u64, alignment: u64) -> u64 {
|
||||
((value + alignment - 1) / alignment) * alignment
|
||||
}
|
||||
|
||||
pub(super) fn get_drm_formats(device: Arc<Device>) -> Vec<DrmFormat> {
|
||||
let possible_formats = [
|
||||
DrmFourcc::Abgr8888,
|
||||
|
|
|
|||
|
|
@ -28,24 +28,20 @@ use {ash::vk, std::os::raw::c_void};
|
|||
|
||||
use vulkano::{
|
||||
self, VulkanObject,
|
||||
buffer::{Buffer, BufferContents, IndexBuffer, Subbuffer},
|
||||
buffer::{Buffer, BufferContents, Subbuffer},
|
||||
device::{
|
||||
DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags,
|
||||
physical::{PhysicalDevice, PhysicalDeviceType},
|
||||
},
|
||||
format::Format,
|
||||
instance::{Instance, InstanceCreateInfo, InstanceExtensions},
|
||||
pipeline::graphics::{
|
||||
color_blend::{AttachmentBlend, BlendFactor, BlendOp},
|
||||
vertex_input::Vertex,
|
||||
},
|
||||
pipeline::graphics::vertex_input::Vertex,
|
||||
shader::ShaderModule,
|
||||
};
|
||||
|
||||
use dmabuf::get_drm_formats;
|
||||
|
||||
pub type Vert2Buf = Subbuffer<[Vert2Uv]>;
|
||||
pub type IndexBuf = IndexBuffer;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(BufferContents, Vertex, Copy, Clone, Debug)]
|
||||
|
|
@ -56,17 +52,6 @@ pub struct Vert2Uv {
|
|||
pub in_uv: [f32; 2],
|
||||
}
|
||||
|
||||
pub const INDICES: [u16; 6] = [2, 1, 0, 1, 2, 3];
|
||||
|
||||
pub const BLEND_ALPHA: AttachmentBlend = AttachmentBlend {
|
||||
src_color_blend_factor: BlendFactor::SrcAlpha,
|
||||
dst_color_blend_factor: BlendFactor::OneMinusSrcAlpha,
|
||||
color_blend_op: BlendOp::Add,
|
||||
src_alpha_blend_factor: BlendFactor::One,
|
||||
dst_alpha_blend_factor: BlendFactor::One,
|
||||
alpha_blend_op: BlendOp::Max,
|
||||
};
|
||||
|
||||
pub struct WGfxExtras {
|
||||
pub shaders: HashMap<&'static str, Arc<ShaderModule>>,
|
||||
pub drm_formats: Arc<[DrmFormat]>,
|
||||
|
|
@ -711,6 +696,7 @@ impl GpuFutures {
|
|||
|
||||
pub trait ExtentExt {
|
||||
fn extent_f32(&self) -> [f32; 2];
|
||||
#[allow(dead_code)]
|
||||
fn extent_vec2(&self) -> Vec2;
|
||||
fn extent_u32arr(&self) -> [u32; 2];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,221 +0,0 @@
|
|||
# Custom UI Elements
|
||||
|
||||
Elements on custom panels may be modified at runtime using `wayvrctl`.
|
||||
|
||||
For more, refer to: `wayvrctl panel-modify --help`
|
||||
|
||||
### Labels
|
||||
|
||||
#### Clock label
|
||||
|
||||
Clock labels are driven by the current time, adhering to the user's 12/24 hour setting as well as timezone settings.
|
||||
|
||||
Available display values are: `name` (timezone name), `time`, `date`, `dow` or a custom [format string](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) like `%H:%M:%S`.
|
||||
|
||||
See the Custom Timezones section for more info on timezones. Skip `_timezone` to use local time.
|
||||
|
||||
```xml
|
||||
<label _source="clock" _display="time" _timezone="0" [...] />
|
||||
```
|
||||
|
||||
#### Timer label
|
||||
|
||||
Instead of a clock, this label shows the amount of time since program start, (aka time in VR).
|
||||
|
||||
Use `_format` to arrange `%h` hours, `%m` minutes, and `%s` seconds.
|
||||
|
||||
```xml
|
||||
<label _source="timer" _format="%h:%m:%s" [...] />
|
||||
```
|
||||
|
||||
#### Battery label
|
||||
|
||||
This is a label type that's used internally to display battery states.
|
||||
|
||||
```xml
|
||||
<label _source="battery" _device="0" [...] />
|
||||
```
|
||||
|
||||
#### IPD
|
||||
|
||||
Displays IPD value in millimeters. Not parametrizable.
|
||||
|
||||
Format: `ipd`
|
||||
|
||||
```xml
|
||||
<label _source="ipd" [...] />
|
||||
```
|
||||
|
||||
### Buttons
|
||||
|
||||
Buttons consist of a label component and one or more actions to handle press and/or release events.
|
||||
|
||||
Supported events:
|
||||
|
||||
```xml
|
||||
<button _press="..." _release="..." />
|
||||
```
|
||||
|
||||
Laser-color-specific variants are also available
|
||||
- `_press_left` & `_release_left` for blue laser
|
||||
- `_press_right` & `_release_right` for orange laser
|
||||
- `_press_middle` & `_release_middle` for purple laser
|
||||
|
||||
Release after short/long press (length controlled by config `long_press_duration`)
|
||||
- `_short_release` & `_long_release` for any laser
|
||||
- `_short_release_left` & `_long_release_left` for blue laser
|
||||
- `_short_release_right` & `_long_release_right` for orange laser
|
||||
- `_short_release_middle` & `_long_release_middle` for purple laser
|
||||
|
||||
#### Supported button actions
|
||||
|
||||
##### `::ShellExec <command> [args ..]`
|
||||
|
||||
This button action executes a shell script using the `sh` shell.
|
||||
|
||||
- Long-running processes are allowed, but a new execution will not be triggered until the previous process has exited.
|
||||
- If triggered again while the previous process is still running, SIGUSR1 will be sent to that child process.
|
||||
|
||||
```xml
|
||||
<button _press="::ShellExec $HOME/myscript.sh test-argument" [...] />
|
||||
```
|
||||
|
||||
##### `::OscSend <path>` `::OscSend <path> <args ..>`
|
||||
|
||||
Send an OSC message. The target port comes from the `osc_out_port` configuration setting.
|
||||
|
||||
There are two formats; here is an example for both formats writing a message to the VRChat Chatbox:
|
||||
```xml
|
||||
<!-- parameter form - OSC arguments are listed as parameters labelled `_arg<n>` where `n` is 0-indexed -->
|
||||
<Button _press="::OscSend /chatbox/input" _arg0="Hello World! I am WayVR." _arg1="true" _arg2="true"> </Button>
|
||||
<!-- will send: ("Hello World! I am WayVR.", True, True) to /chatbox/input -->
|
||||
```
|
||||
|
||||
```xml
|
||||
<!-- shorthand form - OSC arguments are space-separated in one string. note that single strings cannot contain spaces -->
|
||||
<Button _press="::OscSend /chatbox/input Hello_World!_I_am_WayVR. true true"> </Button>
|
||||
<!-- will send: ("Hello_World!_I_am_WayVR.", True, True) to /chatbox/input -->
|
||||
```
|
||||
|
||||
The two can be combined; parameter-form arguments will be appended after shorthand-form arguments:
|
||||
```xml
|
||||
<!-- combined-form - rectangle bounds with a name -->
|
||||
<Button _press="::OscSend /graphthing/rectangle 0i32 0i32 50i32 200i32" _arg0="tall rectangle"> </Button>
|
||||
<!-- will send: (0, 0, 50, 200, "tall rectangle") to /graphthing/rectangle -->
|
||||
```
|
||||
|
||||
Available argument value types (case insensitive):
|
||||
- Bool: `true` or `false`
|
||||
- Nil: `nil`
|
||||
- Inf: `inf`
|
||||
- Int: suffix `i32` (`-1i32`, `1i32`, etc)
|
||||
- Long: suffix `i64` (`-1i64`, `1i64`, etc)
|
||||
- Float: suffix `f32` (`1f32`, `1.0f32`, etc)
|
||||
- Double: suffix `f64` (`1f64`, `1.0f64`, etc)
|
||||
- String: any other value
|
||||
- Shorthand form will treat Strings with spaces as multiple arguments. Use parameter form if you need spaces.
|
||||
|
||||
##### `::SendKey <VirtualKey> <UP|DOWN>`
|
||||
|
||||
Sends a key using the virtual keyboard. If WayVR is focused, the key is sent to the WayVR app.
|
||||
|
||||
Supported VirtualKey values are listed [here](https://github.com/galister/wlx-overlay-s/blob/f2bd169c2217d51cd2de862a6429444bf326f471/wlx-overlay-s/src/subsystem/hid/mod.rs#L336).
|
||||
|
||||
##### `::PlayspaceReset`
|
||||
|
||||
Resets the STAGE space to (0,0,0) with identity rotation.
|
||||
|
||||
##### `::PlayspaceRecenter`
|
||||
|
||||
Recenters the STAGE space position so that the HMD is in the center. Does not modify floor level.
|
||||
|
||||
##### `::PlayspaceFixFloor`
|
||||
|
||||
Adjusts the level of floor for STAGE and LOCAL_FLOOR spaces.
|
||||
|
||||
The user is asked to place one controller on the floor.
|
||||
|
||||
##### `::ContextMenuOpen <name>`
|
||||
|
||||
Opens the `<context_menu>` of the given name at the location of the click.
|
||||
|
||||
##### `::ContextMenuClose`
|
||||
|
||||
Closes any active context menus.
|
||||
|
||||
##### `::ElementSetDisplay <id> <none|flex|block|grid>`
|
||||
|
||||
Sets the visiblitity of the element with `id`.
|
||||
|
||||
##### `::SetToggle <index>`
|
||||
|
||||
If the given set is visible, it will be hidden.
|
||||
|
||||
Otherwise, it will be set visible.
|
||||
|
||||
##### `::SetSwitch <index>`
|
||||
|
||||
Switch to the given set. If the set is already visible, nothing happens.
|
||||
|
||||
##### `::AddSet`
|
||||
|
||||
Add a new set and switch to it. The keyboard will be set to visible.
|
||||
|
||||
##### `::DeleteSet`
|
||||
|
||||
Hides the current set, then deletes it.
|
||||
|
||||
##### `::DashToggle`
|
||||
|
||||
Toggle the dashboard
|
||||
|
||||
##### `::EditToggle`
|
||||
|
||||
Toggle edit mode
|
||||
|
||||
##### `::NewMirror`
|
||||
|
||||
Opens a new PipeWire mirror (Wayland-only)
|
||||
|
||||
##### `::CleanupMirrors`
|
||||
|
||||
Destroys all mirrors that are not currently visible (including those that are in a different set).
|
||||
|
||||
##### `::Restart`
|
||||
|
||||
Restarts WayVR, reloading all settings.
|
||||
|
||||
##### `::ShutDown`
|
||||
|
||||
Gracefully shuts down WayVR.
|
||||
|
||||
##### `::OverlayReset <overlay_name>`
|
||||
|
||||
Resets the position of the given overlay and makes it visible.
|
||||
|
||||
##### `::OverlayToggle <overlay_name>`
|
||||
|
||||
Toggle the visibility of this overlay for the current set
|
||||
|
||||
##### `::OverlayDrop <overlay_name>`
|
||||
|
||||
Destroys the overlay permanently. Mostly useful for mirrors.
|
||||
|
||||
##### `::CustomOverlayReload <overlay_name>`
|
||||
|
||||
If this is a custom overlay, reloads its XML from disk.
|
||||
|
||||
##### `::WvrOverlayCloseWindow <overlay_name>`
|
||||
|
||||
If this is an application, send a close window request to its wl_surface (e.a. X'ing the window)
|
||||
|
||||
##### `::WvrOverlayTermProcess <overlay_name>`
|
||||
|
||||
If the overlay belongs to an application, sends SIGTERM (graceful exit request) to its process.
|
||||
|
||||
##### `::WvrOverlayKillProcess <overlay_name>`
|
||||
|
||||
If the overlay belongs to an application, sends SIGKILL (forced exit request) to its process.
|
||||
|
||||
Also destroys all overlays belonging to the process.
|
||||
|
||||
|
|
@ -186,7 +186,6 @@ fn short_duration(btn: &ComponentButton, app: &AppState) -> bool {
|
|||
btn.get_time_since_last_pressed().as_secs_f32() < app.session.config.long_press_duration
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(super) fn setup_custom_button<S: 'static>(
|
||||
layout: &mut Layout,
|
||||
parser_state: &ParserState,
|
||||
|
|
@ -222,7 +221,8 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
// pass attribs with key `_context_{name}` to the context_menu template
|
||||
let mut template_params = HashMap::new();
|
||||
for AttribPair { attrib, value } in &attribs.pairs {
|
||||
const PREFIX: &'static str = "_context_";
|
||||
const PREFIX: &str = "_context_";
|
||||
#[allow(clippy::manual_strip)]
|
||||
if attrib.starts_with(PREFIX) {
|
||||
template_params.insert(attrib[PREFIX.len()..].into(), value.clone());
|
||||
}
|
||||
|
|
@ -348,10 +348,10 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
}
|
||||
"::OverlayReset" => {
|
||||
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
|
||||
if arg.len() < 1 {
|
||||
if arg.is_empty() {
|
||||
log_cmd_missing_arg(parser_state, TAG, name, command);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
|
|
@ -367,10 +367,10 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
}
|
||||
"::OverlayToggle" => {
|
||||
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
|
||||
if arg.len() < 1 {
|
||||
if arg.is_empty() {
|
||||
log_cmd_missing_arg(parser_state, TAG, name, command);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
|
|
@ -387,10 +387,10 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
}
|
||||
"::OverlayDrop" => {
|
||||
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
|
||||
if arg.len() < 1 {
|
||||
if arg.is_empty() {
|
||||
log_cmd_missing_arg(parser_state, TAG, name, command);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
|
|
@ -422,10 +422,10 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
}),
|
||||
"::CustomOverlayReload" => {
|
||||
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
|
||||
if arg.len() < 1 {
|
||||
if arg.is_empty() {
|
||||
log_cmd_missing_arg(parser_state, TAG, name, command);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
|
|
@ -460,10 +460,10 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
}
|
||||
"::WvrOverlayCloseWindow" => {
|
||||
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
|
||||
if arg.len() < 1 {
|
||||
if arg.is_empty() {
|
||||
log_cmd_missing_arg(parser_state, TAG, name, command);
|
||||
return;
|
||||
};
|
||||
}
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
|
|
@ -486,10 +486,10 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
}
|
||||
"::WvrOverlayKillProcess" | "::WvrOverlayTermProcess" => {
|
||||
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
|
||||
if arg.len() < 1 {
|
||||
if arg.is_empty() {
|
||||
log_cmd_missing_arg(parser_state, TAG, name, command);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
let signal = if command == "::WvrOverlayKillProcess" {
|
||||
KillSignal::Kill
|
||||
|
|
@ -577,19 +577,37 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"TOAST.FIXING_FLOOR".into(),
|
||||
"TOAST.ONE_CONTROLLER_ON_FLOOR".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
.submit(app);
|
||||
let globals = app.wgui_globals.clone();
|
||||
|
||||
app.tasks.enqueue_at(
|
||||
TaskType::Playspace(PlayspaceTask::FixFloor),
|
||||
Instant::now() + Duration::from_secs(5),
|
||||
);
|
||||
let duration_secs = 5;
|
||||
let now = Instant::now();
|
||||
|
||||
for i in 0..duration_secs {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
globals.i18n().translate_and_replace(
|
||||
"TOAST.FIXING_FLOOR_IN_X_SECS",
|
||||
("{SECONDS}", &format!("{}", duration_secs - i)),
|
||||
),
|
||||
"TOAST.ONE_CONTROLLER_ON_FLOOR".into(),
|
||||
)
|
||||
.with_timeout(1.0)
|
||||
.with_lerp_amount(1.0)
|
||||
.with_sound(i == 0)
|
||||
.submit_at(app, now + Duration::from_secs(i as _));
|
||||
}
|
||||
|
||||
app.audio_sample_player
|
||||
.play_sample(&mut app.audio_system, "fix_floor");
|
||||
|
||||
let deadline = now + Duration::from_secs(duration_secs);
|
||||
|
||||
app.tasks
|
||||
.enqueue_at(TaskType::Playspace(PlayspaceTask::FixFloor), deadline);
|
||||
|
||||
Toast::new(ToastTopic::System, "DONE".into(), String::new())
|
||||
.with_timeout(2.0)
|
||||
.submit_at(app, deadline);
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::Shutdown" => Box::new(move |_common, data, app, _| {
|
||||
|
|
@ -667,7 +685,6 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
button: button.clone(),
|
||||
exec: args.fold(String::new(), |c, n| c + " " + n),
|
||||
mut_state: RefCell::new(ShellButtonMutableState::default()),
|
||||
carry_over: RefCell::new(None),
|
||||
});
|
||||
|
||||
layout.add_event_listener::<AppState, S>(
|
||||
|
|
@ -707,7 +724,6 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||
if let Ok(osc_arg) = parse_osc_value(arg).inspect_err(|e| {
|
||||
let msg = format!("Could not parse OSC value \"{arg}\": {e:?}");
|
||||
log_cmd_invalid_arg(parser_state, TAG, name, command, &msg);
|
||||
return;
|
||||
}) {
|
||||
osc_args.push(osc_arg);
|
||||
}
|
||||
|
|
@ -758,10 +774,10 @@ struct ShellButtonMutableState {
|
|||
}
|
||||
|
||||
struct ShellButtonState {
|
||||
#[allow(dead_code)] // preserve lifetime of this button component
|
||||
button: Rc<ComponentButton>,
|
||||
exec: String,
|
||||
mut_state: RefCell<ShellButtonMutableState>,
|
||||
carry_over: RefCell<Option<String>>,
|
||||
}
|
||||
|
||||
fn shell_on_action(state: &ShellButtonState) -> anyhow::Result<()> {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use crate::{
|
|||
pub struct DeviceList;
|
||||
|
||||
impl DeviceList {
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn on_notify(
|
||||
&mut self,
|
||||
app: &AppState,
|
||||
|
|
@ -25,6 +26,7 @@ impl DeviceList {
|
|||
doc_params: &ParseDocumentParams,
|
||||
) -> anyhow::Result<bool> {
|
||||
let mut elements_changed = false;
|
||||
#[allow(clippy::single_match)]
|
||||
match event_data {
|
||||
OverlayEventData::DevicesChanged => {
|
||||
let devices_root = parser_state
|
||||
|
|
@ -48,7 +50,7 @@ impl DeviceList {
|
|||
|
||||
params.insert("idx".into(), i.to_string().into());
|
||||
parser_state.instantiate_template(
|
||||
&doc_params,
|
||||
doc_params,
|
||||
template,
|
||||
layout,
|
||||
devices_root,
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ fn timer_on_tick(
|
|||
data: &mut event::CallbackData,
|
||||
) {
|
||||
let duration = Local::now()
|
||||
.signed_duration_since(&state.start)
|
||||
.signed_duration_since(state.start)
|
||||
.num_seconds();
|
||||
|
||||
let time = &state
|
||||
|
|
@ -271,7 +271,7 @@ fn timer_on_tick(
|
|||
.replace("%h", &format!("{:02}", ((duration / 60) / 60)));
|
||||
|
||||
let label = data.obj.get_as_mut::<WidgetLabel>().unwrap();
|
||||
label.set_text(common, Translation::from_raw_text(&time));
|
||||
label.set_text(common, Translation::from_raw_text(time));
|
||||
}
|
||||
|
||||
fn ipd_on_tick(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use wgui::{
|
|||
button::ComponentButton, checkbox::ComponentCheckbox, radio_group::ComponentRadioGroup,
|
||||
slider::ComponentSlider,
|
||||
},
|
||||
drawing,
|
||||
event::{
|
||||
CallbackDataCommon, Event as WguiEvent, EventAlterables, EventCallback, EventListenerID,
|
||||
EventListenerKind, InternalStateChangeEvent, MouseButtonEvent, MouseButtonIndex,
|
||||
|
|
@ -55,8 +54,6 @@ pub mod set_list;
|
|||
|
||||
const DEFAULT_MAX_SIZE: f32 = 2048.0;
|
||||
|
||||
const COLOR_ERR: drawing::Color = drawing::Color::new(1., 0., 1., 1.);
|
||||
|
||||
pub type OnNotifyFunc<S> =
|
||||
Box<dyn Fn(&mut GuiPanel<S>, &mut AppState, OverlayEventData) -> anyhow::Result<()>>;
|
||||
|
||||
|
|
@ -449,7 +446,7 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
|
|||
self.interaction_transform
|
||||
}
|
||||
fn get_attrib(&self, attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||
self.extra_attribs.get(&attrib).cloned()
|
||||
self.extra_attribs.get(attrib).cloned()
|
||||
}
|
||||
fn set_attrib(&mut self, _app: &mut AppState, _value: BackendAttribValue) -> bool {
|
||||
false
|
||||
|
|
@ -459,22 +456,22 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
|
|||
fn log_missing_attrib(parser_state: &ParserState, tag_name: &str, attrib: &str) {
|
||||
log::warn!(
|
||||
"{:?}: <{tag_name}> is missing \"{attrib}\"",
|
||||
parser_state.path.get_path_buf()
|
||||
)
|
||||
parser_state.path.get_path_buf().display()
|
||||
);
|
||||
}
|
||||
|
||||
fn log_invalid_attrib(parser_state: &ParserState, tag_name: &str, attrib: &str, value: &str) {
|
||||
log::warn!(
|
||||
"{:?}: <{tag_name}> value for \"{attrib}\" is invalid: {value}",
|
||||
parser_state.path.get_path_buf()
|
||||
)
|
||||
parser_state.path.get_path_buf().display()
|
||||
);
|
||||
}
|
||||
|
||||
fn log_cmd_missing_arg(parser_state: &ParserState, tag_name: &str, attrib: &str, command: &str) {
|
||||
log::warn!(
|
||||
"{:?}: <{tag_name}> \"{attrib}\": \"{command}\" has missing arguments",
|
||||
parser_state.path.get_path_buf()
|
||||
)
|
||||
parser_state.path.get_path_buf().display()
|
||||
);
|
||||
}
|
||||
|
||||
fn log_cmd_invalid_arg(
|
||||
|
|
@ -486,8 +483,8 @@ fn log_cmd_invalid_arg(
|
|||
) {
|
||||
log::warn!(
|
||||
"{:?}: <{tag_name}> \"{attrib}\": \"{command}\" has invalid argument: {arg}",
|
||||
parser_state.path.get_path_buf()
|
||||
)
|
||||
parser_state.path.get_path_buf().display()
|
||||
);
|
||||
}
|
||||
|
||||
pub fn apply_custom_command<T>(
|
||||
|
|
@ -600,7 +597,7 @@ pub fn apply_custom_command<T>(
|
|||
.parser_state
|
||||
.fetch_component_as::<ComponentRadioGroup>(element)
|
||||
{
|
||||
radio.set_value(&mut com, &value_str)?;
|
||||
radio.set_value(&mut com, value_str)?;
|
||||
}
|
||||
}
|
||||
ModifyPanelCommand::GetValue => todo!(),
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ impl OverlayList {
|
|||
|
||||
if meta.visible {
|
||||
let mut com = CallbackDataCommon {
|
||||
alterables: alterables,
|
||||
alterables,
|
||||
state: &layout.state,
|
||||
};
|
||||
overlay_button.set_sticky_state(&mut com, true);
|
||||
|
|
@ -116,18 +116,13 @@ impl OverlayList {
|
|||
|
||||
params.insert("idx".into(), i.to_string().into());
|
||||
params.insert("name".into(), meta.name.as_ref().into());
|
||||
parser_state.instantiate_template(
|
||||
&doc_params,
|
||||
template,
|
||||
layout,
|
||||
root,
|
||||
params,
|
||||
)?;
|
||||
parser_state
|
||||
.instantiate_template(doc_params, template, layout, root, params)?;
|
||||
let overlay_button = parser_state
|
||||
.fetch_component_as::<ComponentButton>(&format!("overlay_{i}"))?;
|
||||
if meta.visible {
|
||||
let mut com = CallbackDataCommon {
|
||||
alterables: alterables,
|
||||
alterables,
|
||||
state: &layout.state,
|
||||
};
|
||||
overlay_button.set_sticky_state(&mut com, true);
|
||||
|
|
@ -138,7 +133,7 @@ impl OverlayList {
|
|||
}
|
||||
OverlayEventData::VisibleOverlaysChanged(overlays) => {
|
||||
let mut com = CallbackDataCommon {
|
||||
alterables: alterables,
|
||||
alterables,
|
||||
state: &layout.state,
|
||||
};
|
||||
let mut overlay_buttons = self.overlay_buttons.clone();
|
||||
|
|
|
|||
|
|
@ -18,10 +18,6 @@ pub struct SetList {
|
|||
}
|
||||
|
||||
impl SetList {
|
||||
pub fn num_sets(&self) -> usize {
|
||||
self.set_buttons.len()
|
||||
}
|
||||
|
||||
pub fn on_notify(
|
||||
&mut self,
|
||||
layout: &mut Layout,
|
||||
|
|
@ -34,7 +30,7 @@ impl SetList {
|
|||
match event_data {
|
||||
OverlayEventData::ActiveSetChanged(current_set) => {
|
||||
let mut com = CallbackDataCommon {
|
||||
alterables: alterables,
|
||||
alterables,
|
||||
state: &layout.state,
|
||||
};
|
||||
if let Some(old_set) = self.current_set.take()
|
||||
|
|
@ -64,7 +60,7 @@ impl SetList {
|
|||
parser_state.fetch_component_as::<ComponentButton>(&format!("set_{i}"))?;
|
||||
if self.current_set == Some(i) {
|
||||
let mut com = CallbackDataCommon {
|
||||
alterables: alterables,
|
||||
alterables,
|
||||
state: &layout.state,
|
||||
};
|
||||
set_button.set_sticky_state(&mut com, true);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
},
|
||||
ipc::signal::WayVRSignal,
|
||||
state::AppState,
|
||||
windowing::{OverlaySelector, manager::OverlayWindowManager},
|
||||
windowing::manager::OverlayWindowManager,
|
||||
};
|
||||
|
||||
fn process_tick_tasks(
|
||||
|
|
@ -54,12 +54,6 @@ where
|
|||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::SwitchSet(set)));
|
||||
}
|
||||
WayVRSignal::DropOverlay(overlay_id) => {
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id(
|
||||
overlay_id,
|
||||
))));
|
||||
}
|
||||
WayVRSignal::CustomTask(custom_task) => {
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::ModifyPanel(custom_task)));
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use wayvr_ipc::{
|
|||
packet_server::{self, PacketServer, WlxInputStatePointer},
|
||||
};
|
||||
|
||||
#[allow(dead_code)] // not used for now, wayvr accepts any client
|
||||
pub struct AuthInfo {
|
||||
pub client_name: String,
|
||||
pub protocol_version: u32, // client protocol version
|
||||
|
|
@ -75,6 +76,7 @@ fn read_payload(conn: &mut local_socket::Stream, size: u32) -> Option<Payload> {
|
|||
|
||||
pub struct TickParams<'a> {
|
||||
pub wvr_server: &'a mut WvrServerState,
|
||||
#[allow(dead_code)]
|
||||
pub tasks: &'a mut Vec<wayvr::TickTask>,
|
||||
pub signals: &'a SyncEventQueue<WayVRSignal>,
|
||||
pub input_state: &'a InputState,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
pub enum WayVRSignal {
|
||||
BroadcastStateChanged(wayvr_ipc::packet_server::WvrStateChanged),
|
||||
DeviceHaptics(usize, crate::backend::input::Haptics),
|
||||
DropOverlay(crate::windowing::OverlayID),
|
||||
SwitchSet(Option<usize>),
|
||||
ShowHide,
|
||||
CustomTask(crate::backend::task::ModifyPanelTask),
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
|
||||
#![allow(
|
||||
dead_code,
|
||||
clippy::suboptimal_flops,
|
||||
clippy::default_trait_access,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_lossless,
|
||||
clippy::match_wildcard_for_single_variants,
|
||||
clippy::doc_markdown,
|
||||
clippy::struct_excessive_bools,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::needless_pass_by_ref_mut,
|
||||
clippy::multiple_crate_versions,
|
||||
clippy::cargo_common_metadata,
|
||||
clippy::option_if_let_else
|
||||
clippy::cast_lossless,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::default_trait_access,
|
||||
clippy::doc_markdown,
|
||||
clippy::match_wildcard_for_single_variants,
|
||||
clippy::multiple_crate_versions,
|
||||
clippy::needless_pass_by_ref_mut,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::option_if_let_else,
|
||||
clippy::struct_excessive_bools,
|
||||
clippy::suboptimal_flops,
|
||||
clippy::too_many_lines,
|
||||
clippy::use_self
|
||||
)]
|
||||
mod app_misc;
|
||||
mod backend;
|
||||
|
|
@ -132,7 +133,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
Some(XrBackend::OpenXR) => args.push("--openxr"),
|
||||
Some(XrBackend::OpenVR) => args.push("--openvr"),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
let _ = Command::new(exe).args(args).spawn();
|
||||
}
|
||||
|
|
@ -192,7 +193,7 @@ fn auto_run(args: Args, used_backend: &mut Option<XrBackend>) {
|
|||
|
||||
let instructions = format!("Could not connect to runtime.\n{instructions}");
|
||||
|
||||
let _ = DbusConnector::notify_send("WayVR", &instructions, 1, 0, 0, false);
|
||||
let _ = DbusConnector::notify_send("WayVR", &instructions, 1, 30000, 0, false);
|
||||
|
||||
#[cfg(not(any(feature = "openvr", feature = "openxr")))]
|
||||
compile_error!("No VR support! Enable either openvr or openxr features!");
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ pub fn create_custom(app: &mut AppState, name: Arc<str>) -> Option<OverlayWindow
|
|||
.ok()?;
|
||||
|
||||
if let Some(icon) = panel.parser_state.data.var_map.get("_panel_icon") {
|
||||
let icon = expand_env_vars(&icon);
|
||||
let icon = expand_env_vars(icon);
|
||||
panel
|
||||
.extra_attribs
|
||||
.insert(BackendAttrib::Icon, BackendAttribValue::Icon(icon.into()));
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ impl DashFrontend {
|
|||
let mut interface = DashInterfaceLive::new();
|
||||
|
||||
for p in app.session.config.autostart_apps.clone() {
|
||||
let _ = interface.process_launch(app, false, p)?;
|
||||
let _ = interface.process_launch(app, false, p);
|
||||
}
|
||||
|
||||
let frontend = frontend::Frontend::new(frontend::InitParams {
|
||||
|
|
@ -304,7 +304,7 @@ pub fn create_dash_frontend(app: &mut AppState) -> anyhow::Result<OverlayWindowC
|
|||
pub struct DashInterfaceLive {}
|
||||
|
||||
impl DashInterfaceLive {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
|
@ -411,9 +411,10 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
|||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::ToggleOverlay(
|
||||
OverlaySelector::Id(oid),
|
||||
match visible {
|
||||
true => ToggleMode::EnsureOn,
|
||||
false => ToggleMode::EnsureOff,
|
||||
if visible {
|
||||
ToggleMode::EnsureOn
|
||||
} else {
|
||||
ToggleMode::EnsureOff
|
||||
},
|
||||
)));
|
||||
Ok(())
|
||||
|
|
@ -469,20 +470,27 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
|||
fn monado_client_list(
|
||||
&mut self,
|
||||
app: &mut AppState,
|
||||
filtered: bool,
|
||||
) -> anyhow::Result<Vec<dash_interface::MonadoClient>> {
|
||||
let Some(monado) = &mut app.monado else {
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return Ok(Vec::new()); // no monado available
|
||||
};
|
||||
|
||||
let clients = monado_list_clients_filtered(monado)?;
|
||||
let clients = if filtered {
|
||||
monado_list_clients_filtered(&mut monado.ipc)?
|
||||
} else {
|
||||
monado.ipc.clients()?.into_iter().collect()
|
||||
};
|
||||
|
||||
let mut res = Vec::<dash_interface::MonadoClient>::new();
|
||||
|
||||
for mut client in clients {
|
||||
let client_id = client.id();
|
||||
let name = client.name()?;
|
||||
let state = client.state()?;
|
||||
|
||||
res.push(dash_interface::MonadoClient {
|
||||
id: client_id as i64,
|
||||
name,
|
||||
is_primary: state.contains(libmonado::ClientState::ClientPrimaryApp),
|
||||
is_active: state.contains(libmonado::ClientState::ClientSessionActive),
|
||||
|
|
@ -498,35 +506,117 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
|||
|
||||
#[cfg(feature = "openxr")]
|
||||
fn monado_client_focus(&mut self, app: &mut AppState, name: &str) -> anyhow::Result<()> {
|
||||
let Some(monado) = &mut app.monado else {
|
||||
return Ok(()); // no monado avoilable
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return Ok(()); // no monado available
|
||||
};
|
||||
|
||||
monado_client_focus(monado, name)
|
||||
monado_client_focus(&mut monado.ipc, name)
|
||||
}
|
||||
|
||||
#[cfg(feature = "openxr")]
|
||||
fn monado_brightness_get(&mut self, app: &mut AppState) -> Option<f32> {
|
||||
let Some(monado) = &mut app.monado else {
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return None;
|
||||
};
|
||||
|
||||
monado_get_brightness(monado)
|
||||
monado_get_brightness(&mut monado.ipc)
|
||||
}
|
||||
|
||||
#[cfg(feature = "openxr")]
|
||||
fn monado_brightness_set(&mut self, app: &mut AppState, brightness: f32) -> Option<()> {
|
||||
let Some(monado) = &mut app.monado else {
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return None;
|
||||
};
|
||||
|
||||
monado_set_brightness(monado, brightness).ok()
|
||||
monado_set_brightness(&mut monado.ipc, brightness).ok()
|
||||
}
|
||||
|
||||
#[cfg(feature = "openxr")]
|
||||
fn monado_metrics_set_enabled(&mut self, app: &mut AppState, enabled: bool) -> bool {
|
||||
#[cfg(feature = "feat-monado-metrics")]
|
||||
{
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if let Err(e) = monado.set_metrics_enabled(enabled) {
|
||||
log::error!("failed to enable metrics: {e:?}");
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
#[cfg(not(feature = "feat-monado-metrics"))]
|
||||
#[allow(path_statements)]
|
||||
{
|
||||
app;
|
||||
enabled;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "openxr")]
|
||||
#[allow(clippy::match_same_arms)]
|
||||
fn monado_metrics_dump_session_frames(
|
||||
&mut self,
|
||||
app: &mut AppState,
|
||||
) -> Vec<dash_interface::MonadoDumpSessionFrame> {
|
||||
#[cfg(feature = "feat-monado-metrics")]
|
||||
{
|
||||
let Some(monado) = &mut app.monado_state else {
|
||||
return Vec::new();
|
||||
};
|
||||
let Some(metrics) = &mut monado.metrics else {
|
||||
return Vec::new(); // metrics not enabled or not available
|
||||
};
|
||||
|
||||
metrics
|
||||
.dump_records()
|
||||
.iter()
|
||||
.filter_map(|record| {
|
||||
use crate::subsystem::monado_metrics::proto::record;
|
||||
let record = record.record?;
|
||||
match record {
|
||||
record::Record::SessionFrame(sframe) => {
|
||||
// map it to our struct
|
||||
Some(dash_interface::MonadoDumpSessionFrame {
|
||||
session_id: sframe.session_id,
|
||||
frame_id: sframe.frame_id,
|
||||
predicted_frame_time_ns: sframe.predicted_frame_time_ns,
|
||||
predicted_wake_up_time_ns: sframe.predicted_wake_up_time_ns,
|
||||
predicted_gpu_done_time_ns: sframe.predicted_gpu_done_time_ns,
|
||||
predicted_display_time_ns: sframe.predicted_display_time_ns,
|
||||
predicted_display_period_ns: sframe.predicted_display_period_ns,
|
||||
display_time_ns: sframe.display_time_ns,
|
||||
when_predicted_ns: sframe.when_predicted_ns,
|
||||
when_wait_woke_ns: sframe.when_wait_woke_ns,
|
||||
when_begin_ns: sframe.when_begin_ns,
|
||||
when_delivered_ns: sframe.when_delivered_ns,
|
||||
when_gpu_done_ns: sframe.when_gpu_done_ns,
|
||||
discarded: sframe.discarded,
|
||||
})
|
||||
}
|
||||
record::Record::Version(_) => None,
|
||||
record::Record::Used(_) => None,
|
||||
record::Record::SystemFrame(_) => None,
|
||||
record::Record::SystemGpuInfo(_) => None,
|
||||
record::Record::SystemPresentInfo(_) => None,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
#[cfg(not(feature = "feat-monado-metrics"))]
|
||||
#[allow(path_statements)]
|
||||
{
|
||||
app;
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "openxr"))]
|
||||
fn monado_client_list(
|
||||
&mut self,
|
||||
_: &mut AppState,
|
||||
_filtered: bool,
|
||||
) -> anyhow::Result<Vec<dash_interface::MonadoClient>> {
|
||||
anyhow::bail!("Not supported in this build.")
|
||||
}
|
||||
|
|
@ -542,6 +632,20 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
|||
fn monado_brightness_set(&mut self, _: &mut AppState, _: f32) -> Option<()> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "openxr"))]
|
||||
fn monado_metrics_set_enabled(&mut self, _: &mut AppState, _enabled: bool) -> bool {
|
||||
// not supported in this build
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "openxr"))]
|
||||
fn monado_metrics_dump_session_frames(
|
||||
&mut self,
|
||||
_: &mut AppState,
|
||||
) -> Vec<dash_interface::MonadoDumpSessionFrame> {
|
||||
Vec::new() // not supported in this build
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "openxr")]
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ type EditModeWrapPanel = GuiPanel<EditModeState>;
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct EditWrapperManager {
|
||||
#[allow(dead_code)]
|
||||
edit_mode: bool,
|
||||
panel_pool: Vec<EditModeWrapPanel>,
|
||||
}
|
||||
|
|
@ -371,9 +372,9 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
|||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
sel,
|
||||
Box::new(move |_app, owc| {
|
||||
Box::new(move |app, owc| {
|
||||
let attrib = BackendAttribValue::StereoFullFrame(full_frame);
|
||||
owc.backend.set_attrib(_app, attrib);
|
||||
owc.backend.set_attrib(app, attrib);
|
||||
}),
|
||||
)));
|
||||
Ok(EventResult::Consumed)
|
||||
|
|
@ -534,7 +535,7 @@ fn reset_panel(
|
|||
panel.state.stereo.reset(&mut common, &stereo);
|
||||
|
||||
// Set the checkbox label based on stereo mode
|
||||
let translation = get_stereo_full_frame_translation(&stereo);
|
||||
let translation = get_stereo_full_frame_translation(stereo);
|
||||
let c = panel
|
||||
.parser_state
|
||||
.fetch_component_as::<ComponentCheckbox>("stereo_full_frame_box")?;
|
||||
|
|
@ -636,23 +637,23 @@ const fn cb_assign_block_input(
|
|||
}
|
||||
|
||||
fn cb_assign_stereo_full_frame(
|
||||
_app: &mut AppState,
|
||||
app: &mut AppState,
|
||||
owc: &mut OverlayWindowConfig,
|
||||
full_frame: bool,
|
||||
) {
|
||||
owc.dirty = true;
|
||||
let attrib = BackendAttribValue::StereoFullFrame(full_frame);
|
||||
owc.backend.set_attrib(_app, attrib);
|
||||
owc.backend.set_attrib(app, attrib);
|
||||
}
|
||||
|
||||
fn cb_assign_stereo_adjust_mouse(
|
||||
_app: &mut AppState,
|
||||
app: &mut AppState,
|
||||
owc: &mut OverlayWindowConfig,
|
||||
adjust_mouse: bool,
|
||||
) {
|
||||
owc.dirty = true;
|
||||
let attrib = BackendAttribValue::StereoAdjustMouse(adjust_mouse);
|
||||
owc.backend.set_attrib(_app, attrib);
|
||||
owc.backend.set_attrib(app, attrib);
|
||||
}
|
||||
|
||||
fn set_up_slider(
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ pub trait SpriteTabKey {
|
|||
}
|
||||
|
||||
struct SpriteTabButtonState<S> {
|
||||
name: &'static str,
|
||||
sprite: CustomGlyphData,
|
||||
component: Rc<ComponentButton>,
|
||||
state: S,
|
||||
|
|
@ -69,7 +68,6 @@ where
|
|||
buttons.insert(
|
||||
*name,
|
||||
Rc::new(SpriteTabButtonState {
|
||||
name,
|
||||
sprite,
|
||||
component,
|
||||
state,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub fn new_stereo_tab_handler(
|
|||
Box::new(move |common, state| {
|
||||
let stereo = *state;
|
||||
|
||||
let translation = get_stereo_full_frame_translation(&stereo);
|
||||
let translation = get_stereo_full_frame_translation(stereo);
|
||||
checkbox.set_text(common, Translation::from_translation_key(translation));
|
||||
|
||||
Box::new(move |app, owc| {
|
||||
|
|
@ -59,7 +59,7 @@ impl SpriteTabKey for StereoMode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_stereo_full_frame_translation(stereo: &StereoMode) -> &'static str {
|
||||
pub const fn get_stereo_full_frame_translation(stereo: StereoMode) -> &'static str {
|
||||
match stereo {
|
||||
StereoMode::LeftRight | StereoMode::RightLeft => "EDIT_MODE.STEREO_3D_MODE.FULL_FRAME_SBS",
|
||||
StereoMode::TopBottom => "EDIT_MODE.STEREO_3D_MODE.FULL_FRAME_TAB",
|
||||
|
|
|
|||
|
|
@ -143,7 +143,6 @@ pub(self) fn hide_swipe_type_manager(panel: &mut GuiPanel<KeyboardState>) {
|
|||
let predictions_root = panel.parser_state
|
||||
.get_widget_id("swipe_predictions_root")
|
||||
.unwrap_or_default();
|
||||
|
||||
if !predictions_root.is_null() {
|
||||
panel.layout.tasks.push(LayoutTask::SetWidgetStyle(
|
||||
predictions_root,
|
||||
|
|
@ -151,7 +150,7 @@ pub(self) fn hide_swipe_type_manager(panel: &mut GuiPanel<KeyboardState>) {
|
|||
));
|
||||
}
|
||||
}
|
||||
fn alt_modifier_to_key(m: AltModifier) -> KeyModifier {
|
||||
const fn alt_modifier_to_key(m: AltModifier) -> KeyModifier {
|
||||
match m {
|
||||
AltModifier::Shift => SHIFT,
|
||||
AltModifier::Ctrl => CTRL,
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ use wlx_common::{
|
|||
|
||||
use super::capture::{ScreenPipeline, WlxCaptureIn, WlxCaptureOut, receive_callback};
|
||||
|
||||
const CURSOR_SIZE: f32 = 16. / 1440.;
|
||||
|
||||
static START: LazyLock<Instant> = LazyLock::new(Instant::now);
|
||||
static NEXT_MOVE: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
|
|
@ -404,7 +402,7 @@ impl OverlayBackend for ScreenBackend {
|
|||
}
|
||||
}
|
||||
|
||||
fn mouse_transform_to_transform(mouse_transform: MouseTransform) -> Transform {
|
||||
const fn mouse_transform_to_transform(mouse_transform: MouseTransform) -> Transform {
|
||||
match mouse_transform {
|
||||
MouseTransform::Default => Transform::Undefined,
|
||||
MouseTransform::Normal => Transform::Normal,
|
||||
|
|
|
|||
|
|
@ -95,7 +95,8 @@ pub(super) fn select_pw_screen(
|
|||
task::Poll::Pending => {
|
||||
if Instant::now() >= print_at {
|
||||
log::info!("{instructions}");
|
||||
if let Ok(id) = DbusConnector::notify_send(instructions, "", 1, 60, 0, true)
|
||||
if let Ok(id) =
|
||||
DbusConnector::notify_send(instructions, "", 1, 30000, 0, true)
|
||||
{
|
||||
notify = Some(id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ use crate::{
|
|||
windowing::{OverlaySelector, Z_ORDER_TOAST, window::OverlayWindowConfig},
|
||||
};
|
||||
|
||||
const FONT_SIZE: isize = 16;
|
||||
const PADDING: (f32, f32) = (25., 7.);
|
||||
const PIXELS_TO_METERS: f32 = 1. / 2000.;
|
||||
static TOAST_NAME: LazyLock<Arc<str>> = LazyLock::new(|| "toast".into());
|
||||
|
||||
|
|
@ -30,6 +28,7 @@ pub struct Toast {
|
|||
pub body: String,
|
||||
pub opacity: f32,
|
||||
pub timeout: f32,
|
||||
pub lerp_amount: f32,
|
||||
pub sound: bool,
|
||||
pub topic: ToastTopic,
|
||||
}
|
||||
|
|
@ -41,6 +40,7 @@ impl Toast {
|
|||
title,
|
||||
body,
|
||||
opacity: 1.0,
|
||||
lerp_amount: 0.1,
|
||||
timeout: 3.0,
|
||||
sound: false,
|
||||
topic,
|
||||
|
|
@ -50,6 +50,10 @@ impl Toast {
|
|||
self.timeout = timeout;
|
||||
self
|
||||
}
|
||||
pub const fn with_lerp_amount(mut self, lerp: f32) -> Self {
|
||||
self.lerp_amount = lerp;
|
||||
self
|
||||
}
|
||||
pub const fn with_opacity(mut self, opacity: f32) -> Self {
|
||||
self.opacity = opacity;
|
||||
self
|
||||
|
|
@ -118,7 +122,9 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
|||
ToastDisplayMethod::Center => (
|
||||
vec3(0., -0.2, -0.5),
|
||||
Quat::IDENTITY,
|
||||
Positioning::FollowHead { lerp: 0.1 },
|
||||
Positioning::FollowHead {
|
||||
lerp: toast.lerp_amount,
|
||||
},
|
||||
),
|
||||
ToastDisplayMethod::Watch => {
|
||||
let relative_to = Positioning::FollowHand {
|
||||
|
|
@ -202,6 +208,9 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
|||
})
|
||||
}
|
||||
|
||||
// FIXME: Will these functions be used in the future? Can they be removed?
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn msg_err(app: &mut AppState, message: &str) {
|
||||
Toast::new(ToastTopic::Error, "TOAST.ERROR".into(), message.into())
|
||||
.with_timeout(3.)
|
||||
|
|
@ -210,6 +219,7 @@ fn msg_err(app: &mut AppState, message: &str) {
|
|||
|
||||
// Display the same error in the terminal and as a toast in VR.
|
||||
// Formatted as "Failed to XYZ: Object is not defined"
|
||||
#[allow(dead_code)]
|
||||
pub fn error_toast<ErrorType>(app: &mut AppState, title: &str, err: ErrorType)
|
||||
where
|
||||
ErrorType: std::fmt::Display + std::fmt::Debug,
|
||||
|
|
@ -220,6 +230,7 @@ where
|
|||
msg_err(app, &format!("{title}: {err}"));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn error_toast_str(app: &mut AppState, message: &str) {
|
||||
log::error!("{message}");
|
||||
msg_err(app, message);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use std::{rc::Rc, time::Duration};
|
||||
use std::time::Duration;
|
||||
|
||||
use glam::{Affine3A, Quat, Vec3, vec3};
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::button::ComponentButton,
|
||||
event::{CallbackDataCommon, EventAlterables, StyleSetRequest},
|
||||
layout::WidgetID,
|
||||
parser::{Fetchable, ParseDocumentParams},
|
||||
taffy,
|
||||
};
|
||||
|
|
@ -27,23 +26,12 @@ use crate::{
|
|||
};
|
||||
|
||||
pub const WATCH_NAME: &str = "watch";
|
||||
const MAX_TOOLBOX_BUTTONS: usize = 16;
|
||||
const MAX_DEVICES: usize = 12;
|
||||
|
||||
pub const WATCH_POS: Vec3 = vec3(-0.03, -0.01, 0.125);
|
||||
pub const WATCH_ROT: Quat = Quat::from_xyzw(-0.707_106_6, 0.000_796_361_8, 0.707_106_6, 0.0);
|
||||
|
||||
struct OverlayButton {
|
||||
button: Rc<ComponentButton>,
|
||||
label: WidgetID,
|
||||
sprite: WidgetID,
|
||||
condensed: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct WatchState {
|
||||
edit_mode_widgets: Vec<(WidgetID, bool)>,
|
||||
edit_add_widget: WidgetID,
|
||||
device_list: DeviceList,
|
||||
overlay_list: OverlayList,
|
||||
set_list: SetList,
|
||||
|
|
|
|||
|
|
@ -545,7 +545,7 @@ impl OverlayBackend for WvrWindowBackend {
|
|||
return;
|
||||
};
|
||||
|
||||
let mut hit2 = hit.clone();
|
||||
let mut hit2 = *hit;
|
||||
hit2.uv.y *= meta.extent[1] as f32 / (meta.extent[1] - self.inner_extent[1]) as f32;
|
||||
self.panel_hovered = true;
|
||||
return self.panel.on_pointer(app, &hit2, pressed);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@
|
|||
"type": "boolean",
|
||||
"requirement": "optional"
|
||||
},
|
||||
{
|
||||
"name": "/actions/default/in/SpaceReset",
|
||||
"type": "boolean",
|
||||
"requirement": "optional"
|
||||
},
|
||||
{
|
||||
"name": "/actions/default/in/LeftHand",
|
||||
"type": "pose",
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue