Merge remote-tracking branch 'origin/main' into next-dash-interface
[skip ci]
This commit is contained in:
146
Cargo.lock
generated
146
Cargo.lock
generated
@@ -2890,8 +2890,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d"
|
||||
dependencies = [
|
||||
"doctest-file",
|
||||
"futures-core",
|
||||
"libc",
|
||||
"recvmsg",
|
||||
"tokio",
|
||||
"widestring",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
@@ -3343,6 +3345,17 @@ version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moveit"
|
||||
version = "0.6.0"
|
||||
@@ -5236,6 +5249,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
@@ -5639,6 +5662,45 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.2"
|
||||
@@ -6299,16 +6361,18 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayvr_ipc"
|
||||
name = "wayvr-ipc"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/olekolek1000/wayvr-ipc.git?rev=6d253ef9e36db0f181566030a4990454ecb60395#6d253ef9e36db0f181566030a4990454ecb60395"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"interprocess",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6608,6 +6672,15 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
@@ -6641,13 +6714,30 @@ dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link 0.2.1",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
@@ -6669,6 +6759,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -6681,6 +6777,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
@@ -6693,12 +6795,24 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -6711,6 +6825,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
@@ -6723,6 +6843,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
@@ -6735,6 +6861,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -6747,6 +6879,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.30.12"
|
||||
@@ -6903,7 +7041,7 @@ dependencies = [
|
||||
"vulkano-shaders",
|
||||
"wayland-client",
|
||||
"wayland-egl",
|
||||
"wayvr_ipc",
|
||||
"wayvr-ipc",
|
||||
"wgui",
|
||||
"winit",
|
||||
"wlx-capture",
|
||||
|
||||
@@ -17,6 +17,7 @@ members = [
|
||||
"wlx-overlay-s",
|
||||
"wlx-capture",
|
||||
"dash-frontend",
|
||||
"wayvr-ipc",
|
||||
]
|
||||
resolver = "3"
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
"HEADSET_SETTINGS": "ヘッドセット設定",
|
||||
"BRIGHTNESS": "明るさ",
|
||||
"WLX": {
|
||||
"NOTIFICATIONS_ENABLED": "通知が有効",
|
||||
"NOTIFICATIONS_SOUND_ENABLED": "通知音を有効にする",
|
||||
"KEYBOARD_SOUND_ENABLED": "キーボード音を有効にする",
|
||||
"NOTIFICATIONS_ENABLED": "通知",
|
||||
"NOTIFICATIONS_SOUND_ENABLED": "通知音",
|
||||
"KEYBOARD_SOUND_ENABLED": "キーボード音",
|
||||
"BLOCK_GAME_INPUT": "ゲーム入力をブロック",
|
||||
"SPACE_DRAG_MULTIPLIER": "スペースドラッグ乗数",
|
||||
"SPACE_DRAG_ROTATION_ENABLED": "スペースドラッグでの回転を有効にする",
|
||||
"SHOW_SKYBOX": "スカイボックスを表示",
|
||||
"ENABLE_PASSTHROUGH": "Passthroughを有効にする"
|
||||
"SPACE_DRAG_ROTATION_ENABLED": "スペースドラッグでの回転",
|
||||
"SHOW_SKYBOX": "スカイボックス",
|
||||
"ENABLE_PASSTHROUGH": "パススルー"
|
||||
},
|
||||
"RESTART_SOFTWARE": "ソフトウェアを再起動"
|
||||
},
|
||||
@@ -64,4 +64,4 @@
|
||||
"NO_PROCESSES_FOUND": "プロセスが見つかりませんでした",
|
||||
"LOCATED_ON": "に"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ impl ToastManager {
|
||||
// show-up animation
|
||||
layout.animations.add(Animation::new(
|
||||
rect.id,
|
||||
160,
|
||||
160, // does not use anim_mult
|
||||
AnimationEasing::Linear,
|
||||
Box::new(move |common, data| {
|
||||
let pos_showup = AnimationEasing::OutQuint.interpolate((data.pos * 4.0).min(1.0));
|
||||
|
||||
@@ -4,7 +4,7 @@ cd "$(dirname "$0")"
|
||||
|
||||
bun install
|
||||
|
||||
export MODEL="gemma3:12b"
|
||||
export MODEL="gemma3:27b"
|
||||
|
||||
TEMPLATE="pl" bun main.ts
|
||||
TEMPLATE="de" bun main.ts
|
||||
|
||||
390
wayvr-ipc/Cargo.lock
generated
Normal file
390
wayvr-ipc/Cargo.lock
generated
Normal file
@@ -0,0 +1,390 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "doctest-file"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "interprocess"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb"
|
||||
dependencies = [
|
||||
"doctest-file",
|
||||
"futures-core",
|
||||
"libc",
|
||||
"recvmsg",
|
||||
"tokio",
|
||||
"widestring",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "recvmsg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.43.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "492a604e2fd7f814268a378409e6c92b5525d747d10db9a229723f55a417958c"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wayvr-ipc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"interprocess",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
21
wayvr-ipc/Cargo.toml
Normal file
21
wayvr-ipc/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "wayvr-ipc"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.9.0"
|
||||
smallvec = "1.13.2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
anyhow = "1.0.93"
|
||||
log = "0.4.22"
|
||||
|
||||
# client-only deps
|
||||
interprocess = { version = "2.2.2", features = ["tokio"], optional = true }
|
||||
tokio = { version = "1.43.1", features = ["macros"], optional = true }
|
||||
tokio-util = { version = "0.7.13", optional = true }
|
||||
serde_json = "1.0.135"
|
||||
|
||||
[features]
|
||||
default = ["client"]
|
||||
client = ["dep:tokio", "dep:tokio-util", "dep:interprocess"]
|
||||
21
wayvr-ipc/LICENSE.txt
Normal file
21
wayvr-ipc/LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Aleksander <aleksander@oo8.dev>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
9
wayvr-ipc/README.md
Normal file
9
wayvr-ipc/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# WayVR Protocol Specification & Client Implementation
|
||||
|
||||
This repository contains the IPC specification and client implementation for the WayVR Server protocol (which is used in wlx-overlay-s). 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/galister/wlx-overlay-s/tree/main/contrib/wayvr)
|
||||
|
||||
## Usage
|
||||
|
||||
[Example WayVR IPC Client usage in WayVR Dashboard](https://github.com/olekolek1000/wayvr-dashboard/blob/master/src-tauri/src/frontend_ipc.rs)
|
||||
2
wayvr-ipc/rustfmt.toml
Normal file
2
wayvr-ipc/rustfmt.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
tab_spaces = 2
|
||||
hard_tabs = true
|
||||
496
wayvr-ipc/src/client.rs
Normal file
496
wayvr-ipc/src/client.rs
Normal file
@@ -0,0 +1,496 @@
|
||||
use bytes::BufMut;
|
||||
use interprocess::local_socket::{
|
||||
self,
|
||||
tokio::{prelude::*, Stream},
|
||||
GenericNamespaced,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use smallvec::SmallVec;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
sync::Mutex,
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{
|
||||
gen_id,
|
||||
ipc::{self, Serial},
|
||||
packet_client::{self, PacketClient},
|
||||
packet_server::{self, PacketServer},
|
||||
util::notifier::Notifier,
|
||||
};
|
||||
|
||||
pub struct QueuedPacket {
|
||||
notifier: Notifier,
|
||||
serial: Serial,
|
||||
packet: Option<PacketServer>,
|
||||
}
|
||||
|
||||
gen_id!(
|
||||
QueuedPacketVec,
|
||||
QueuedPacket,
|
||||
QueuedPacketCell,
|
||||
QueuedPacketHandle
|
||||
);
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
pub struct AuthInfo {
|
||||
pub runtime: String,
|
||||
}
|
||||
|
||||
type SignalFunc = Box<dyn FnMut(&packet_server::PacketServer) -> bool + Send>;
|
||||
|
||||
pub struct WayVRClient {
|
||||
receiver: ReceiverMutex,
|
||||
sender: SenderMutex,
|
||||
cancel_token: CancellationToken,
|
||||
exiting: bool,
|
||||
queued_packets: QueuedPacketVec,
|
||||
pub auth: Option<AuthInfo>, // Some if authenticated
|
||||
pub on_signal: Option<SignalFunc>,
|
||||
}
|
||||
|
||||
pub async fn send_packet(sender: &SenderMutex, data: &[u8]) -> anyhow::Result<()> {
|
||||
let mut bytes = bytes::BytesMut::new();
|
||||
|
||||
// packet size
|
||||
bytes.put_u32(data.len() as u32);
|
||||
|
||||
// packet data
|
||||
bytes.put_slice(data);
|
||||
|
||||
sender.lock().await.write_all(&bytes).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub type WayVRClientMutex = Arc<Mutex<WayVRClient>>;
|
||||
pub type WayVRClientWeak = Weak<Mutex<WayVRClient>>;
|
||||
|
||||
type ReceiverMutex = Arc<Mutex<local_socket::tokio::RecvHalf>>;
|
||||
type SenderMutex = Arc<Mutex<local_socket::tokio::SendHalf>>;
|
||||
|
||||
async fn client_runner(client: WayVRClientMutex) -> anyhow::Result<()> {
|
||||
loop {
|
||||
WayVRClient::tick(client.clone()).await?;
|
||||
}
|
||||
}
|
||||
|
||||
type Payload = SmallVec<[u8; 64]>;
|
||||
|
||||
async fn read_payload(
|
||||
conn: &mut local_socket::tokio::RecvHalf,
|
||||
size: u32,
|
||||
) -> anyhow::Result<Payload> {
|
||||
let mut payload = Payload::new();
|
||||
payload.resize(size as usize, 0);
|
||||
conn.read_exact(&mut payload).await?;
|
||||
Ok(payload)
|
||||
}
|
||||
|
||||
macro_rules! bail_unexpected_response {
|
||||
() => {
|
||||
anyhow::bail!("unexpected response");
|
||||
};
|
||||
}
|
||||
|
||||
// Send and wait for a response from the server
|
||||
macro_rules! send_and_wait {
|
||||
($client_mtx:expr, $serial:expr, $packet_to_send:expr, $expected_response_type:ident) => {{
|
||||
let result =
|
||||
WayVRClient::queue_wait_packet($client_mtx, $serial, &ipc::data_encode($packet_to_send))
|
||||
.await?;
|
||||
if let PacketServer::$expected_response_type(_, res) = result {
|
||||
res
|
||||
} else {
|
||||
bail_unexpected_response!();
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// Send without expecting any response
|
||||
macro_rules! send_only {
|
||||
($client_mtx:expr, $packet_to_send:expr) => {{
|
||||
WayVRClient::send_payload($client_mtx, &ipc::data_encode($packet_to_send)).await?;
|
||||
}};
|
||||
}
|
||||
|
||||
impl WayVRClient {
|
||||
pub fn set_signal_handler(&mut self, on_signal: SignalFunc) {
|
||||
self.on_signal = Some(on_signal);
|
||||
}
|
||||
|
||||
pub async fn new(client_name: &str) -> anyhow::Result<WayVRClientMutex> {
|
||||
let printname = "/tmp/wayvr_ipc.sock";
|
||||
let name = printname.to_ns_name::<GenericNamespaced>()?;
|
||||
|
||||
let stream = match Stream::connect(name).await {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
anyhow::bail!("Failed to connect to the WayVR IPC: {}", e)
|
||||
}
|
||||
};
|
||||
let (receiver, sender) = stream.split();
|
||||
|
||||
let receiver = Arc::new(Mutex::new(receiver));
|
||||
let sender = Arc::new(Mutex::new(sender));
|
||||
|
||||
let cancel_token = CancellationToken::new();
|
||||
|
||||
let client = Arc::new(Mutex::new(Self {
|
||||
receiver,
|
||||
sender: sender.clone(),
|
||||
exiting: false,
|
||||
cancel_token: cancel_token.clone(),
|
||||
queued_packets: QueuedPacketVec::new(),
|
||||
auth: None,
|
||||
on_signal: None,
|
||||
}));
|
||||
|
||||
WayVRClient::start_runner(client.clone(), cancel_token);
|
||||
|
||||
// Send handshake to the server
|
||||
send_packet(
|
||||
&sender,
|
||||
&ipc::data_encode(&PacketClient::Handshake(packet_client::Handshake {
|
||||
client_name: String::from(client_name),
|
||||
magic: String::from(ipc::CONNECTION_MAGIC),
|
||||
protocol_version: ipc::PROTOCOL_VERSION,
|
||||
})),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
fn start_runner(client: WayVRClientMutex, cancel_token: CancellationToken) {
|
||||
tokio::spawn(async move {
|
||||
tokio::select! {
|
||||
_ = cancel_token.cancelled() => {
|
||||
log::info!("Exiting IPC runner gracefully");
|
||||
}
|
||||
e = client_runner(client.clone()) => {
|
||||
log::info!("IPC Runner failed: {:?}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn tick(client_mtx: WayVRClientMutex) -> anyhow::Result<()> {
|
||||
let receiver = {
|
||||
let client = client_mtx.lock().await;
|
||||
client.receiver.clone()
|
||||
};
|
||||
|
||||
// read packet
|
||||
let packet = {
|
||||
let mut receiver = receiver.lock().await;
|
||||
let packet_size = receiver.read_u32().await?;
|
||||
log::trace!("packet size {}", packet_size);
|
||||
if packet_size > 128 * 1024 {
|
||||
anyhow::bail!("packet size too large (> 128 KiB)");
|
||||
}
|
||||
let payload = read_payload(&mut receiver, packet_size).await?;
|
||||
let packet: PacketServer = ipc::data_decode(&payload)?;
|
||||
packet
|
||||
};
|
||||
|
||||
{
|
||||
let mut client = client_mtx.lock().await;
|
||||
|
||||
if let PacketServer::HandshakeSuccess(success) = &packet {
|
||||
if client.auth.is_some() {
|
||||
anyhow::bail!("Got handshake response twice");
|
||||
}
|
||||
|
||||
client.auth = Some(AuthInfo {
|
||||
runtime: success.runtime.clone(),
|
||||
});
|
||||
|
||||
log::info!(
|
||||
"Authenticated. Server runtime name: \"{}\"",
|
||||
success.runtime
|
||||
);
|
||||
}
|
||||
|
||||
if let PacketServer::Disconnect(disconnect) = &packet {
|
||||
anyhow::bail!("Server disconnected us. Reason: {}", disconnect.reason);
|
||||
}
|
||||
|
||||
if client.auth.is_none() {
|
||||
anyhow::bail!(
|
||||
"Server tried to send us a packet which is not a HandshakeSuccess or Disconnect"
|
||||
);
|
||||
}
|
||||
|
||||
if let PacketServer::WvrStateChanged(_) = &packet {
|
||||
if let Some(on_signal) = &mut client.on_signal {
|
||||
if (*on_signal)(&packet) {
|
||||
// Signal consumed
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// queue packet to read if it contains a serial response
|
||||
if let Some(serial) = packet.serial() {
|
||||
for qpacket in &mut client.queued_packets.vec {
|
||||
let Some(qpacket) = qpacket else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let qpacket = &mut qpacket.obj;
|
||||
if qpacket.serial != *serial {
|
||||
continue; //skip
|
||||
}
|
||||
|
||||
// found response serial, fill it and notify the receiver
|
||||
qpacket.packet = Some(packet);
|
||||
let notifier = qpacket.notifier.clone();
|
||||
|
||||
drop(client);
|
||||
notifier.notify();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Send packet without feedback
|
||||
async fn send_payload(client_mtx: WayVRClientMutex, payload: &[u8]) -> anyhow::Result<()> {
|
||||
let client = client_mtx.lock().await;
|
||||
let sender = client.sender.clone();
|
||||
drop(client);
|
||||
send_packet(&sender, payload).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn queue_wait_packet(
|
||||
client_mtx: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
payload: &[u8],
|
||||
) -> anyhow::Result<PacketServer> {
|
||||
let notifier = Notifier::new();
|
||||
|
||||
// Send packet to the server
|
||||
let queued_packet_handle = {
|
||||
let mut client = client_mtx.lock().await;
|
||||
let handle = client.queued_packets.add(QueuedPacket {
|
||||
notifier: notifier.clone(),
|
||||
packet: None, // will be filled after notify
|
||||
serial,
|
||||
});
|
||||
|
||||
let sender = client.sender.clone();
|
||||
|
||||
drop(client);
|
||||
|
||||
send_packet(&sender, payload).await?;
|
||||
handle
|
||||
};
|
||||
|
||||
// Wait for response message
|
||||
notifier.wait().await;
|
||||
|
||||
// Fetch response packet
|
||||
{
|
||||
let mut client = client_mtx.lock().await;
|
||||
|
||||
let cell = client
|
||||
.queued_packets
|
||||
.get_mut(&queued_packet_handle)
|
||||
.ok_or(anyhow::anyhow!(
|
||||
"missing packet cell, this shouldn't happen"
|
||||
))?;
|
||||
|
||||
let Some(packet) = cell.packet.take() else {
|
||||
anyhow::bail!("packet is None, this shouldn't happen");
|
||||
};
|
||||
|
||||
client.queued_packets.remove(&queued_packet_handle);
|
||||
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_display_list(
|
||||
client: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
) -> anyhow::Result<Vec<packet_server::WvrDisplay>> {
|
||||
Ok(
|
||||
send_and_wait!(
|
||||
client,
|
||||
serial,
|
||||
&PacketClient::WvrDisplayList(serial),
|
||||
WvrDisplayListResponse
|
||||
)
|
||||
.list,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_display_get(
|
||||
client: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
handle: packet_server::WvrDisplayHandle,
|
||||
) -> anyhow::Result<Option<packet_server::WvrDisplay>> {
|
||||
Ok(send_and_wait!(
|
||||
client,
|
||||
serial,
|
||||
&PacketClient::WvrDisplayGet(serial, handle),
|
||||
WvrDisplayGetResponse
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_display_remove(
|
||||
client: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
handle: packet_server::WvrDisplayHandle,
|
||||
) -> anyhow::Result<()> {
|
||||
send_and_wait!(
|
||||
client,
|
||||
serial,
|
||||
&PacketClient::WvrDisplayRemove(serial, handle),
|
||||
WvrDisplayRemoveResponse
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_display_create(
|
||||
client: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
params: packet_client::WvrDisplayCreateParams,
|
||||
) -> anyhow::Result<packet_server::WvrDisplayHandle> {
|
||||
Ok(send_and_wait!(
|
||||
client,
|
||||
serial,
|
||||
&PacketClient::WvrDisplayCreate(serial, params),
|
||||
WvrDisplayCreateResponse
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_display_set_visible(
|
||||
client: WayVRClientMutex,
|
||||
handle: packet_server::WvrDisplayHandle,
|
||||
visible: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
send_only!(client, &PacketClient::WvrDisplaySetVisible(handle, visible));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_display_set_layout(
|
||||
client: WayVRClientMutex,
|
||||
handle: packet_server::WvrDisplayHandle,
|
||||
layout: packet_server::WvrDisplayWindowLayout,
|
||||
) -> anyhow::Result<()> {
|
||||
send_only!(
|
||||
client,
|
||||
&PacketClient::WvrDisplaySetWindowLayout(handle, layout)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_display_window_list(
|
||||
client: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
handle: packet_server::WvrDisplayHandle,
|
||||
) -> anyhow::Result<Option<Vec<packet_server::WvrWindow>>> {
|
||||
Ok(
|
||||
send_and_wait!(
|
||||
client,
|
||||
serial,
|
||||
&PacketClient::WvrDisplayWindowList(serial, handle),
|
||||
WvrDisplayWindowListResponse
|
||||
)
|
||||
.map(|res| res.list),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_window_set_visible(
|
||||
client: WayVRClientMutex,
|
||||
handle: packet_server::WvrWindowHandle,
|
||||
visible: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
send_only!(client, &PacketClient::WvrWindowSetVisible(handle, visible));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_process_list(
|
||||
client: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
) -> anyhow::Result<Vec<packet_server::WvrProcess>> {
|
||||
Ok(
|
||||
send_and_wait!(
|
||||
client,
|
||||
serial,
|
||||
&PacketClient::WvrProcessList(serial),
|
||||
WvrProcessListResponse
|
||||
)
|
||||
.list,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_process_get(
|
||||
client: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
handle: packet_server::WvrProcessHandle,
|
||||
) -> anyhow::Result<Option<packet_server::WvrProcess>> {
|
||||
Ok(send_and_wait!(
|
||||
client,
|
||||
serial,
|
||||
&PacketClient::WvrProcessGet(serial, handle),
|
||||
WvrProcessGetResponse
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_process_terminate(
|
||||
client: WayVRClientMutex,
|
||||
handle: packet_server::WvrProcessHandle,
|
||||
) -> anyhow::Result<()> {
|
||||
send_only!(client, &PacketClient::WvrProcessTerminate(handle));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fn_wvr_process_launch(
|
||||
client: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
params: packet_client::WvrProcessLaunchParams,
|
||||
) -> anyhow::Result<packet_server::WvrProcessHandle> {
|
||||
send_and_wait!(
|
||||
client,
|
||||
serial,
|
||||
&PacketClient::WvrProcessLaunch(serial, params),
|
||||
WvrProcessLaunchResponse
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))
|
||||
}
|
||||
|
||||
pub async fn fn_wlx_input_state(
|
||||
client: WayVRClientMutex,
|
||||
serial: Serial,
|
||||
) -> anyhow::Result<packet_server::WlxInputState> {
|
||||
Ok(send_and_wait!(
|
||||
client,
|
||||
serial,
|
||||
&PacketClient::WlxInputState(serial),
|
||||
WlxInputStateResponse
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn fn_wlx_haptics(
|
||||
client: WayVRClientMutex,
|
||||
params: packet_client::WlxHapticsParams,
|
||||
) -> anyhow::Result<()> {
|
||||
send_only!(client, &PacketClient::WlxHaptics(params));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WayVRClient {
|
||||
fn drop(&mut self) {
|
||||
self.exiting = true;
|
||||
self.cancel_token.cancel();
|
||||
}
|
||||
}
|
||||
42
wayvr-ipc/src/ipc.rs
Normal file
42
wayvr-ipc/src/ipc.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use std::sync::{Arc, Mutex as SyncMutex};
|
||||
|
||||
pub type Serial = u64;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SerialGenerator {
|
||||
serial: Arc<SyncMutex<u64>>,
|
||||
}
|
||||
|
||||
impl SerialGenerator {
|
||||
pub fn new() -> SerialGenerator {
|
||||
Self {
|
||||
serial: Arc::new(SyncMutex::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment_get(&self) -> Serial {
|
||||
let mut serial = self.serial.lock().unwrap();
|
||||
let cur = *serial;
|
||||
*serial += 1;
|
||||
cur
|
||||
}
|
||||
}
|
||||
|
||||
pub const PROTOCOL_VERSION: u32 = 3;
|
||||
pub const CONNECTION_MAGIC: &str = "wayvr_ipc";
|
||||
|
||||
pub fn data_encode<T>(data: &T) -> Vec<u8>
|
||||
where
|
||||
T: serde::Serialize,
|
||||
{
|
||||
let str = serde_json::to_string(&data).unwrap();
|
||||
log::debug!("serialized data: {}", str);
|
||||
str.into_bytes()
|
||||
}
|
||||
|
||||
pub fn data_decode<T>(data: &[u8]) -> anyhow::Result<T>
|
||||
where
|
||||
T: for<'a> serde::Deserialize<'a>,
|
||||
{
|
||||
Ok(serde_json::from_str::<T>(std::str::from_utf8(data)?)?)
|
||||
}
|
||||
7
wayvr-ipc/src/lib.rs
Normal file
7
wayvr-ipc/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub mod ipc;
|
||||
pub mod packet_client;
|
||||
pub mod packet_server;
|
||||
mod util;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub mod client;
|
||||
71
wayvr-ipc/src/packet_client.rs
Normal file
71
wayvr-ipc/src/packet_client.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
// Contents of this file should be the same as on wlx-overlay-s.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ipc::Serial, packet_server};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Handshake {
|
||||
pub protocol_version: u32, // always set to PROTOCOL_VERSION
|
||||
pub magic: String, // always set to CONNECTION_MAGIC
|
||||
pub client_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum AttachTo {
|
||||
None,
|
||||
HandLeft,
|
||||
HandRight,
|
||||
Head,
|
||||
Stage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct WvrProcessLaunchParams {
|
||||
pub name: String,
|
||||
pub exec: String,
|
||||
pub target_display: packet_server::WvrDisplayHandle,
|
||||
pub env: Vec<String>,
|
||||
pub args: String,
|
||||
pub userdata: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct WvrDisplayCreateParams {
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
pub name: String,
|
||||
pub scale: Option<f32>,
|
||||
pub attach_to: AttachTo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct WlxHapticsParams {
|
||||
pub intensity: f32,
|
||||
pub duration: f32,
|
||||
pub frequency: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum PacketClient {
|
||||
Handshake(Handshake),
|
||||
WvrDisplayCreate(Serial, WvrDisplayCreateParams),
|
||||
WvrDisplayGet(Serial, packet_server::WvrDisplayHandle),
|
||||
WvrDisplayList(Serial),
|
||||
WvrDisplayRemove(Serial, packet_server::WvrDisplayHandle),
|
||||
WvrDisplaySetVisible(packet_server::WvrDisplayHandle, bool),
|
||||
WvrDisplayWindowList(Serial, packet_server::WvrDisplayHandle),
|
||||
WvrDisplaySetWindowLayout(
|
||||
packet_server::WvrDisplayHandle,
|
||||
packet_server::WvrDisplayWindowLayout,
|
||||
),
|
||||
WvrWindowSetVisible(packet_server::WvrWindowHandle, bool),
|
||||
WvrProcessGet(Serial, packet_server::WvrProcessHandle),
|
||||
WvrProcessLaunch(Serial, WvrProcessLaunchParams),
|
||||
WvrProcessList(Serial),
|
||||
WvrProcessTerminate(packet_server::WvrProcessHandle),
|
||||
WlxHaptics(WlxHapticsParams),
|
||||
WlxInputState(Serial),
|
||||
}
|
||||
163
wayvr-ipc/src/packet_server.rs
Normal file
163
wayvr-ipc/src/packet_server.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
// Contents of this file should be the same as on wlx-overlay-s.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::ipc::Serial;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ServerInfo {}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HandshakeSuccess {
|
||||
pub runtime: String, // Runtime name, for example "wlx-overlay-s"
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Disconnect {
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct WvrDisplayHandle {
|
||||
pub idx: u32,
|
||||
pub generation: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct WvrProcessHandle {
|
||||
pub idx: u32,
|
||||
pub generation: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct WvrWindowHandle {
|
||||
pub idx: u32,
|
||||
pub generation: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WvrDisplay {
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
pub name: String,
|
||||
pub visible: bool,
|
||||
pub handle: WvrDisplayHandle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WvrWindow {
|
||||
pub pos_x: i32,
|
||||
pub pos_y: i32,
|
||||
pub size_x: u32,
|
||||
pub size_y: u32,
|
||||
pub visible: bool,
|
||||
pub handle: WvrWindowHandle,
|
||||
pub process_handle: WvrProcessHandle,
|
||||
pub display_handle: WvrDisplayHandle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WvrDisplayList {
|
||||
pub list: Vec<WvrDisplay>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WvrWindowList {
|
||||
pub list: Vec<WvrWindow>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WvrProcess {
|
||||
pub name: String,
|
||||
pub display_handle: WvrDisplayHandle,
|
||||
pub handle: WvrProcessHandle,
|
||||
pub userdata: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WvrProcessList {
|
||||
pub list: Vec<WvrProcess>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Margins {
|
||||
pub left: u16,
|
||||
pub right: u16,
|
||||
pub top: u16,
|
||||
pub bottom: u16,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct StackingOptions {
|
||||
pub margins_first: Margins,
|
||||
pub margins_rest: Margins,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub enum WvrDisplayWindowLayout {
|
||||
Tiling,
|
||||
Stacking(StackingOptions),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub enum WvrStateChanged {
|
||||
DisplayCreated,
|
||||
DisplayRemoved,
|
||||
ProcessCreated,
|
||||
ProcessRemoved,
|
||||
WindowCreated,
|
||||
WindowRemoved,
|
||||
DashboardShown,
|
||||
DashboardHidden,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct WlxInputStatePointer {
|
||||
pub pos: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct WlxInputState {
|
||||
pub hmd_pos: [f32; 3],
|
||||
pub left: WlxInputStatePointer,
|
||||
pub right: WlxInputStatePointer,
|
||||
}
|
||||
|
||||
// "Wvr" prefixes are WayVR-specific
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum PacketServer {
|
||||
Disconnect(Disconnect),
|
||||
HandshakeSuccess(HandshakeSuccess),
|
||||
WlxInputStateResponse(Serial, WlxInputState),
|
||||
WvrDisplayCreateResponse(Serial, WvrDisplayHandle),
|
||||
WvrDisplayGetResponse(Serial, Option<WvrDisplay>),
|
||||
WvrDisplayListResponse(Serial, WvrDisplayList),
|
||||
WvrDisplayRemoveResponse(Serial, Result<(), String>),
|
||||
WvrDisplayWindowListResponse(Serial, Option<WvrWindowList>),
|
||||
WvrProcessGetResponse(Serial, Option<WvrProcess>),
|
||||
WvrProcessLaunchResponse(Serial, Result<WvrProcessHandle, String>),
|
||||
WvrProcessListResponse(Serial, WvrProcessList),
|
||||
WvrStateChanged(WvrStateChanged),
|
||||
}
|
||||
|
||||
impl PacketServer {
|
||||
pub fn serial(&self) -> Option<&Serial> {
|
||||
match self {
|
||||
PacketServer::Disconnect(_) => None,
|
||||
PacketServer::HandshakeSuccess(_) => None,
|
||||
PacketServer::WlxInputStateResponse(serial, _) => Some(serial),
|
||||
PacketServer::WvrDisplayCreateResponse(serial, _) => Some(serial),
|
||||
PacketServer::WvrDisplayGetResponse(serial, _) => Some(serial),
|
||||
PacketServer::WvrDisplayListResponse(serial, _) => Some(serial),
|
||||
PacketServer::WvrDisplayRemoveResponse(serial, _) => Some(serial),
|
||||
PacketServer::WvrDisplayWindowListResponse(serial, _) => Some(serial),
|
||||
PacketServer::WvrProcessGetResponse(serial, _) => Some(serial),
|
||||
PacketServer::WvrProcessLaunchResponse(serial, _) => Some(serial),
|
||||
PacketServer::WvrProcessListResponse(serial, _) => Some(serial),
|
||||
PacketServer::WvrStateChanged(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
173
wayvr-ipc/src/util/handle.rs
Normal file
173
wayvr-ipc/src/util/handle.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
#[macro_export]
|
||||
macro_rules! gen_id {
|
||||
(
|
||||
$container_name:ident,
|
||||
$instance_name:ident,
|
||||
$cell_name:ident,
|
||||
$handle_name:ident) => {
|
||||
//ThingCell
|
||||
pub struct $cell_name {
|
||||
pub obj: $instance_name,
|
||||
pub generation: u64,
|
||||
}
|
||||
|
||||
//ThingVec
|
||||
pub struct $container_name {
|
||||
// Vec<Option<ThingCell>>
|
||||
pub vec: Vec<Option<$cell_name>>,
|
||||
|
||||
cur_generation: u64,
|
||||
}
|
||||
|
||||
//ThingHandle
|
||||
#[derive(Default, Clone, Copy, PartialEq, Hash, Eq)]
|
||||
pub struct $handle_name {
|
||||
idx: u32,
|
||||
generation: u64,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl $handle_name {
|
||||
pub fn reset(&mut self) {
|
||||
self.generation = 0;
|
||||
}
|
||||
|
||||
pub fn is_set(&self) -> bool {
|
||||
self.generation > 0
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
self.idx
|
||||
}
|
||||
|
||||
pub fn new(idx: u32, generation: u64) -> Self {
|
||||
Self { idx, generation }
|
||||
}
|
||||
}
|
||||
|
||||
//ThingVec
|
||||
#[allow(dead_code)]
|
||||
impl $container_name {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vec: Vec::new(),
|
||||
cur_generation: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self, callback: &dyn Fn($handle_name, &$instance_name)) {
|
||||
for (idx, opt_cell) in self.vec.iter().enumerate() {
|
||||
if let Some(cell) = opt_cell {
|
||||
let handle = $container_name::get_handle(&cell, idx);
|
||||
callback(handle, &cell.obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(
|
||||
&mut self,
|
||||
callback: &mut dyn FnMut($handle_name, &mut $instance_name),
|
||||
) {
|
||||
for (idx, opt_cell) in self.vec.iter_mut().enumerate() {
|
||||
if let Some(cell) = opt_cell {
|
||||
let handle = $container_name::get_handle(&cell, idx);
|
||||
callback(handle, &mut cell.obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_handle(cell: &$cell_name, idx: usize) -> $handle_name {
|
||||
$handle_name {
|
||||
idx: idx as u32,
|
||||
generation: cell.generation,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_unused_idx(&mut self) -> Option<u32> {
|
||||
for (num, obj) in self.vec.iter().enumerate() {
|
||||
if obj.is_none() {
|
||||
return Some(num as u32);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn add(&mut self, obj: $instance_name) -> $handle_name {
|
||||
self.cur_generation += 1;
|
||||
let generation = self.cur_generation;
|
||||
|
||||
let unused_idx = self.find_unused_idx();
|
||||
|
||||
let idx = if let Some(idx) = unused_idx {
|
||||
idx
|
||||
} else {
|
||||
self.vec.len() as u32
|
||||
};
|
||||
|
||||
let handle = $handle_name { idx, generation };
|
||||
|
||||
let cell = $cell_name { obj, generation };
|
||||
|
||||
if let Some(idx) = unused_idx {
|
||||
self.vec[idx as usize] = Some(cell);
|
||||
} else {
|
||||
self.vec.push(Some(cell))
|
||||
}
|
||||
|
||||
handle
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, handle: &$handle_name) {
|
||||
// Out of bounds, ignore
|
||||
if handle.idx as usize >= self.vec.len() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove only if the generation matches
|
||||
if let Some(cell) = &self.vec[handle.idx as usize] {
|
||||
if cell.generation == handle.generation {
|
||||
self.vec[handle.idx as usize] = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, handle: &$handle_name) -> Option<&$instance_name> {
|
||||
// Out of bounds, ignore
|
||||
if handle.idx as usize >= self.vec.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(cell) = &self.vec[handle.idx as usize] {
|
||||
if cell.generation == handle.generation {
|
||||
return Some(&cell.obj);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, handle: &$handle_name) -> Option<&mut $instance_name> {
|
||||
// Out of bounds, ignore
|
||||
if handle.idx as usize >= self.vec.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(cell) = &mut self.vec[handle.idx as usize] {
|
||||
if cell.generation == handle.generation {
|
||||
return Some(&mut cell.obj);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* Example usage:
|
||||
gen_id!(ThingVec, ThingInstance, ThingCell, ThingHandle);
|
||||
|
||||
struct ThingInstance {}
|
||||
|
||||
impl ThingInstance {}
|
||||
*/
|
||||
4
wayvr-ipc/src/util/mod.rs
Normal file
4
wayvr-ipc/src/util/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod handle;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub mod notifier;
|
||||
25
wayvr-ipc/src/util/notifier.rs
Normal file
25
wayvr-ipc/src/util/notifier.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::Notify;
|
||||
|
||||
// Copyable wrapped Notify struct for easier usage
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Notifier {
|
||||
notifier: Arc<Notify>,
|
||||
}
|
||||
|
||||
impl Notifier {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
notifier: Arc::new(Notify::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify(&self) {
|
||||
self.notifier.notify_waiters();
|
||||
}
|
||||
|
||||
pub async fn wait(&self) {
|
||||
self.notifier.notified().await;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,11 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use glam::{Mat4, Vec3};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use taffy::{AlignItems, JustifyContent, prelude::length};
|
||||
|
||||
pub struct Params<'a> {
|
||||
@@ -41,6 +45,7 @@ pub struct Params<'a> {
|
||||
/// until "un-clicked". this is visual only.
|
||||
/// set the initial state using `set_sticky_state`
|
||||
pub sticky: bool,
|
||||
pub long_press_time: f32,
|
||||
}
|
||||
|
||||
impl Default for Params<'_> {
|
||||
@@ -58,6 +63,7 @@ impl Default for Params<'_> {
|
||||
text_style: TextStyle::default(),
|
||||
tooltip: None,
|
||||
sticky: false,
|
||||
long_press_time: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,6 +85,7 @@ struct State {
|
||||
on_click: Option<ButtonClickCallback>,
|
||||
active_tooltip: Option<Rc<ComponentTooltip>>,
|
||||
colors: Colors,
|
||||
last_pressed: Instant,
|
||||
}
|
||||
|
||||
struct Data {
|
||||
@@ -151,6 +158,10 @@ impl ComponentButton {
|
||||
rect.params.color2 = get_color2(&color);
|
||||
}
|
||||
|
||||
pub fn get_time_since_last_pressed(&self) -> Duration {
|
||||
self.state.borrow().last_pressed.elapsed()
|
||||
}
|
||||
|
||||
pub fn on_click(&self, func: ButtonClickCallback) {
|
||||
self.state.borrow_mut().on_click = Some(func);
|
||||
}
|
||||
@@ -170,10 +181,13 @@ impl ComponentButton {
|
||||
return;
|
||||
}
|
||||
|
||||
let anim_mult = common.state.globals.defaults().animation_mult;
|
||||
let anim_ticks = if sticky_down { 5. } else { 10. };
|
||||
|
||||
let state = self.state.clone();
|
||||
let anim = Animation::new(
|
||||
self.data.id_rect,
|
||||
if sticky_down { 5 } else { 10 },
|
||||
(anim_ticks * anim_mult) as _,
|
||||
AnimationEasing::OutCubic,
|
||||
Box::new(move |common, anim_data| {
|
||||
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
@@ -227,10 +241,10 @@ fn anim_hover(
|
||||
rect.params.border_color = init_border_color.lerp(&colors.hover_border_color, mult);
|
||||
}
|
||||
|
||||
fn anim_hover_create(state: Rc<RefCell<State>>, widget_id: WidgetID, fade_in: bool) -> Animation {
|
||||
fn anim_hover_create(state: Rc<RefCell<State>>, widget_id: WidgetID, fade_in: bool, anim_mult: f32) -> Animation {
|
||||
Animation::new(
|
||||
widget_id,
|
||||
if fade_in { 5 } else { 10 },
|
||||
((if fade_in { 5. } else { 10. }) * anim_mult) as _,
|
||||
AnimationEasing::OutCubic,
|
||||
Box::new(move |common, anim_data| {
|
||||
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
@@ -254,6 +268,7 @@ fn register_event_mouse_enter(
|
||||
state: Rc<RefCell<State>>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
info: Option<components::tooltip::TooltipInfo>,
|
||||
anim_mult: f32,
|
||||
) -> EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::MouseEnter,
|
||||
@@ -262,7 +277,7 @@ fn register_event_mouse_enter(
|
||||
common.alterables.mark_redraw();
|
||||
common
|
||||
.alterables
|
||||
.animate(anim_hover_create(state.clone(), event_data.widget_id, true));
|
||||
.animate(anim_hover_create(state.clone(), event_data.widget_id, true, anim_mult));
|
||||
|
||||
if let Some(info) = info.clone() {
|
||||
common.alterables.tasks.push(LayoutTask::ModifyLayoutState({
|
||||
@@ -282,14 +297,18 @@ fn register_event_mouse_enter(
|
||||
)
|
||||
}
|
||||
|
||||
fn register_event_mouse_leave(state: Rc<RefCell<State>>, listeners: &mut EventListenerCollection) -> EventListenerID {
|
||||
fn register_event_mouse_leave(
|
||||
state: Rc<RefCell<State>>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
anim_mult: f32,
|
||||
) -> EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::MouseLeave,
|
||||
Box::new(move |common, event_data, (), ()| {
|
||||
common.alterables.trigger_haptics();
|
||||
common
|
||||
.alterables
|
||||
.animate(anim_hover_create(state.clone(), event_data.widget_id, false));
|
||||
.animate(anim_hover_create(state.clone(), event_data.widget_id, false, anim_mult));
|
||||
let mut state = state.borrow_mut();
|
||||
state.active_tooltip = None;
|
||||
state.hovered = false;
|
||||
@@ -320,7 +339,7 @@ fn register_event_mouse_press(state: Rc<RefCell<State>>, listeners: &mut EventLi
|
||||
|
||||
if state.hovered {
|
||||
state.down = true;
|
||||
state.active_tooltip = None;
|
||||
state.last_pressed = Instant::now();
|
||||
Ok(EventResult::Consumed)
|
||||
} else {
|
||||
Ok(EventResult::Pass)
|
||||
@@ -349,7 +368,6 @@ fn register_event_mouse_release(
|
||||
|
||||
if state.down {
|
||||
state.down = false;
|
||||
|
||||
if state.hovered
|
||||
&& let Some(on_click) = &state.on_click
|
||||
{
|
||||
@@ -503,6 +521,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
||||
on_click: None,
|
||||
active_tooltip: None,
|
||||
sticky_down: false,
|
||||
last_pressed: Instant::now(),
|
||||
colors: Colors {
|
||||
color,
|
||||
border_color,
|
||||
@@ -515,9 +534,16 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
||||
id: root.id,
|
||||
lhandles: {
|
||||
let mut widget = ess.layout.state.widgets.get(id_rect).unwrap().state();
|
||||
let anim_mult = ess.layout.state.globals.defaults().animation_mult;
|
||||
vec![
|
||||
register_event_mouse_enter(data.clone(), state.clone(), &mut widget.event_listeners, params.tooltip),
|
||||
register_event_mouse_leave(state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_enter(
|
||||
data.clone(),
|
||||
state.clone(),
|
||||
&mut widget.event_listeners,
|
||||
params.tooltip,
|
||||
anim_mult,
|
||||
),
|
||||
register_event_mouse_leave(state.clone(), &mut widget.event_listeners, anim_mult),
|
||||
register_event_mouse_press(state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_release(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use taffy::{
|
||||
AlignItems,
|
||||
prelude::{length, percent},
|
||||
AlignItems,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -13,10 +13,10 @@ use crate::{
|
||||
layout::{self, WidgetID, WidgetPair},
|
||||
renderer_vk::text::{FontWeight, TextStyle},
|
||||
widget::{
|
||||
ConstructEssentials, EventResult,
|
||||
label::{WidgetLabel, WidgetLabelParams},
|
||||
rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||
util::WLength,
|
||||
ConstructEssentials, EventResult,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -120,10 +120,10 @@ fn anim_hover(rect: &mut WidgetRectangle, pos: f32, pressed: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fn anim_hover_in(state: Rc<RefCell<State>>, widget_id: WidgetID) -> Animation {
|
||||
fn anim_hover_in(state: Rc<RefCell<State>>, widget_id: WidgetID, anim_mult: f32) -> Animation {
|
||||
Animation::new(
|
||||
widget_id,
|
||||
5,
|
||||
(5. * anim_mult) as _,
|
||||
AnimationEasing::OutQuad,
|
||||
Box::new(move |common, anim_data| {
|
||||
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
@@ -133,10 +133,10 @@ fn anim_hover_in(state: Rc<RefCell<State>>, widget_id: WidgetID) -> Animation {
|
||||
)
|
||||
}
|
||||
|
||||
fn anim_hover_out(state: Rc<RefCell<State>>, widget_id: WidgetID) -> Animation {
|
||||
fn anim_hover_out(state: Rc<RefCell<State>>, widget_id: WidgetID, anim_mult: f32) -> Animation {
|
||||
Animation::new(
|
||||
widget_id,
|
||||
8,
|
||||
(8. * anim_mult) as _,
|
||||
AnimationEasing::OutQuad,
|
||||
Box::new(move |common, anim_data| {
|
||||
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
@@ -146,28 +146,36 @@ fn anim_hover_out(state: Rc<RefCell<State>>, widget_id: WidgetID) -> Animation {
|
||||
)
|
||||
}
|
||||
|
||||
fn register_event_mouse_enter(state: Rc<RefCell<State>>, listeners: &mut EventListenerCollection) -> EventListenerID {
|
||||
fn register_event_mouse_enter(
|
||||
state: Rc<RefCell<State>>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
anim_mult: f32,
|
||||
) -> EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::MouseEnter,
|
||||
Box::new(move |common, event_data, (), ()| {
|
||||
common.alterables.trigger_haptics();
|
||||
common
|
||||
.alterables
|
||||
.animate(anim_hover_in(state.clone(), event_data.widget_id));
|
||||
.animate(anim_hover_in(state.clone(), event_data.widget_id, anim_mult));
|
||||
state.borrow_mut().hovered = true;
|
||||
Ok(EventResult::Pass)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn register_event_mouse_leave(state: Rc<RefCell<State>>, listeners: &mut EventListenerCollection) -> EventListenerID {
|
||||
fn register_event_mouse_leave(
|
||||
state: Rc<RefCell<State>>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
anim_mult: f32,
|
||||
) -> EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::MouseLeave,
|
||||
Box::new(move |common, event_data, (), ()| {
|
||||
common.alterables.trigger_haptics();
|
||||
common
|
||||
.alterables
|
||||
.animate(anim_hover_out(state.clone(), event_data.widget_id));
|
||||
.animate(anim_hover_out(state.clone(), event_data.widget_id, anim_mult));
|
||||
state.borrow_mut().hovered = false;
|
||||
Ok(EventResult::Pass)
|
||||
}),
|
||||
@@ -341,9 +349,10 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
||||
id: root.id,
|
||||
lhandles: {
|
||||
let mut widget = ess.layout.state.widgets.get(id_container).unwrap().state();
|
||||
let anim_mult = ess.layout.state.globals.defaults().animation_mult;
|
||||
vec![
|
||||
register_event_mouse_enter(state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_leave(state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_enter(state.clone(), &mut widget.event_listeners, anim_mult),
|
||||
register_event_mouse_leave(state.clone(), &mut widget.event_listeners, anim_mult),
|
||||
register_event_mouse_press(state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_release(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
]
|
||||
|
||||
@@ -18,11 +18,11 @@ use crate::{
|
||||
util,
|
||||
},
|
||||
widget::{
|
||||
ConstructEssentials, EventResult,
|
||||
div::WidgetDiv,
|
||||
label::{WidgetLabel, WidgetLabelParams},
|
||||
rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||
util::WLength,
|
||||
ConstructEssentials, EventResult,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -266,10 +266,10 @@ fn anim_rect(rect: &mut WidgetRectangle, pos: f32) {
|
||||
rect.params.border_color = drawing::Color::lerp(&HANDLE_BORDER_COLOR, &HANDLE_BORDER_COLOR_HOVERED, pos);
|
||||
}
|
||||
|
||||
fn on_enter_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
|
||||
fn on_enter_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID, anim_mult: f32) {
|
||||
common.alterables.animate(Animation::new(
|
||||
handle_id,
|
||||
20,
|
||||
(20. * anim_mult) as _,
|
||||
AnimationEasing::OutBack,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
@@ -280,10 +280,10 @@ fn on_enter_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
|
||||
));
|
||||
}
|
||||
|
||||
fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
|
||||
fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID, anim_mult: f32) {
|
||||
common.alterables.animate(Animation::new(
|
||||
handle_id,
|
||||
10,
|
||||
(10. * anim_mult) as _,
|
||||
AnimationEasing::OutQuad,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
@@ -298,13 +298,14 @@ fn register_event_mouse_enter(
|
||||
data: Rc<Data>,
|
||||
state: Rc<RefCell<State>>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
anim_mult: f32,
|
||||
) -> event::EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::MouseEnter,
|
||||
Box::new(move |common, _data, (), ()| {
|
||||
common.alterables.trigger_haptics();
|
||||
state.borrow_mut().hovered = true;
|
||||
on_enter_anim(common, data.slider_handle_rect_id);
|
||||
on_enter_anim(common, data.slider_handle_rect_id, anim_mult);
|
||||
Ok(EventResult::Pass)
|
||||
}),
|
||||
)
|
||||
@@ -314,13 +315,14 @@ fn register_event_mouse_leave(
|
||||
data: Rc<Data>,
|
||||
state: Rc<RefCell<State>>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
anim_mult: f32,
|
||||
) -> event::EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::MouseLeave,
|
||||
Box::new(move |common, _data, (), ()| {
|
||||
common.alterables.trigger_haptics();
|
||||
state.borrow_mut().hovered = false;
|
||||
on_leave_anim(common, data.slider_handle_rect_id);
|
||||
on_leave_anim(common, data.slider_handle_rect_id, anim_mult);
|
||||
Ok(EventResult::Pass)
|
||||
}),
|
||||
)
|
||||
@@ -508,12 +510,12 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
||||
id: root.id,
|
||||
lhandles: {
|
||||
let mut widget = ess.layout.state.widgets.get(body_id).unwrap().state();
|
||||
let anim_mult = ess.layout.state.globals.defaults().animation_mult;
|
||||
vec![
|
||||
register_event_mouse_enter(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_leave(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_enter(data.clone(), state.clone(), &mut widget.event_listeners, anim_mult),
|
||||
register_event_mouse_leave(data.clone(), state.clone(), &mut widget.event_listeners, anim_mult),
|
||||
register_event_mouse_motion(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_press(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_leave(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_release(state.clone(), &mut widget.event_listeners),
|
||||
]
|
||||
},
|
||||
|
||||
@@ -22,7 +22,10 @@ pub struct Defaults {
|
||||
pub accent_color: drawing::Color,
|
||||
pub danger_color: drawing::Color,
|
||||
pub faded_color: drawing::Color,
|
||||
pub bg_color: drawing::Color,
|
||||
pub translucent_alpha: f32,
|
||||
pub animation_mult: f32,
|
||||
pub rounding_mult: f32,
|
||||
}
|
||||
|
||||
impl Default for Defaults {
|
||||
@@ -34,7 +37,10 @@ impl Default for Defaults {
|
||||
accent_color: drawing::Color::new(0.0, 0.54, 1.0, 1.0),
|
||||
danger_color: drawing::Color::new(0.8, 0.0, 0.0, 1.0),
|
||||
faded_color: drawing::Color::new(0.4, 0.5, 0.6, 1.0),
|
||||
bg_color: drawing::Color::new(0.0039, 0.0078, 0.0235, 0.8352),
|
||||
translucent_alpha: 0.25,
|
||||
animation_mult: 1.0,
|
||||
rounding_mult: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::{
|
||||
assets::AssetPath,
|
||||
components::{Component, button, tooltip},
|
||||
components::{button, tooltip, Component},
|
||||
drawing::Color,
|
||||
i18n::Translation,
|
||||
layout::WidgetID,
|
||||
parser::{
|
||||
AttribPair, ParserContext, ParserFile, parse_check_f32, parse_check_i32, parse_children, print_invalid_attrib,
|
||||
process_component,
|
||||
parse_check_f32, parse_check_i32, parse_children, parse_f32, print_invalid_attrib, process_component,
|
||||
style::{parse_color_opt, parse_round, parse_style, parse_text_style},
|
||||
AttribPair, ParserContext, ParserFile,
|
||||
},
|
||||
widget::util::WLength,
|
||||
};
|
||||
@@ -28,6 +28,7 @@ pub fn parse_component_button<'a>(
|
||||
let mut tooltip: Option<String> = None;
|
||||
let mut tooltip_side: Option<tooltip::TooltipSide> = None;
|
||||
let mut sticky: bool = false;
|
||||
let mut long_press_time = 0.0;
|
||||
let mut sprite_src: Option<AssetPath> = None;
|
||||
|
||||
let mut translation: Option<Translation> = None;
|
||||
@@ -45,7 +46,7 @@ pub fn parse_component_button<'a>(
|
||||
translation = Some(Translation::from_translation_key(value));
|
||||
}
|
||||
"round" => {
|
||||
parse_round(value, &mut round);
|
||||
parse_round(value, &mut round, ctx.doc_params.globals.get().defaults.rounding_mult);
|
||||
}
|
||||
"color" => {
|
||||
parse_color_opt(value, &mut color);
|
||||
@@ -92,6 +93,9 @@ pub fn parse_component_button<'a>(
|
||||
let mut sticky_i32 = 0;
|
||||
sticky = parse_check_i32(value, &mut sticky_i32) && sticky_i32 == 1;
|
||||
}
|
||||
"long_press_time" => {
|
||||
long_press_time = parse_f32(value).unwrap_or(long_press_time);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -113,6 +117,7 @@ pub fn parse_component_button<'a>(
|
||||
text: Translation::from_translation_key(&t),
|
||||
}),
|
||||
sticky,
|
||||
long_press_time,
|
||||
sprite_src,
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -8,7 +8,7 @@ mod widget_rectangle;
|
||||
mod widget_sprite;
|
||||
|
||||
use crate::{
|
||||
assets::{AssetPath, AssetPathOwned, normalize_path},
|
||||
assets::{normalize_path, AssetPath, AssetPathOwned},
|
||||
components::{Component, ComponentWeak},
|
||||
drawing::{self},
|
||||
globals::WguiGlobals,
|
||||
@@ -400,6 +400,7 @@ impl ParserContext<'_> {
|
||||
insert_color_vars!(self, "accent", def.accent_color, def.translucent_alpha);
|
||||
insert_color_vars!(self, "danger", def.danger_color, def.translucent_alpha);
|
||||
insert_color_vars!(self, "faded", def.faded_color, def.translucent_alpha);
|
||||
insert_color_vars!(self, "bg", def.bg_color, def.translucent_alpha);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ use taffy::{
|
||||
use crate::{
|
||||
drawing,
|
||||
parser::{
|
||||
AttribPair, is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val,
|
||||
print_invalid_attrib, print_invalid_value,
|
||||
is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val, print_invalid_attrib,
|
||||
print_invalid_value, AttribPair,
|
||||
},
|
||||
renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle},
|
||||
widget::util::WLength,
|
||||
};
|
||||
|
||||
pub fn parse_round(value: &str, round: &mut WLength) {
|
||||
pub fn parse_round(value: &str, round: &mut WLength, multiplier: f32) {
|
||||
if is_percent(value) {
|
||||
if let Some(val) = parse_percent(value) {
|
||||
*round = WLength::Percent(val);
|
||||
@@ -21,7 +21,7 @@ pub fn parse_round(value: &str, round: &mut WLength) {
|
||||
print_invalid_value(value);
|
||||
}
|
||||
} else if let Some(val) = parse_f32(value) {
|
||||
*round = WLength::Units(val);
|
||||
*round = WLength::Units((val * multiplier).max(0.));
|
||||
} else {
|
||||
print_invalid_value(value);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ use crate::{
|
||||
drawing::GradientMode,
|
||||
layout::WidgetID,
|
||||
parser::{
|
||||
AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, print_invalid_attrib,
|
||||
parse_children, parse_widget_universal, print_invalid_attrib,
|
||||
style::{parse_color, parse_round, parse_style},
|
||||
AttribPair, ParserContext, ParserFile,
|
||||
},
|
||||
widget::rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||
};
|
||||
@@ -40,7 +41,11 @@ pub fn parse_widget_rectangle<'a>(
|
||||
}
|
||||
}
|
||||
"round" => {
|
||||
parse_round(value, &mut params.round);
|
||||
parse_round(
|
||||
value,
|
||||
&mut params.round,
|
||||
ctx.doc_params.globals.get().defaults.rounding_mult,
|
||||
);
|
||||
}
|
||||
"border" => {
|
||||
params.border = value.parse().unwrap_or_else(|_| {
|
||||
|
||||
@@ -205,7 +205,11 @@ impl EventResult {
|
||||
|
||||
#[must_use]
|
||||
pub fn merge(self, other: Self) -> Self {
|
||||
if self > other { self } else { other }
|
||||
if self > other {
|
||||
self
|
||||
} else {
|
||||
other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,12 +47,14 @@ use crate::frame::Transform;
|
||||
use crate::frame::WlxFrame;
|
||||
use crate::frame::{DmabufFrame, FramePlane, MemFdFrame, MemPtrFrame};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PipewireStream {
|
||||
pub node_id: u32,
|
||||
pub position: Option<(i32, i32)>,
|
||||
pub size: Option<(i32, i32)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PipewireSelectScreenResult {
|
||||
pub streams: Vec<PipewireStream>,
|
||||
pub restore_token: Option<String>,
|
||||
@@ -279,6 +281,7 @@ where
|
||||
U: Any,
|
||||
R: Any,
|
||||
{
|
||||
log::debug!("{}: pipewire main_loop start", &name);
|
||||
let main_loop = MainLoop::new(None)?;
|
||||
let context = Context::new(&main_loop)?;
|
||||
let core = context.connect(None)?;
|
||||
|
||||
@@ -92,9 +92,18 @@ pub struct GeneralConfig {
|
||||
#[serde(default = "def_theme_path")]
|
||||
pub theme_path: Arc<str>,
|
||||
|
||||
pub color_text: Option<String>,
|
||||
pub color_accent: Option<String>,
|
||||
pub color_danger: Option<String>,
|
||||
pub color_faded: Option<String>,
|
||||
pub color_background: Option<String>,
|
||||
|
||||
#[serde(default = "def_one")]
|
||||
pub animation_speed: f32,
|
||||
|
||||
#[serde(default = "def_one")]
|
||||
pub round_multiplier: f32,
|
||||
|
||||
pub default_keymap: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
@@ -112,6 +121,9 @@ pub struct GeneralConfig {
|
||||
#[serde(default = "def_one")]
|
||||
pub scroll_speed: f32,
|
||||
|
||||
#[serde(default = "def_one")]
|
||||
pub long_press_duration: f32,
|
||||
|
||||
#[serde(default = "def_mouse_move_interval_ms")]
|
||||
pub mouse_move_interval_ms: u32,
|
||||
|
||||
@@ -205,6 +217,9 @@ pub struct GeneralConfig {
|
||||
#[serde(default = "def_point3")]
|
||||
pub pointer_lerp_factor: f32,
|
||||
|
||||
#[serde(default = "def_true")]
|
||||
pub space_drag_unlocked: bool,
|
||||
|
||||
#[serde(default = "def_false")]
|
||||
pub space_rotate_unlocked: bool,
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, IntegerId, Serialize, Deserialize)]
|
||||
pub enum ToastTopic {
|
||||
System,
|
||||
Error,
|
||||
DesktopNotification,
|
||||
XSNotification,
|
||||
IpdChange,
|
||||
|
||||
15
wlx-overlay-s/Cargo.lock
generated
15
wlx-overlay-s/Cargo.lock
generated
@@ -5551,19 +5551,6 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayvr_ipc"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/olekolek1000/wayvr-ipc.git?rev=a72587d23f3bb8624d9aeb1f13c0a21e65350f51#a72587d23f3bb8624d9aeb1f13c0a21e65350f51"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.77"
|
||||
@@ -6206,7 +6193,7 @@ dependencies = [
|
||||
"uuid",
|
||||
"wayland-client",
|
||||
"wayland-egl",
|
||||
"wayvr_ipc",
|
||||
"wayvr-ipc",
|
||||
"wgui",
|
||||
"winit",
|
||||
"wlx-capture",
|
||||
|
||||
@@ -97,6 +97,7 @@ uuid = { version = "1.19.0", features = ["v4", "fast-rng"], optional = true }
|
||||
wayland-client = { workspace = true, optional = true }
|
||||
wayland-egl = { version = "0.32.8", optional = true }
|
||||
bytes = { version = "1.11.0", optional = true }
|
||||
wayvr-ipc = { path = "../wayvr-ipc", default-features = false, optional = true }
|
||||
rust-embed = { workspace = true }
|
||||
signal-hook = "0.3.18"
|
||||
################################
|
||||
@@ -121,5 +122,6 @@ wayvr = [
|
||||
"dep:wayland-client",
|
||||
"dep:wayland-egl",
|
||||
"dep:bytes",
|
||||
"dep:wayvr-ipc",
|
||||
]
|
||||
as-raw-xcb-connection = []
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</template>
|
||||
|
||||
<template name="TopButtonDanger">
|
||||
<Button macro="button_style" tooltip="${tooltip}" _press="${press}" _release="${release}" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
|
||||
<Button macro="button_style" tooltip="${tooltip}" _long_release="${long_release}" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
|
||||
<sprite width="48" height="48" src="${src}" />
|
||||
</Button>
|
||||
</template>
|
||||
@@ -39,7 +39,7 @@
|
||||
<rectangle padding="16" gap="8" round="32" color="~color_bg" border="2" border_color="~color_accent" justify_content="center">
|
||||
<div flex_direction="column" gap="8">
|
||||
<div flex_direction="row" gap="4">
|
||||
<TopButton sticky="1" id="top_lock" src="edit/lock_open.svg" tooltip="EDIT_MODE.LOCK_INTERACTION" press="::EditModeToggleLock" />
|
||||
<TopButton sticky="0" id="top_lock" src="edit/lock_open.svg" tooltip="EDIT_MODE.LOCK_INTERACTION" press="::EditModeToggleLock" />
|
||||
<TopButton sticky="1" id="top_grab" src="edit/disable-grab.svg" tooltip="EDIT_MODE.DISABLE_GRAB" press="::EditModeToggleGrab" />
|
||||
<TopButton sticky="0" id="top_pos" src="edit/anchor.svg" tooltip="EDIT_MODE.POSITIONING" press="::EditModeTab pos" />
|
||||
<TopButton sticky="0" id="top_alpha" src="edit/fade.svg" tooltip="EDIT_MODE.OPACITY" press="::EditModeTab alpha" />
|
||||
@@ -48,7 +48,7 @@
|
||||
<TopButton sticky="0" id="top_mouse" src="edit/normal.svg" tooltip="EDIT_MODE.MOUSE.TITLE" press="::EditModeTab mouse" />
|
||||
<!-- TopButton sticky="0" id="top_move" src="edit/move-all.svg" tooltip="EDIT_MODE.MOVE_PRESS_AND_DRAG" / -->
|
||||
<!-- TopButton sticky="0" id="top_resize" src="edit/resize.svg" tooltip="EDIT_MODE.RESIZE_PRESS_AND_DRAG" / -->
|
||||
<TopButtonDanger src="edit/delete.svg" tooltip="EDIT_MODE.DELETE" press="::EditModeDeletePress" release="::EditModeDeleteRelease" />
|
||||
<TopButtonDanger src="edit/delete.svg" tooltip="EDIT_MODE.DELETE" long_release="::EditModeDelete" />
|
||||
<div width="8" height="100%" />
|
||||
<TopButtonFaded src="watch/edit.svg" tooltip="EDIT_MODE.LEAVE" press="::EditToggle" />
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<template name="KeySpecial">
|
||||
<div macro="keycap_div">
|
||||
<rectangle id="${id}" macro="keycap_rect">
|
||||
<sprite width="32" height="32" src="keyboard/${text}.svg" />
|
||||
<sprite color="~color_text" width="32" height="32" src="keyboard/${text}.svg" />
|
||||
</rectangle>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,8 +34,8 @@ All overlays are listed on bottom row.
|
||||
</template>
|
||||
|
||||
<template name="Overlay">
|
||||
<Button macro="button_style" id="overlay_${idx}" sticky="1"
|
||||
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::SingleSetOverlayToggle ${idx}"
|
||||
<Button macro="button_style" id="overlay_${idx}"
|
||||
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::SingleSetOverlayToggle ${idx}" _long_release="::SingleSetOverlayReset ${idx}"
|
||||
align_items="center"
|
||||
height="40">
|
||||
<sprite id="overlay_${idx}_sprite" src_builtin="${src}" width="32" height="32" />
|
||||
@@ -111,13 +111,13 @@ All overlays are listed on bottom row.
|
||||
|
||||
<!-- Bottom buttons -->
|
||||
<div id="toolbox-condensed" gap="6" width="100%" max_width="400" flex_direction="row" flex_wrap="wrap">
|
||||
<Button id="btn_dashboard" height="40" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" sticky="1" >
|
||||
<Button id="btn_dashboard" height="40" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" >
|
||||
<sprite color="~set_color" width="32" height="32" src="watch/wayvr_dashboard_mono.svg" />
|
||||
</Button>
|
||||
<Button id="btn_edit_mode" height="40" macro="button_style" _press="::EditToggle" tooltip="WATCH.EDIT_MODE" tooltip_side="top">
|
||||
<sprite color="~set_color" width="32" height="32" src="watch/edit.svg" />
|
||||
</Button>
|
||||
<Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" sticky="1" >
|
||||
<Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" >
|
||||
<sprite src_builtin="watch/keyboard.svg" width="32" height="32" />
|
||||
</Button>
|
||||
<!-- Src here may be changed, but maintain `OverlayCategory` order: Panel, Screen, Mirror, WayVR -->
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</template>
|
||||
|
||||
<template name="Overlay">
|
||||
<Button macro="button_style" id="overlay_${idx}" sticky="1"
|
||||
<Button macro="button_style" id="overlay_${idx}"
|
||||
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::EditModeOverlayToggle ${idx}"
|
||||
align_items="center"
|
||||
height="40">
|
||||
@@ -120,7 +120,7 @@
|
||||
</div>
|
||||
<div flex_direction="column" align_items="center" justify_content="center">
|
||||
<div id="toolbox" gap="8" width="100%" max_width="400" flex_direction="row" flex_wrap="wrap">
|
||||
<Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" sticky="1" >
|
||||
<Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" >
|
||||
<sprite src_builtin="watch/keyboard.svg" width="32" height="32" />
|
||||
<label translation="EDIT_MODE.KEYBOARD" size="18" />
|
||||
</Button>
|
||||
@@ -136,11 +136,11 @@
|
||||
|
||||
<!-- Bottom buttons -->
|
||||
<div flex_direction="row" gap="4">
|
||||
<Button id="btn_dashboard" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" sticky="1" >
|
||||
<Button id="btn_dashboard" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" >
|
||||
<sprite color="~set_color" width="40" height="40" src="watch/wayvr_dashboard_mono.svg" />
|
||||
</Button>
|
||||
<div id="edit_delete" display="none">
|
||||
<Button macro="button_style" _press="::EditModeDeleteDown" _release="::EditModeDeleteUp" tooltip="WATCH.LONG_PRESS_TO_DELETE_SET" tooltip_side="top" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
|
||||
<Button macro="button_style" _long_release="::EditModeDeleteSet" tooltip="WATCH.LONG_PRESS_TO_DELETE_SET" tooltip_side="top" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
|
||||
<sprite color="~set_color" width="40" height="40" src="edit/delete.svg" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,43 @@
|
||||
"SPLIT_TOP_BOTTOM": "OBEN→UNTEN",
|
||||
"SPLIT_BOTTOM_TOP": "UNTEN→OBEN"
|
||||
},
|
||||
"STEREO_3D_MODE_": {}
|
||||
"STEREO_3D_MODE_": {},
|
||||
"ALIGN_TO_HMD": "An HMD ausrichten",
|
||||
"MOUSE": {
|
||||
"TITLE": "Mauskorrekturen",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "Wenn der Cursor auf einem völlig anderen Bildschirm verschwindet,\nwurden die Bildschirme wahrscheinlich falsch ausgewählt. Siehe Lesezeichen.",
|
||||
"NORMAL": "Normal",
|
||||
"FLIPPED": "Umgedreht",
|
||||
"FLIP90": "Um 90° gedreht",
|
||||
"FLIP180": "Um 180° gedreht",
|
||||
"FLIP270": "Um 270° gedreht",
|
||||
"ROTATE90": "Um 90° gedreht",
|
||||
"ROTATE180": "Um 180° gedreht",
|
||||
"ROTATE270": "Um 270° gedreht"
|
||||
}
|
||||
},
|
||||
"DISABLED": "Deaktiviert"
|
||||
}
|
||||
"DISABLED": "Deaktiviert",
|
||||
"DEFAULT": "Standard",
|
||||
"GRAB": {
|
||||
"ADJUST_DISTANCE": "Entfernung anpassen",
|
||||
"ADJUST_SIZE": "Größe anpassen",
|
||||
"UNRESTRICTED_MOVEMENT": "Unbeschränkte Bewegung",
|
||||
"GRABBING_WATCH": "Um die Hände zu vertauschen, bewegen Sie die Uhr vor sich und greifen Sie sie mit der anderen Hand.",
|
||||
"GRABBING_STATIC": "Dieses Overlay ist statisch und bleibt an Ort und Stelle, ignoriert die Neu-Zentrierung.",
|
||||
"GRABBING_ANCHORED": "Festgelegte Overlays bewegen sich alle zusammen. Trennen Sie ein einzelnes Fenster, indem Sie es mit der anderen Hand greifen, während Sie den Anker weiterhin greifen.",
|
||||
"GRABBING_ANCHORED_EDIT": "Diese Overlay-Darstellung bleibt an der Mittelmarkierung verankert.",
|
||||
"GRABBING_FLOATING": "Diese Overlay-Darstellung ist frei schwebend und bleibt an Ort und Stelle, es sei denn, sie wird neu zentriert.",
|
||||
"GRABBING_FOLLOW": "Dieses Overlay wird dem Gerät folgen, an das es angehängt ist."
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "Benachrichtigung",
|
||||
"ERROR": "Fehler",
|
||||
"CANNOT_REMOVE_SET": "Satz kann nicht entfernt werden!",
|
||||
"NO_SET_SELECTED": "Kein Set ausgewählt.",
|
||||
"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!"
|
||||
}
|
||||
}
|
||||
@@ -39,12 +39,12 @@
|
||||
"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 170°",
|
||||
"ROTATE180": "Rotated 180°",
|
||||
"ROTATE270": "Rotated 270°",
|
||||
"FLIPPED": "Flipped",
|
||||
"ROTATE90": "Flipped 90°",
|
||||
"ROTATE180": "Flipped 170°",
|
||||
"ROTATE270": "Flipped 270°"
|
||||
"FLIP90": "Flipped 90°",
|
||||
"FLIP180": "Flipped 180°",
|
||||
"FLIP270": "Flipped 270°"
|
||||
}
|
||||
},
|
||||
"GRAB": {
|
||||
@@ -58,6 +58,17 @@
|
||||
"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."
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "Notification",
|
||||
"ERROR": "Error",
|
||||
"CANNOT_REMOVE_SET": "Cannot remove set!",
|
||||
"NO_SET_SELECTED": "No set is selected.",
|
||||
"LAST_EXISTING_SET": "This is the last existing set.",
|
||||
"EMPTY_SET": "Empty set!",
|
||||
"LETS_ADD_OVERLAYS": "Let's add some overlays from the watch!",
|
||||
"FIXING_FLOOR": "Fixing floor in 5 seconds...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "Place one controller on the floor!"
|
||||
},
|
||||
"WATCH": {
|
||||
"ADD_NEW_SET": "Add a new set",
|
||||
"CLEANUP_MIRRORS": "Remove mirrors that are\nnot currently visible",
|
||||
|
||||
@@ -45,7 +45,43 @@
|
||||
"SPLIT_TOP_BOTTOM": "ARRIBA→ABAJO",
|
||||
"SPLIT_BOTTOM_TOP": "ABAJO→ARRIBA"
|
||||
},
|
||||
"STEREO_3D_MODE_": {}
|
||||
"STEREO_3D_MODE_": {},
|
||||
"ALIGN_TO_HMD": "Alinear al HMD",
|
||||
"MOUSE": {
|
||||
"TITLE": "Correcciones del ratón",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "Si el cursor se mueve en una pantalla completamente diferente,\nes probable que las pantallas se hayan seleccionado incorrectamente. Consulta el archivo readme.",
|
||||
"NORMAL": "Normal",
|
||||
"FLIPPED": "Invertido",
|
||||
"FLIP90": "Girado 90°",
|
||||
"FLIP180": "Invertido 180°",
|
||||
"FLIP270": "Invertido 270°",
|
||||
"ROTATE90": "Rotado 90°",
|
||||
"ROTATE180": "Rotado 180°",
|
||||
"ROTATE270": "Rotado 270°"
|
||||
}
|
||||
},
|
||||
"DISABLED": "Deshabilitado"
|
||||
"DISABLED": "Deshabilitado",
|
||||
"DEFAULT": "Por defecto",
|
||||
"GRAB": {
|
||||
"ADJUST_DISTANCE": "Ajustar distancia",
|
||||
"ADJUST_SIZE": "Ajustar tamaño",
|
||||
"UNRESTRICTED_MOVEMENT": "Movimiento sin restricciones",
|
||||
"GRABBING_WATCH": "Para intercambiar las manos, mueve el reloj al frente y agrárralo con la otra mano.",
|
||||
"GRABBING_STATIC": "Esta superposición es estática y permanecerá en su lugar, ignorando el recentrado.",
|
||||
"GRABBING_ANCHORED": "Las superposiciones ancladas se mueven todas juntas. Separa una ventana individual agarrándola con la otra mano mientras aún sujetas el ancla.",
|
||||
"GRABBING_ANCHORED_EDIT": "Esta superposición permanecerá anclada al marcador central.",
|
||||
"GRABBING_FLOATING": "Esta superposición es flotante y permanecerá en su lugar a menos que se recentre.",
|
||||
"GRABBING_FOLLOW": "Esta superposición seguirá el dispositivo al que está adjunta."
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "Notificación",
|
||||
"ERROR": "Error",
|
||||
"CANNOT_REMOVE_SET": "¡No se puede eliminar el conjunto!",
|
||||
"NO_SET_SELECTED": "No se ha seleccionado ningún conjunto.",
|
||||
"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!"
|
||||
}
|
||||
}
|
||||
@@ -16,28 +16,27 @@
|
||||
"LONG_PRESS_TO_DELETE_SET": "長押しでセットを削除"
|
||||
},
|
||||
"EDIT_MODE": {
|
||||
"ADJUST_CURVATURE": "曲率を調整する",
|
||||
"ALPHA_BLEND_MODE": "アルファブレンドモード",
|
||||
"BLENDING_ADDITIVE": "加算",
|
||||
"ADJUST_CURVATURE": "曲率の調整",
|
||||
"ALPHA_BLEND_MODE": "アルファブレンド",
|
||||
"BLENDING_ADDITIVE": "加算モード",
|
||||
"CURVATURE": "曲率",
|
||||
"DELETE": "削除",
|
||||
"HINT_POINT_WINDOW": "ウィンドウをタップしてそのパラメータを変更します。\n完了したら、右側のボタンで編集モードを終了してください。",
|
||||
"LEAVE": "編集モードを終了",
|
||||
"LOCK_INTERACTION": "インタラクションをロック",
|
||||
"HINT_POINT_WINDOW": "ウィンドウをタップしてそのパラメータを変更できます。\n右側のボタンで編集モードを終了できます。",
|
||||
"LEAVE": "編集モードを終了する",
|
||||
"LOCK_INTERACTION": "インタラクションを無効化",
|
||||
"DISABLE_GRAB": "グラブを無効化",
|
||||
"MOVE_PRESS_AND_DRAG": "移動(押してドラッグ)",
|
||||
"OPACITY": "不透明度",
|
||||
"POSITIONING": "位置調整",
|
||||
"POSITIONING": "位置付け",
|
||||
"RESIZE_PRESS_AND_DRAG": "サイズ変更(押してドラッグ)",
|
||||
"POS_STATIC": "固定:その場に留まり、再センタリングされません。",
|
||||
"POS_ANCHORED": "アンカー:他のセット要素と一緒に移動します。デフォルト。",
|
||||
"POS_FLOATING": "フローティング:その場に留まり、表示されるときに再センタリングされます。",
|
||||
"POS_STATIC": "固定:置かれた場所に留まり、再センタリングされません。",
|
||||
"POS_ANCHORED": "アンカー:セット内の他のウィンドウと共に移動します。デフォルト。",
|
||||
"POS_FLOATING": "フローティング:置かれた場所に留まるが、表示される時に再センタリングされます。",
|
||||
"POS_HMD": "HMD に追従する。",
|
||||
"POS_HAND_L": "左手に追従する。",
|
||||
"POS_HAND_R": "右手に追従する。",
|
||||
"INTERPOLATION": "補間",
|
||||
"INTERPOLATION": "スムージング",
|
||||
"KEYBOARD": "キーボード",
|
||||
"STEREO_3D_MODE_A": {},
|
||||
"STEREO_3D_MODE": {
|
||||
"TITLE": "ステレオ3Dモード",
|
||||
"SPLIT_LEFT_RIGHT": "左→右",
|
||||
@@ -45,7 +44,42 @@
|
||||
"SPLIT_TOP_BOTTOM": "上→下",
|
||||
"SPLIT_BOTTOM_TOP": "下→上"
|
||||
},
|
||||
"STEREO_3D_MODE_": {}
|
||||
"ALIGN_TO_HMD": "常にHMDの方を向く",
|
||||
"MOUSE": {
|
||||
"TITLE": "マウスの修正",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "カーソルが全く別のスクリーンで動く場合、\nスクリーンが正しく選択されていない可能性があります。\nマニュアルをご確認ください。",
|
||||
"NORMAL": "通常",
|
||||
"FLIPPED": "ミラー",
|
||||
"FLIP90": "ミラー 90°",
|
||||
"FLIP180": "ミラー 180°",
|
||||
"FLIP270": "ミラー 270°",
|
||||
"ROTATE90": "回転 90°",
|
||||
"ROTATE180": "回転 180°",
|
||||
"ROTATE270": "回転 270°"
|
||||
}
|
||||
},
|
||||
"DISABLED": "無効"
|
||||
"DISABLED": "無効",
|
||||
"DEFAULT": "デフォルト",
|
||||
"GRAB": {
|
||||
"ADJUST_DISTANCE": "距離を調整",
|
||||
"ADJUST_SIZE": "サイズ調整",
|
||||
"UNRESTRICTED_MOVEMENT": "配置制限なし",
|
||||
"GRABBING_WATCH": "手を入れ替えるにはウォッチを前に移動させて、もう一方の手で掴んでください。",
|
||||
"GRABBING_STATIC": "このオーバーレイは固定であり、リセンターを無視してその場に留まります。",
|
||||
"GRABBING_ANCHORED": "アンカーされたオーバーレイはすべて一緒に移動します。もう一方の手で掴んでアンカーを掴んだまま、単一のウィンドウを分離できます。",
|
||||
"GRABBING_ANCHORED_EDIT": "このオーバーレイは、センター・マーカーに固定されます。",
|
||||
"GRABBING_FLOATING": "このオーバーレイはフローティングのため、リセンターまではその場所に留まります。",
|
||||
"GRABBING_FOLLOW": "このオーバーレイは、接続されているデバイスに追従します。"
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "通知",
|
||||
"ERROR": "エラー",
|
||||
"CANNOT_REMOVE_SET": "セットを削除できません!",
|
||||
"NO_SET_SELECTED": "セットが選択されていません。",
|
||||
"LAST_EXISTING_SET": "これが最後の設定です。",
|
||||
"EMPTY_SET": "空のセットです!",
|
||||
"LETS_ADD_OVERLAYS": "ウォッチからオーバーレイを追加しましょう!",
|
||||
"FIXING_FLOOR": "5秒後にフロアを固定します...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "コントローラーを床に置いてください!"
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,43 @@
|
||||
"SPLIT_RIGHT_LEFT": "PRAWO→LEWO",
|
||||
"SPLIT_TOP_BOTTOM": "GÓRA→DÓŁ",
|
||||
"TITLE": "Tryb stereoskopowy 3D"
|
||||
},
|
||||
"ALIGN_TO_HMD": "Dopasuj do HMD",
|
||||
"MOUSE": {
|
||||
"TITLE": "Naprawa myszy",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "Jeśli kursor porusza się na zupełnie innym ekranie,\nekrany zostały prawdopodobnie nieprawidłowo wybrane. Zobacz dokument README.",
|
||||
"NORMAL": "Normalny",
|
||||
"ROTATE90": "Obrócony o 90°",
|
||||
"ROTATE180": "Obrócony o 180°",
|
||||
"ROTATE270": "Obrócony o 270°",
|
||||
"FLIPPED": "Odwrócony",
|
||||
"FLIP90": "Odwrócony o 90°",
|
||||
"FLIP180": "Odwrócony o 180°",
|
||||
"FLIP270": "Odwrócony o 270°"
|
||||
}
|
||||
},
|
||||
"DISABLED": "Wyłączone"
|
||||
"DISABLED": "Wyłączone",
|
||||
"DEFAULT": "Domyślny",
|
||||
"GRAB": {
|
||||
"ADJUST_DISTANCE": "Dostosuj odległość",
|
||||
"ADJUST_SIZE": "Dostosuj rozmiar",
|
||||
"UNRESTRICTED_MOVEMENT": "Nieograniczony ruch",
|
||||
"GRABBING_WATCH": "Aby zamienić ręce, przenieś zegarek na przód i chwyć go drugą ręką.",
|
||||
"GRABBING_STATIC": "Ta nakładka jest statyczna i pozostanie na swoim miejscu, ignorując centrowanie.",
|
||||
"GRABBING_ANCHORED": "Przymocowane nakładki poruszają się razem. Aby oddzielić pojedyncze okno, chwyć je drugą ręką, podczas gdy nadal trzymasz kotwicę.",
|
||||
"GRABBING_ANCHORED_EDIT": "Ta nakładka pozostanie zakotwiczona do znacznika centralnego.",
|
||||
"GRABBING_FLOATING": "Ta nakładka jest zawieszony i pozostanie na miejscu, chyba że zostanie wycentrowany.",
|
||||
"GRABBING_FOLLOW": "Ta nakładka będzie śledzić urządzenie, do którego jest dołączona."
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "Powiadomienie",
|
||||
"ERROR": "Błąd",
|
||||
"CANNOT_REMOVE_SET": "Nie można usunąć zestawu!",
|
||||
"NO_SET_SELECTED": "Nie wybrano żadnego zestawu.",
|
||||
"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!"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use glam::{Affine3A, Quat, Vec3, Vec3A};
|
||||
use glam::{Affine3A, Quat, Vec3, Vec3A, vec3a};
|
||||
use ovr_overlay::{
|
||||
chaperone_setup::ChaperoneSetupManager,
|
||||
compositor::CompositorManager,
|
||||
@@ -58,12 +58,12 @@ impl PlayspaceMover {
|
||||
&mut self,
|
||||
chaperone_mgr: &mut ChaperoneSetupManager,
|
||||
overlays: &mut OverlayWindowManager<OpenVrOverlayData>,
|
||||
state: &AppState,
|
||||
app: &AppState,
|
||||
) {
|
||||
let universe = self.universe.clone();
|
||||
|
||||
if let Some(data) = self.rotate.as_mut() {
|
||||
let pointer = &state.input_state.pointers[data.hand];
|
||||
let pointer = &app.input_state.pointers[data.hand];
|
||||
if !pointer.now.space_rotate {
|
||||
self.rotate = None;
|
||||
log::info!("End space rotate");
|
||||
@@ -71,7 +71,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
let new_hand =
|
||||
Quat::from_affine3(&(data.pose * state.input_state.pointers[data.hand].raw_pose));
|
||||
Quat::from_affine3(&(data.pose * app.input_state.pointers[data.hand].raw_pose));
|
||||
|
||||
let dq = new_hand * data.hand_pose.conjugate();
|
||||
let rel_y = f32::atan2(
|
||||
@@ -80,8 +80,8 @@ impl PlayspaceMover {
|
||||
);
|
||||
|
||||
let mut space_transform = Affine3A::from_rotation_y(rel_y);
|
||||
let offset = (space_transform.transform_vector3a(state.input_state.hmd.translation)
|
||||
- state.input_state.hmd.translation)
|
||||
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);
|
||||
|
||||
@@ -97,7 +97,7 @@ impl PlayspaceMover {
|
||||
set_working_copy(&universe, chaperone_mgr, &data.pose);
|
||||
chaperone_mgr.commit_working_copy(EChaperoneConfigFile::EChaperoneConfigFile_Live);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
if pointer.now.space_rotate {
|
||||
let Some(mat) = get_working_copy(&universe, chaperone_mgr) else {
|
||||
log::warn!("Can't space rotate - failed to get zero pose");
|
||||
@@ -117,7 +117,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
if let Some(data) = self.drag.as_mut() {
|
||||
let pointer = &state.input_state.pointers[data.hand];
|
||||
let pointer = &app.input_state.pointers[data.hand];
|
||||
if !pointer.now.space_drag {
|
||||
self.drag = None;
|
||||
log::info!("End space drag");
|
||||
@@ -126,9 +126,13 @@ impl PlayspaceMover {
|
||||
|
||||
let new_hand = data
|
||||
.pose
|
||||
.transform_point3a(state.input_state.pointers[data.hand].raw_pose.translation);
|
||||
let relative_pos =
|
||||
(new_hand - data.hand_pose) * state.session.config.space_drag_multiplier;
|
||||
.transform_point3a(app.input_state.pointers[data.hand].raw_pose.translation);
|
||||
|
||||
let relative_pos = if app.session.config.space_drag_unlocked {
|
||||
new_hand - data.hand_pose
|
||||
} else {
|
||||
vec3a(0., new_hand.y - data.hand_pose.y, 0.)
|
||||
} * app.session.config.space_drag_multiplier;
|
||||
|
||||
if relative_pos.length_squared() > 1000.0 {
|
||||
log::warn!("Space drag too fast, ignoring");
|
||||
@@ -156,7 +160,7 @@ impl PlayspaceMover {
|
||||
set_working_copy(&universe, chaperone_mgr, &data.pose);
|
||||
chaperone_mgr.commit_working_copy(EChaperoneConfigFile::EChaperoneConfigFile_Live);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
if pointer.now.space_drag {
|
||||
let Some(mat) = get_working_copy(&universe, chaperone_mgr) else {
|
||||
log::warn!("Can't space drag - failed to get zero pose");
|
||||
|
||||
@@ -11,7 +11,6 @@ use std::{
|
||||
};
|
||||
|
||||
use wgui::gfx::{
|
||||
WGfx,
|
||||
cmd::WGfxClearMode,
|
||||
pass::WGfxPass,
|
||||
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
||||
@@ -49,8 +48,6 @@ static COLORS: [[f32; 6]; 5] = {
|
||||
pub(super) struct LinePool {
|
||||
lines: IdMap<usize, LineContainer>,
|
||||
pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
||||
pass: WGfxPass<Vert2Uv>,
|
||||
buf_color: Subbuffer<[f32]>,
|
||||
}
|
||||
|
||||
impl LinePool {
|
||||
@@ -61,13 +58,22 @@ impl LinePool {
|
||||
WPipelineCreateInfo::new(app.gfx.surface_format),
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
lines: IdMap::new(),
|
||||
pipeline,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn allocate(&mut self, xr: &XrState, app: &AppState) -> anyhow::Result<usize> {
|
||||
let id = LINE_AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let buf_color = app
|
||||
.gfx
|
||||
.empty_buffer(BufferUsage::TRANSFER_DST | BufferUsage::UNIFORM_BUFFER, 6)?;
|
||||
|
||||
let set0 = pipeline.buffer(0, buf_color.clone())?;
|
||||
let set0 = self.pipeline.buffer(0, buf_color.clone())?;
|
||||
|
||||
let pass = pipeline.create_pass(
|
||||
let pass = self.pipeline.create_pass(
|
||||
[1.0, 1.0],
|
||||
app.gfx_extras.quad_verts.clone(),
|
||||
0..4,
|
||||
@@ -76,22 +82,13 @@ impl LinePool {
|
||||
&Default::default(),
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
lines: IdMap::new(),
|
||||
pipeline,
|
||||
pass,
|
||||
buf_color,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn allocate(&mut self, xr: &XrState, gfx: Arc<WGfx>) -> anyhow::Result<usize> {
|
||||
let id = LINE_AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let srd = create_swapchain(xr, gfx, [1, 1, 1], SwapchainOpts::new())?;
|
||||
let srd = create_swapchain(xr, app.gfx.clone(), [1, 1, 1], SwapchainOpts::new())?;
|
||||
self.lines.insert(
|
||||
id,
|
||||
LineContainer {
|
||||
swapchain: srd,
|
||||
buf_color,
|
||||
pass,
|
||||
maybe_line: None,
|
||||
},
|
||||
);
|
||||
@@ -165,13 +162,13 @@ impl LinePool {
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
self.buf_color.write()?[0..6].copy_from_slice(&COLORS[inner.color]);
|
||||
line.buf_color.write()?[0..6].copy_from_slice(&COLORS[inner.color]);
|
||||
|
||||
let mut cmd_buffer = app
|
||||
.gfx
|
||||
.create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_rendering(tgt, WGfxClearMode::DontCare)?;
|
||||
cmd_buffer.run_ref(&self.pass)?;
|
||||
cmd_buffer.run_ref(&line.pass)?;
|
||||
cmd_buffer.end_rendering()?;
|
||||
|
||||
futures.execute(cmd_buffer.queue.clone(), cmd_buffer.build()?)?;
|
||||
@@ -217,5 +214,7 @@ pub(super) struct Line {
|
||||
|
||||
struct LineContainer {
|
||||
swapchain: WlxSwapchain,
|
||||
buf_color: Subbuffer<[f32]>,
|
||||
pass: WGfxPass<Vert2Uv>,
|
||||
maybe_line: Option<Line>,
|
||||
}
|
||||
|
||||
@@ -149,8 +149,8 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
};
|
||||
|
||||
let pointer_lines = [
|
||||
lines.allocate(&xr_state, app.gfx.clone())?,
|
||||
lines.allocate(&xr_state, app.gfx.clone())?,
|
||||
lines.allocate(&xr_state, &mut app)?,
|
||||
lines.allocate(&xr_state, &mut app)?,
|
||||
];
|
||||
|
||||
let watch_id = overlays.lookup(WATCH_NAME).unwrap(); // want panic
|
||||
@@ -238,6 +238,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
|
||||
if !session_running {
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
log::trace!("session not running: continue");
|
||||
continue 'main_loop;
|
||||
}
|
||||
|
||||
@@ -387,10 +388,12 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
for o in overlays.values_mut() {
|
||||
o.data.cur_visible = false;
|
||||
let Some(alpha) = o.config.active_state.as_ref().map(|x| x.alpha) else {
|
||||
log::trace!("{}: hidden, skip render", o.config.name);
|
||||
continue;
|
||||
};
|
||||
|
||||
if !o.data.init {
|
||||
log::trace!("{}: init", o.config.name);
|
||||
o.init(&mut app)?;
|
||||
o.data.init = true;
|
||||
}
|
||||
@@ -400,8 +403,10 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
ShouldRender::Can => (o.data.last_alpha - alpha).abs() > f32::EPSILON,
|
||||
ShouldRender::Unable => false, //try show old image if exists
|
||||
};
|
||||
log::trace!("{}: should_render returned: {should_render}", o.config.name);
|
||||
|
||||
if should_render {
|
||||
log::trace!("{}: render new frame", o.config.name);
|
||||
let meta = o.config.backend.frame_meta().unwrap(); // want panic
|
||||
let wsi = o.ensure_swapchain_acquire(&app, &xr_state, meta.extent)?;
|
||||
let tgt = RenderTarget { views: wsi.views };
|
||||
@@ -410,7 +415,10 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
o.data.last_alpha = alpha;
|
||||
futures.execute_results(rdr.end()?)?;
|
||||
} else if o.data.swapchain.is_none() {
|
||||
log::trace!("{}: not showing due to missing swapchain", o.config.name);
|
||||
continue;
|
||||
} else {
|
||||
log::trace!("{}: showing stale frame", o.config.name);
|
||||
}
|
||||
o.data.cur_visible = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use glam::{Affine3A, Quat, Vec3A};
|
||||
use glam::{Affine3A, Quat, Vec3A, vec3a};
|
||||
use libmonado::{Monado, Pose, ReferenceSpaceType};
|
||||
|
||||
use crate::{
|
||||
@@ -60,10 +60,10 @@ impl PlayspaceMover {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
overlays: &mut OverlayWindowManager<OpenXrOverlayData>,
|
||||
state: &AppState,
|
||||
app: &AppState,
|
||||
monado: &mut Monado,
|
||||
) {
|
||||
for pointer in &state.input_state.pointers {
|
||||
for pointer in &app.input_state.pointers {
|
||||
if pointer.now.space_reset {
|
||||
if !pointer.before.space_reset {
|
||||
log::info!("Space reset");
|
||||
@@ -74,7 +74,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
if let Some(mut data) = self.rotate.take() {
|
||||
let pointer = &state.input_state.pointers[data.hand];
|
||||
let pointer = &app.input_state.pointers[data.hand];
|
||||
if !pointer.now.space_rotate {
|
||||
self.last_transform = data.pose;
|
||||
log::info!("End space rotate");
|
||||
@@ -82,10 +82,10 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
let new_hand =
|
||||
Quat::from_affine3(&(data.pose * state.input_state.pointers[data.hand].raw_pose));
|
||||
Quat::from_affine3(&(data.pose * app.input_state.pointers[data.hand].raw_pose));
|
||||
|
||||
let dq = new_hand * data.hand_pose.conjugate();
|
||||
let mut space_transform = if state.session.config.space_rotate_unlocked {
|
||||
let mut space_transform = if app.session.config.space_rotate_unlocked {
|
||||
Affine3A::from_quat(dq)
|
||||
} else {
|
||||
let rel_y = f32::atan2(
|
||||
@@ -95,8 +95,8 @@ impl PlayspaceMover {
|
||||
|
||||
Affine3A::from_rotation_y(rel_y)
|
||||
};
|
||||
let offset = (space_transform.transform_vector3a(state.input_state.hmd.translation)
|
||||
- state.input_state.hmd.translation)
|
||||
let offset = (space_transform.transform_vector3a(app.input_state.hmd.translation)
|
||||
- app.input_state.hmd.translation)
|
||||
* -1.0;
|
||||
|
||||
space_transform.translation = offset;
|
||||
@@ -107,7 +107,7 @@ impl PlayspaceMover {
|
||||
apply_offset(data.pose, monado);
|
||||
self.rotate = Some(data);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
if pointer.now.space_rotate {
|
||||
let hand_pose = Quat::from_affine3(&(self.last_transform * pointer.raw_pose));
|
||||
self.rotate = Some(MoverData {
|
||||
@@ -123,7 +123,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
if let Some(mut data) = self.drag.take() {
|
||||
let pointer = &state.input_state.pointers[data.hand];
|
||||
let pointer = &app.input_state.pointers[data.hand];
|
||||
if !pointer.now.space_drag {
|
||||
self.last_transform = data.pose;
|
||||
log::info!("End space drag");
|
||||
@@ -132,9 +132,13 @@ impl PlayspaceMover {
|
||||
|
||||
let new_hand = data
|
||||
.pose
|
||||
.transform_point3a(state.input_state.pointers[data.hand].raw_pose.translation);
|
||||
let relative_pos =
|
||||
(new_hand - data.hand_pose) * state.session.config.space_drag_multiplier;
|
||||
.transform_point3a(app.input_state.pointers[data.hand].raw_pose.translation);
|
||||
|
||||
let relative_pos = if app.session.config.space_drag_unlocked {
|
||||
new_hand - data.hand_pose
|
||||
} else {
|
||||
vec3a(0., new_hand.y - data.hand_pose.y, 0.)
|
||||
} * app.session.config.space_drag_multiplier;
|
||||
|
||||
if relative_pos.length_squared() > 1000.0 {
|
||||
log::warn!("Space drag too fast, ignoring");
|
||||
@@ -159,7 +163,7 @@ impl PlayspaceMover {
|
||||
apply_offset(data.pose, monado);
|
||||
self.drag = Some(data);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
if pointer.now.space_drag {
|
||||
let hand_pos = self
|
||||
.last_transform
|
||||
@@ -234,7 +238,7 @@ impl PlayspaceMover {
|
||||
|
||||
let y1 = input.pointers[0].raw_pose.translation.y;
|
||||
let y2 = input.pointers[1].raw_pose.translation.y;
|
||||
let delta = y1.min(y2) - 0.03;
|
||||
let delta = y1.min(y2) - 0.05;
|
||||
|
||||
pose.position.y += delta;
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ pub fn dds_to_vk(dds_fmt: ImageFormat) -> anyhow::Result<Format> {
|
||||
ImageFormat::Rgba8UnormSrgb => Ok(Format::R8G8B8A8_SRGB),
|
||||
ImageFormat::Rgba16Float => Ok(Format::R16G16B16A16_SFLOAT),
|
||||
ImageFormat::Rgba32Float => Ok(Format::R32G32B32A32_SFLOAT),
|
||||
ImageFormat::Bgr8Unorm => Ok(Format::B8G8R8_UNORM),
|
||||
ImageFormat::Bgra8Unorm => Ok(Format::B8G8R8A8_UNORM),
|
||||
ImageFormat::Bgra8UnormSrgb => Ok(Format::B8G8R8A8_SRGB),
|
||||
// DXT1
|
||||
|
||||
@@ -81,6 +81,17 @@ Supported events:
|
||||
<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 ..]`
|
||||
|
||||
@@ -10,7 +10,9 @@ use std::{
|
||||
use anyhow::Context;
|
||||
use wgui::{
|
||||
components::button::ComponentButton,
|
||||
event::{self, EventCallback, EventListenerKind},
|
||||
event::{
|
||||
self, CallbackData, CallbackMetadata, EventCallback, EventListenerKind, MouseButtonIndex,
|
||||
},
|
||||
i18n::Translation,
|
||||
layout::Layout,
|
||||
parser::CustomAttribsInfoOwned,
|
||||
@@ -31,18 +33,159 @@ use crate::{
|
||||
#[cfg(feature = "wayvr")]
|
||||
use crate::backend::wayvr::WayVRAction;
|
||||
|
||||
pub const BUTTON_EVENTS: [(&str, EventListenerKind); 2] = [
|
||||
("_press", EventListenerKind::MousePress),
|
||||
("_release", EventListenerKind::MouseRelease),
|
||||
pub const BUTTON_EVENTS: [(
|
||||
&str,
|
||||
EventListenerKind,
|
||||
fn(&mut CallbackData) -> bool,
|
||||
fn(&ComponentButton, &AppState) -> bool,
|
||||
); 16] = [
|
||||
(
|
||||
"_press",
|
||||
EventListenerKind::MousePress,
|
||||
button_any,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_release",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_any,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_press_left",
|
||||
EventListenerKind::MousePress,
|
||||
button_left,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_release_left",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_left,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_press_right",
|
||||
EventListenerKind::MousePress,
|
||||
button_right,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_release_right",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_right,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_press_middle",
|
||||
EventListenerKind::MousePress,
|
||||
button_middle,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_release_middle",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_middle,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_short_release",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_any,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_short_release_left",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_left,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_short_release_right",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_right,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_short_release_middle",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_middle,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_long_release",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_any,
|
||||
long_duration,
|
||||
),
|
||||
(
|
||||
"_long_release_left",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_left,
|
||||
long_duration,
|
||||
),
|
||||
(
|
||||
"_long_release_right",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_right,
|
||||
long_duration,
|
||||
),
|
||||
(
|
||||
"_long_release_middle",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_middle,
|
||||
long_duration,
|
||||
),
|
||||
];
|
||||
|
||||
fn button_any(_: &mut CallbackData) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn button_left(data: &mut CallbackData) -> bool {
|
||||
if let CallbackMetadata::MouseButton(b) = data.metadata
|
||||
&& let MouseButtonIndex::Left = b.index
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn button_right(data: &mut CallbackData) -> bool {
|
||||
if let CallbackMetadata::MouseButton(b) = data.metadata
|
||||
&& let MouseButtonIndex::Right = b.index
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn button_middle(data: &mut CallbackData) -> bool {
|
||||
if let CallbackMetadata::MouseButton(b) = data.metadata
|
||||
&& let MouseButtonIndex::Middle = b.index
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn ignore_duration(_btn: &ComponentButton, _app: &AppState) -> bool {
|
||||
true
|
||||
}
|
||||
fn long_duration(btn: &ComponentButton, app: &AppState) -> bool {
|
||||
btn.get_time_since_last_pressed().as_secs_f32() > app.session.config.long_press_duration
|
||||
}
|
||||
fn short_duration(btn: &ComponentButton, app: &AppState) -> bool {
|
||||
btn.get_time_since_last_pressed().as_secs_f32() < app.session.config.long_press_duration
|
||||
}
|
||||
|
||||
pub(super) fn setup_custom_button<S: 'static>(
|
||||
layout: &mut Layout,
|
||||
attribs: &CustomAttribsInfoOwned,
|
||||
_app: &AppState,
|
||||
button: Rc<ComponentButton>,
|
||||
) {
|
||||
for (name, kind) in &BUTTON_EVENTS {
|
||||
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
|
||||
let Some(action) = attribs.get_value(name) else {
|
||||
continue;
|
||||
};
|
||||
@@ -52,9 +195,15 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
continue;
|
||||
};
|
||||
|
||||
let button = button.clone();
|
||||
|
||||
let callback: EventCallback<AppState, S> = match command {
|
||||
#[cfg(feature = "wayvr")]
|
||||
"::DashToggle" => Box::new(move |_common, _data, app, _| {
|
||||
"::DashToggle" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::WayVR(WayVRAction::ToggleDashboard));
|
||||
Ok(EventResult::Consumed)
|
||||
@@ -65,7 +214,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(set_idx)));
|
||||
Ok(EventResult::Consumed)
|
||||
@@ -77,7 +230,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
return;
|
||||
};
|
||||
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
OverlaySelector::Name(arg.clone()),
|
||||
Box::new(move |app, owc| {
|
||||
@@ -91,54 +248,84 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::EditToggle" => Box::new(move |_common, _data, app, _| {
|
||||
"::EditToggle" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::ToggleEditMode));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
#[cfg(feature = "wayland")]
|
||||
"::NewMirror" => Box::new(move |_common, _data, app, _| {
|
||||
let name = crate::overlays::mirror::new_mirror_name();
|
||||
"::NewMirror" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let name = crate::overlays::screen::mirror::new_mirror_name();
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Create(
|
||||
OverlaySelector::Name(name.clone()),
|
||||
Box::new(move |app| {
|
||||
Some(crate::overlays::mirror::new_mirror(name, &app.session))
|
||||
Some(crate::overlays::screen::mirror::new_mirror(
|
||||
name,
|
||||
&app.session,
|
||||
))
|
||||
}),
|
||||
)));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::CleanupMirrors" => Box::new(move |_common, _data, app, _| {
|
||||
"::CleanupMirrors" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::CleanupMirrors));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::PlayspaceReset" => Box::new(move |_common, _data, app, _| {
|
||||
"::PlayspaceReset" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks.enqueue(TaskType::Playspace(PlayspaceTask::Reset));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::PlayspaceRecenter" => Box::new(move |_common, _data, app, _| {
|
||||
"::PlayspaceRecenter" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Playspace(PlayspaceTask::Recenter));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::PlayspaceFixFloor" => Box::new(move |_common, _data, app, _| {
|
||||
for i in 0..5 {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
format!("Fixing floor in {}", 5 - i),
|
||||
"Touch your controller to the floor!".into(),
|
||||
)
|
||||
.with_timeout(1.)
|
||||
.with_sound(true)
|
||||
.submit_at(app, Instant::now() + Duration::from_secs(i));
|
||||
"::PlayspaceFixFloor" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
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);
|
||||
|
||||
app.tasks.enqueue_at(
|
||||
TaskType::Playspace(PlayspaceTask::FixFloor),
|
||||
Instant::now() + Duration::from_secs(5),
|
||||
);
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::Shutdown" => Box::new(move |_common, _data, _app, _| {
|
||||
"::Shutdown" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
RUNNING.store(false, Ordering::Relaxed);
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
@@ -155,7 +342,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
log::error!("{command} has bad/missing arguments");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.hid_provider.send_key_routed(key, down);
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
@@ -182,7 +373,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
}),
|
||||
);
|
||||
|
||||
Box::new(move |_common, _data, _app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let _ = shell_on_action(&state).inspect_err(|e| log::error!("{e:?}"));
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
@@ -206,7 +401,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
osc_args.push(osc_arg);
|
||||
}
|
||||
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let Some(sender) = app.osc_sender.as_mut() else {
|
||||
log::error!("OscSend: sender is not available.");
|
||||
return Ok(EventResult::Consumed);
|
||||
|
||||
@@ -70,7 +70,8 @@ pub type OnCustomIdFunc<S> = Box<
|
||||
) -> anyhow::Result<()>,
|
||||
>;
|
||||
|
||||
pub type OnCustomAttribFunc = Box<dyn Fn(&mut Layout, &CustomAttribsInfoOwned, &AppState)>;
|
||||
pub type OnCustomAttribFunc =
|
||||
Box<dyn Fn(&mut Layout, &ParserState, &CustomAttribsInfoOwned, &AppState)>;
|
||||
|
||||
pub struct NewGuiPanelParams<S> {
|
||||
pub on_custom_id: Option<OnCustomIdFunc<S>>, // used only in `new_from_template`
|
||||
@@ -155,7 +156,7 @@ impl<S: 'static> GuiPanel<S> {
|
||||
}
|
||||
|
||||
if let Some(on_custom_attrib) = ¶ms.on_custom_attrib {
|
||||
on_custom_attrib(&mut layout, elem, app);
|
||||
on_custom_attrib(&mut layout, &parser_state, elem, app);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use anyhow::Context;
|
||||
use glam::FloatExt;
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
components::button::ComponentButton,
|
||||
event::CallbackDataCommon,
|
||||
layout::WidgetID,
|
||||
parser::Fetchable,
|
||||
@@ -15,6 +18,7 @@ pub(super) struct InteractLockHandler {
|
||||
id: WidgetID,
|
||||
color: wgui::drawing::Color,
|
||||
interactable: bool,
|
||||
button: Option<Rc<ComponentButton>>,
|
||||
}
|
||||
|
||||
impl InteractLockHandler {
|
||||
@@ -27,10 +31,13 @@ impl InteractLockHandler {
|
||||
.get_as::<WidgetRectangle>(id)
|
||||
.context("Element with id=\"shadow\" must be a <rectangle>")?;
|
||||
|
||||
let button = panel.parser_state.fetch_component_as("top_lock")?;
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
color: shadow_rect.params.color,
|
||||
interactable: true,
|
||||
button: Some(button),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,6 +49,10 @@ impl InteractLockHandler {
|
||||
.get_as::<WidgetRectangle>(self.id)
|
||||
.unwrap(); // can only fail if set_up_rect has issues
|
||||
|
||||
if let Some(button) = self.button.as_ref() {
|
||||
button.set_sticky_state(common, !interactable);
|
||||
}
|
||||
|
||||
let globals = common.state.globals.get();
|
||||
if interactable {
|
||||
set_anim_color(&mut rect, 0.0, self.color, globals.defaults.danger_color);
|
||||
@@ -54,16 +65,20 @@ impl InteractLockHandler {
|
||||
&mut self,
|
||||
common: &mut CallbackDataCommon,
|
||||
app: &mut AppState,
|
||||
anim_mult: f32,
|
||||
) -> Box<ModifyOverlayTask> {
|
||||
let defaults = app.wgui_globals.get().defaults.clone();
|
||||
let rect_color = self.color;
|
||||
|
||||
self.interactable = !self.interactable;
|
||||
if let Some(button) = self.button.as_ref() {
|
||||
button.set_sticky_state(common, !self.interactable);
|
||||
}
|
||||
|
||||
let anim = if self.interactable {
|
||||
Animation::new(
|
||||
self.id,
|
||||
10,
|
||||
(10. * anim_mult) as _,
|
||||
AnimationEasing::OutQuad,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
@@ -79,7 +94,7 @@ impl InteractLockHandler {
|
||||
} else {
|
||||
Animation::new(
|
||||
self.id,
|
||||
10,
|
||||
(10. * anim_mult) as _,
|
||||
AnimationEasing::OutBack,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
|
||||
@@ -47,21 +47,9 @@ mod sprite_tab;
|
||||
mod stereo;
|
||||
pub mod tab;
|
||||
|
||||
pub(super) struct LongPressButtonState {
|
||||
pub(super) pressed: Instant,
|
||||
}
|
||||
impl Default for LongPressButtonState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pressed: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EditModeState {
|
||||
tasks: Rc<RefCell<TaskContainer>>,
|
||||
id: Rc<RefCell<OverlayID>>,
|
||||
delete: LongPressButtonState,
|
||||
tabs: ButtonPaneTabSwitcher,
|
||||
lock: InteractLockHandler,
|
||||
pos: SpriteTabHandler<PosTabState>,
|
||||
@@ -272,9 +260,6 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
let state = EditModeState {
|
||||
id: Rc::new(RefCell::new(OverlayID::null())),
|
||||
tasks: Rc::new(RefCell::new(TaskContainer::new())),
|
||||
delete: LongPressButtonState {
|
||||
pressed: Instant::now(),
|
||||
},
|
||||
tabs: ButtonPaneTabSwitcher::default(),
|
||||
lock: InteractLockHandler::default(),
|
||||
pos: SpriteTabHandler::default(),
|
||||
@@ -282,8 +267,16 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
mouse: SpriteTabHandler::default(),
|
||||
};
|
||||
|
||||
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, attribs, _app| {
|
||||
for (name, kind) in &BUTTON_EVENTS {
|
||||
let anim_mult = app.wgui_globals.defaults().animation_mult;
|
||||
|
||||
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| {
|
||||
let Ok(button) =
|
||||
parser.fetch_component_from_widget_id_as::<ComponentButton>(attribs.widget_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
|
||||
let Some(action) = attribs.get_value(name) else {
|
||||
continue;
|
||||
};
|
||||
@@ -293,15 +286,25 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
continue;
|
||||
};
|
||||
|
||||
let button = button.clone();
|
||||
|
||||
let callback: EventCallback<AppState, EditModeState> = match command {
|
||||
"::EditModeToggleLock" => Box::new(move |common, _data, app, state| {
|
||||
"::EditModeToggleLock" => Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.lock.toggle(common, app);
|
||||
let task = state.lock.toggle(common, app, anim_mult);
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::Modify(sel, task)));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeToggleGrab" => Box::new(move |_common, _data, app, state| {
|
||||
"::EditModeToggleGrab" => Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
sel,
|
||||
@@ -314,14 +317,22 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
}),
|
||||
"::EditModeTab" => {
|
||||
let tab_name = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, _app, state| {
|
||||
Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
state.tabs.tab_button_clicked(common, &tab_name);
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::EditModeSetPos" => {
|
||||
let key = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, app, state| {
|
||||
Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.pos.button_clicked(common, &key);
|
||||
app.tasks
|
||||
@@ -331,7 +342,11 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
}
|
||||
"::EditModeSetStereo" => {
|
||||
let key = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, app, state| {
|
||||
Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.stereo.button_clicked(common, &key);
|
||||
app.tasks
|
||||
@@ -341,7 +356,11 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
}
|
||||
"::EditModeSetMouse" => {
|
||||
let key = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, app, state| {
|
||||
Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.mouse.button_clicked(common, &key);
|
||||
app.tasks
|
||||
@@ -349,15 +368,11 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::EditModeDeletePress" => Box::new(move |_common, _data, _app, state| {
|
||||
state.delete.pressed = Instant::now();
|
||||
// TODO: animate to light up button after 2s
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeDeleteRelease" => Box::new(move |_common, _data, app, state| {
|
||||
if state.delete.pressed.elapsed() < Duration::from_secs(1) {
|
||||
"::EditModeDelete" => Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
OverlaySelector::Id(*state.id.borrow()),
|
||||
Box::new(move |_app, owc| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::{gui::panel::GuiPanel, state::AppState, subsystem::hid::XkbKeymap};
|
||||
use glam::{FloatExt, Mat4, Vec2, Vec3, vec2};
|
||||
use glam::{FloatExt, Mat4, Vec2, vec2, vec3};
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
assets::AssetPath,
|
||||
@@ -39,11 +39,13 @@ pub(super) fn create_keyboard_panel(
|
||||
let globals = app.wgui_globals.clone();
|
||||
let accent_color = globals.get().defaults.accent_color;
|
||||
|
||||
let anim_mult = globals.defaults().animation_mult;
|
||||
|
||||
let (background, _) = panel.layout.add_child(
|
||||
panel.layout.content_root_widget,
|
||||
WidgetRectangle::create(WidgetRectangleParams {
|
||||
color: wgui::drawing::Color::new(0., 0., 0., 0.75),
|
||||
round: WLength::Units(16.0),
|
||||
color: globals.defaults().bg_color,
|
||||
round: WLength::Units((16.0 * globals.defaults().rounding_mult).max(0.)),
|
||||
border: 2.0,
|
||||
border_color: accent_color,
|
||||
..Default::default()
|
||||
@@ -169,6 +171,8 @@ pub(super) fn create_keyboard_panel(
|
||||
})
|
||||
};
|
||||
|
||||
let width_mul = 1. / my_size_f32;
|
||||
|
||||
panel.add_event_listener(
|
||||
widget_id,
|
||||
EventListenerKind::MouseEnter,
|
||||
@@ -176,7 +180,14 @@ pub(super) fn create_keyboard_panel(
|
||||
let k = key_state.clone();
|
||||
move |common, data, _app, _state| {
|
||||
common.alterables.trigger_haptics();
|
||||
on_enter_anim(k.clone(), common, data, accent_color);
|
||||
on_enter_anim(
|
||||
k.clone(),
|
||||
common,
|
||||
data,
|
||||
accent_color,
|
||||
anim_mult,
|
||||
width_mul,
|
||||
);
|
||||
Ok(EventResult::Pass)
|
||||
}
|
||||
}),
|
||||
@@ -188,7 +199,14 @@ pub(super) fn create_keyboard_panel(
|
||||
let k = key_state.clone();
|
||||
move |common, data, _app, _state| {
|
||||
common.alterables.trigger_haptics();
|
||||
on_leave_anim(k.clone(), common, data, accent_color);
|
||||
on_leave_anim(
|
||||
k.clone(),
|
||||
common,
|
||||
data,
|
||||
accent_color,
|
||||
anim_mult,
|
||||
width_mul,
|
||||
);
|
||||
Ok(EventResult::Pass)
|
||||
}
|
||||
}),
|
||||
@@ -254,11 +272,14 @@ pub(super) fn create_keyboard_panel(
|
||||
|
||||
const BUTTON_HOVER_SCALE: f32 = 0.1;
|
||||
|
||||
fn get_anim_transform(pos: f32, widget_size: Vec2) -> Mat4 {
|
||||
util::centered_matrix(
|
||||
widget_size,
|
||||
&Mat4::from_scale(Vec3::splat(BUTTON_HOVER_SCALE.mul_add(pos, 1.0))),
|
||||
)
|
||||
fn get_anim_transform(pos: f32, widget_size: Vec2, width_mult: f32) -> Mat4 {
|
||||
let scale = vec3(
|
||||
(BUTTON_HOVER_SCALE * width_mult).mul_add(pos, 1.0),
|
||||
BUTTON_HOVER_SCALE.mul_add(pos, 1.0),
|
||||
1.0,
|
||||
);
|
||||
|
||||
util::centered_matrix(widget_size, &Mat4::from_scale(scale))
|
||||
}
|
||||
|
||||
fn set_anim_color(
|
||||
@@ -291,15 +312,18 @@ fn on_enter_anim(
|
||||
common: &mut event::CallbackDataCommon,
|
||||
data: &event::CallbackData,
|
||||
accent_color: drawing::Color,
|
||||
anim_mult: f32,
|
||||
width_mult: f32,
|
||||
) {
|
||||
common.alterables.animate(Animation::new(
|
||||
data.widget_id,
|
||||
10,
|
||||
(10. * anim_mult) as _,
|
||||
AnimationEasing::OutBack,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
set_anim_color(&key_state, rect, data.pos, accent_color);
|
||||
data.data.transform = get_anim_transform(data.pos, data.widget_boundary.size);
|
||||
data.data.transform =
|
||||
get_anim_transform(data.pos, data.widget_boundary.size, width_mult);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
));
|
||||
@@ -310,15 +334,18 @@ fn on_leave_anim(
|
||||
common: &mut event::CallbackDataCommon,
|
||||
data: &event::CallbackData,
|
||||
accent_color: drawing::Color,
|
||||
anim_mult: f32,
|
||||
width_mult: f32,
|
||||
) {
|
||||
common.alterables.animate(Animation::new(
|
||||
data.widget_id,
|
||||
15,
|
||||
(15. * anim_mult) as _,
|
||||
AnimationEasing::OutQuad,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
set_anim_color(&key_state, rect, 1.0 - data.pos, accent_color);
|
||||
data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_boundary.size);
|
||||
data.data.transform =
|
||||
get_anim_transform(1.0 - data.pos, data.widget_boundary.size, width_mult);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
));
|
||||
|
||||
@@ -2,8 +2,6 @@ pub mod anchor;
|
||||
pub mod custom;
|
||||
pub mod edit;
|
||||
pub mod keyboard;
|
||||
#[cfg(feature = "wayland")]
|
||||
pub mod mirror;
|
||||
pub mod screen;
|
||||
pub mod toast;
|
||||
pub mod watch;
|
||||
|
||||
@@ -215,6 +215,7 @@ impl OverlayBackend for ScreenBackend {
|
||||
Ok(ShouldRender::Can)
|
||||
}
|
||||
} else {
|
||||
log::trace!("{}: backend ready, but no image received.", self.name);
|
||||
Ok(ShouldRender::Unable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ use std::{
|
||||
|
||||
use futures::{Future, FutureExt};
|
||||
use glam::{Affine2, Affine3A, Quat, Vec3, vec3};
|
||||
use wlx_capture::pipewire::{PipewireCapture, PipewireSelectScreenResult, pipewire_select_screen};
|
||||
use wlx_capture::{
|
||||
WlxCapture,
|
||||
pipewire::{PipewireCapture, PipewireSelectScreenResult, pipewire_select_screen},
|
||||
};
|
||||
use wlx_common::{
|
||||
overlays::{BackendAttrib, BackendAttribValue},
|
||||
windowing::OverlayWindowState,
|
||||
@@ -19,6 +22,7 @@ use crate::{
|
||||
input::{HoverResult, PointerHit},
|
||||
task::{OverlayTask, TaskType},
|
||||
},
|
||||
overlays::screen::capture::{MainThreadWlxCapture, new_wlx_capture},
|
||||
state::{AppSession, AppState},
|
||||
subsystem::hid::WheelDelta,
|
||||
windowing::{
|
||||
@@ -31,7 +35,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::screen::backend::ScreenBackend;
|
||||
use super::backend::ScreenBackend;
|
||||
type PinnedSelectorFuture = core::pin::Pin<
|
||||
Box<dyn Future<Output = Result<PipewireSelectScreenResult, wlx_capture::pipewire::AshpdError>>>,
|
||||
>;
|
||||
@@ -76,13 +80,21 @@ impl OverlayBackend for MirrorBackend {
|
||||
|
||||
match maybe_pw_result {
|
||||
Ok(pw_result) => {
|
||||
log::debug!(
|
||||
"{}: PipeWire result streams: {:?}",
|
||||
self.name,
|
||||
&pw_result.streams
|
||||
);
|
||||
let node_id = pw_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element
|
||||
log::info!("{}: PipeWire node selected: {}", self.name.clone(), node_id);
|
||||
let capture = PipewireCapture::new(self.name.clone(), node_id);
|
||||
log::info!("{}: PipeWire node selected: {}", self.name, node_id);
|
||||
let capture = new_wlx_capture!(
|
||||
app.gfx_extras.queue_capture,
|
||||
PipewireCapture::new(self.name.clone(), node_id)
|
||||
);
|
||||
self.renderer = Some(ScreenBackend::new_raw(
|
||||
self.name.clone(),
|
||||
app.xr_backend,
|
||||
Box::new(capture),
|
||||
capture,
|
||||
));
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
OverlaySelector::Name(self.name.clone()),
|
||||
@@ -15,6 +15,8 @@ use crate::{
|
||||
|
||||
pub mod backend;
|
||||
mod capture;
|
||||
#[cfg(feature = "wayland")]
|
||||
pub mod mirror;
|
||||
#[cfg(feature = "pipewire")]
|
||||
pub mod pw;
|
||||
#[cfg(feature = "wayland")]
|
||||
|
||||
@@ -42,6 +42,12 @@ impl ScreenBackend {
|
||||
false,
|
||||
)?;
|
||||
|
||||
log::debug!(
|
||||
"{}: PipeWire result streams: {:?}",
|
||||
output.name,
|
||||
&select_screen_result.streams
|
||||
);
|
||||
|
||||
let node_id = select_screen_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element
|
||||
|
||||
let capture = new_wlx_capture!(
|
||||
|
||||
@@ -16,7 +16,6 @@ use wlx_common::{
|
||||
use crate::{
|
||||
backend::task::{OverlayTask, TaskType},
|
||||
gui::panel::{GuiPanel, NewGuiPanelParams, OnCustomIdFunc},
|
||||
overlays::watch::{WATCH_POS, WATCH_ROT},
|
||||
state::AppState,
|
||||
windowing::{OverlaySelector, Z_ORDER_TOAST, window::OverlayWindowConfig},
|
||||
};
|
||||
@@ -121,35 +120,29 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
||||
Positioning::FollowHead { lerp: 0.1 },
|
||||
),
|
||||
ToastDisplayMethod::Watch => {
|
||||
//FIXME: properly follow watch
|
||||
let watch_pos = WATCH_POS + vec3(-0.005, -0.05, 0.02);
|
||||
let watch_rot = WATCH_ROT;
|
||||
let relative_to = /*match app.session.config.watch_hand {
|
||||
LeftRight::Left =>*/ Positioning::FollowHand {
|
||||
hand: LeftRight::Left,
|
||||
lerp: 1.0,
|
||||
align_to_hmd: true,
|
||||
/*
|
||||
},
|
||||
LeftRight::Right => {
|
||||
watch_pos.x = -watch_pos.x;
|
||||
watch_rot = watch_rot * Quat::from_rotation_x(PI) * Quat::from_rotation_z(PI);
|
||||
Positioning::FollowHand {
|
||||
hand: LeftRight::Right,
|
||||
lerp: 1.0,
|
||||
}
|
||||
}*/
|
||||
let relative_to = Positioning::FollowHand {
|
||||
hand: LeftRight::Left,
|
||||
lerp: 0.1,
|
||||
align_to_hmd: true,
|
||||
};
|
||||
(watch_pos, watch_rot, relative_to)
|
||||
(vec3(0., 0., 0.), Quat::IDENTITY, relative_to)
|
||||
}
|
||||
};
|
||||
|
||||
let title = if toast.title.is_empty() {
|
||||
Translation::from_translation_key("TOAST.DEFAULT_TITLE")
|
||||
} else if matches!(toast.topic, ToastTopic::System | ToastTopic::Error) {
|
||||
Translation::from_translation_key(&toast.title)
|
||||
} else {
|
||||
Translation::from_raw_text(&toast.title)
|
||||
};
|
||||
|
||||
let body = if matches!(toast.topic, ToastTopic::System) {
|
||||
Translation::from_translation_key(&toast.body)
|
||||
} else {
|
||||
Translation::from_raw_text(&toast.body)
|
||||
};
|
||||
|
||||
let on_custom_id: OnCustomIdFunc<()> =
|
||||
Box::new(move |id, widget, _doc_params, layout, _parser_state, ()| {
|
||||
if &*id == "toast_title" {
|
||||
@@ -168,7 +161,7 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
||||
.get_as::<WidgetLabel>(widget)
|
||||
.context("toast.xml: missing element with id: toast_body")?;
|
||||
let mut globals = layout.state.globals.get();
|
||||
label.set_text_simple(&mut globals, Translation::from_raw_text(&toast.body));
|
||||
label.set_text_simple(&mut globals, body.clone());
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
@@ -206,7 +199,7 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
||||
}
|
||||
|
||||
fn msg_err(app: &mut AppState, message: &str) {
|
||||
Toast::new(ToastTopic::System, "Error".into(), message.into())
|
||||
Toast::new(ToastTopic::Error, "TOAST.ERROR".into(), message.into())
|
||||
.with_timeout(3.)
|
||||
.submit(app);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use std::{collections::HashMap, rc::Rc, time::Duration};
|
||||
|
||||
use glam::{Affine3A, Quat, Vec3, Vec3A, vec3};
|
||||
use idmap::{DirectIdMap, ordered::Keys};
|
||||
use idmap::DirectIdMap;
|
||||
use slotmap::SecondaryMap;
|
||||
use wgui::{
|
||||
components::button::ComponentButton,
|
||||
@@ -31,7 +27,6 @@ use crate::{
|
||||
panel::{GuiPanel, NewGuiPanelParams, OnCustomAttribFunc, button::BUTTON_EVENTS},
|
||||
timer::GuiTimer,
|
||||
},
|
||||
overlays::edit::LongPressButtonState,
|
||||
state::AppState,
|
||||
windowing::{
|
||||
OverlayID, OverlaySelector, Z_ORDER_WATCH,
|
||||
@@ -70,7 +65,6 @@ struct WatchState {
|
||||
keyboard_oid: OverlayID,
|
||||
dashboard_oid: OverlayID,
|
||||
num_sets: usize,
|
||||
delete: LongPressButtonState,
|
||||
}
|
||||
|
||||
#[allow(clippy::significant_drop_tightening)]
|
||||
@@ -78,8 +72,14 @@ struct WatchState {
|
||||
pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
let state = WatchState::default();
|
||||
|
||||
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, attribs, _app| {
|
||||
for (name, kind) in &BUTTON_EVENTS {
|
||||
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| {
|
||||
let Ok(button) =
|
||||
parser.fetch_component_from_widget_id_as::<ComponentButton>(attribs.widget_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
|
||||
let Some(action) = attribs.get_value(name) else {
|
||||
continue;
|
||||
};
|
||||
@@ -89,20 +89,23 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
continue;
|
||||
};
|
||||
|
||||
let button = button.clone();
|
||||
|
||||
let callback: EventCallback<AppState, WatchState> = match command {
|
||||
"::EditModeDeleteDown" => Box::new(move |_common, _data, _app, state| {
|
||||
state.delete.pressed = Instant::now();
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeDeleteUp" => Box::new(move |_common, _data, app, state| {
|
||||
if state.delete.pressed.elapsed() < Duration::from_secs(1) {
|
||||
return Ok(EventResult::Consumed);
|
||||
"::EditModeDeleteSet" => Box::new(move |_common, data, app, _state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::DeleteActiveSet));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeAddSet" => Box::new(move |_common, _data, app, _state| {
|
||||
"::EditModeAddSet" => Box::new(move |_common, data, app, _state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::AddSet));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
@@ -112,7 +115,11 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, state| {
|
||||
Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let Some(overlay) = state.overlay_metas.get(idx) else {
|
||||
log::error!("No overlay at index {idx}.");
|
||||
return Ok(EventResult::Consumed);
|
||||
@@ -137,7 +144,11 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, state| {
|
||||
Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let Some(overlay) = state.overlay_metas.get(idx) else {
|
||||
log::error!("No overlay at index {idx}.");
|
||||
return Ok(EventResult::Consumed);
|
||||
@@ -150,6 +161,29 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::SingleSetOverlayReset" => {
|
||||
let arg = args.next().unwrap_or_default();
|
||||
let Ok(idx) = arg.parse::<usize>() else {
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let Some(overlay) = state.overlay_metas.get(idx) else {
|
||||
log::error!("No overlay at index {idx}.");
|
||||
return Ok(EventResult::Consumed);
|
||||
};
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
OverlaySelector::Id(overlay.id),
|
||||
Box::new(|app, owc| owc.activate(app)),
|
||||
)));
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
||||
@@ -28,8 +28,12 @@
|
||||
## How fast to drag when the space drag feature is activated
|
||||
#space_drag_multiplier: 1.0
|
||||
|
||||
## When enabled, the space_rotate binding can be used
|
||||
## to rotate in any axis. Imagine horizon mode².
|
||||
## Setting this to false will make space_drag
|
||||
## only affect the vertical axis (Y).
|
||||
#space_drag_unlocked: true
|
||||
|
||||
## Monado/WiVRn only. When enabled, the space_rotate binding
|
||||
## can rotate in any axis. Imagine horizon mode².
|
||||
#space_rotate_unlocked: false
|
||||
|
||||
## Monado/WiVRn only. Use passthrough camera if the headset supports it.
|
||||
@@ -87,9 +91,17 @@
|
||||
#theme_path: "theme"
|
||||
|
||||
## These can be used to control the color theme of WlxOverlay-S.
|
||||
#color_text: "#ffffff"
|
||||
#color_accent: "#008cff"
|
||||
#color_danger: "#ff3300"
|
||||
#color_faded: "#668299"
|
||||
#color_background: "#010206"
|
||||
|
||||
## Multiplier for animation speed. 2.0 → double speed, 0.5 → half speed
|
||||
#animation_speed: 1.0
|
||||
|
||||
## Adjust this between 0..1 for a more rectangular feel.
|
||||
#round_multiplier: 1.0
|
||||
|
||||
## Path to custom skybox texture, relative to `~/.config/wlxoverlay`
|
||||
#skybox_texture: ""
|
||||
@@ -163,6 +175,10 @@
|
||||
#xr_click_sensitivity: 0.7
|
||||
#xr_click_sensitivity_release: 0.5
|
||||
|
||||
## How many seconds to buttons need to be held
|
||||
## before it's considered a long press
|
||||
#long_press_duration: 1.0
|
||||
|
||||
## Change speed of scrolling
|
||||
# 0.5 → half the speed
|
||||
# 2.0 → twice the speed
|
||||
|
||||
@@ -3,7 +3,7 @@ use idmap::IdMap;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use std::sync::Arc;
|
||||
use wgui::{
|
||||
font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals, parser::parse_color_hex,
|
||||
drawing, font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals, parser::parse_color_hex,
|
||||
renderer_vk::context::SharedContext as WSharedContext,
|
||||
};
|
||||
use wlx_common::{
|
||||
@@ -96,26 +96,21 @@ impl AppState {
|
||||
let theme = session.config.theme_path.clone();
|
||||
|
||||
let mut defaults = wgui::globals::Defaults::default();
|
||||
defaults.accent_color = session
|
||||
.config
|
||||
.color_accent
|
||||
.as_ref()
|
||||
.and_then(|c| parse_color_hex(&c))
|
||||
.unwrap_or(defaults.accent_color);
|
||||
|
||||
defaults.danger_color = session
|
||||
.config
|
||||
.color_danger
|
||||
.as_ref()
|
||||
.and_then(|c| parse_color_hex(&c))
|
||||
.unwrap_or(defaults.danger_color);
|
||||
fn apply_color(default: &mut drawing::Color, value: &Option<String>) {
|
||||
if let Some(parsed) = value.as_ref().and_then(|c| parse_color_hex(c)) {
|
||||
*default = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
defaults.faded_color = session
|
||||
.config
|
||||
.color_faded
|
||||
.as_ref()
|
||||
.and_then(|c| parse_color_hex(&c))
|
||||
.unwrap_or(defaults.faded_color);
|
||||
apply_color(&mut defaults.text_color, &session.config.color_text);
|
||||
apply_color(&mut defaults.accent_color, &session.config.color_accent);
|
||||
apply_color(&mut defaults.danger_color, &session.config.color_danger);
|
||||
apply_color(&mut defaults.faded_color, &session.config.color_faded);
|
||||
apply_color(&mut defaults.bg_color, &session.config.color_background);
|
||||
|
||||
defaults.animation_mult = 1. / session.config.animation_speed;
|
||||
defaults.rounding_mult = session.config.round_multiplier;
|
||||
|
||||
let dbus = DbusConnector::default();
|
||||
|
||||
@@ -205,6 +200,7 @@ impl AppSession {
|
||||
|
||||
let mut toast_topics = IdMap::new();
|
||||
toast_topics.insert(ToastTopic::System, ToastDisplayMethod::Center);
|
||||
toast_topics.insert(ToastTopic::Error, ToastDisplayMethod::Center);
|
||||
toast_topics.insert(ToastTopic::DesktopNotification, ToastDisplayMethod::Center);
|
||||
toast_topics.insert(ToastTopic::XSNotification, ToastDisplayMethod::Center);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ pub struct FrameMeta {
|
||||
pub clear: WGfxClearMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ShouldRender {
|
||||
/// The overlay is dirty and needs to be rendered.
|
||||
Should,
|
||||
|
||||
@@ -34,7 +34,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
pub const MAX_OVERLAY_SETS: usize = 7;
|
||||
pub const MAX_OVERLAY_SETS: usize = 6;
|
||||
|
||||
pub struct OverlayWindowManager<T> {
|
||||
wrappers: EditWrapperManager,
|
||||
@@ -49,6 +49,7 @@ pub struct OverlayWindowManager<T> {
|
||||
watch_id: OverlayID,
|
||||
edit_mode: bool,
|
||||
dropped_overlays: VecDeque<OverlayWindowData<T>>,
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
impl<T> OverlayWindowManager<T>
|
||||
@@ -66,6 +67,7 @@ where
|
||||
watch_id: OverlayID::null(), // set down below
|
||||
edit_mode: false,
|
||||
dropped_overlays: VecDeque::with_capacity(8),
|
||||
initialized: false,
|
||||
};
|
||||
|
||||
let mut wayland = false;
|
||||
@@ -148,6 +150,8 @@ where
|
||||
.notify(app, ev)?;
|
||||
}
|
||||
|
||||
me.initialized = true;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
@@ -199,8 +203,8 @@ where
|
||||
let Some(set) = self.current_set else {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"Can't remove set".into(),
|
||||
"No set is selected!".into(),
|
||||
"TOAST.CANNOT_REMOVE_SET".into(),
|
||||
"TOAST.NO_SET_SELECTED".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
@@ -211,8 +215,8 @@ where
|
||||
if self.sets.len() <= 1 {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"Can't remove set".into(),
|
||||
"This is the last existing set!".into(),
|
||||
"TOAST.CANNOT_REMOVE_SET".into(),
|
||||
"TOAST.LAST_EXISTING_SET".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
@@ -658,6 +662,7 @@ impl<T> OverlayWindowManager<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut num_overlays = 0;
|
||||
let ws = &mut self.sets[new_set];
|
||||
for (id, data) in self.overlays.iter_mut().filter(|(_, d)| !d.config.global) {
|
||||
if let Some(state) = ws.overlays.remove(id) {
|
||||
@@ -666,10 +671,28 @@ impl<T> OverlayWindowManager<T> {
|
||||
if !keep_transforms {
|
||||
data.config.reset(app, false);
|
||||
}
|
||||
if !matches!(
|
||||
data.config.category,
|
||||
OverlayCategory::Internal
|
||||
| OverlayCategory::Keyboard
|
||||
| OverlayCategory::Dashboard
|
||||
) {
|
||||
num_overlays += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
ws.overlays.clear();
|
||||
self.restore_set = new_set;
|
||||
|
||||
if !self.edit_mode && self.initialized && num_overlays < 1 {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"TOAST.EMPTY_SET".into(),
|
||||
"TOAST.LETS_ADD_OVERLAYS".into(),
|
||||
)
|
||||
.with_timeout(3.)
|
||||
.submit(app);
|
||||
}
|
||||
}
|
||||
self.current_set = new_set;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user