From db1675d3992cc39ba995d650eb11cddfb4e2dac8 Mon Sep 17 00:00:00 2001
From: Diamond White <53794934+oneshinyboi@users.noreply.github.com>
Date: Fri, 10 Apr 2026 00:46:22 -0500
Subject: [PATCH 001/307] feat: swipe to type (#451)
* start to integrate swipetype with keyboardstate
* successful debug implementation
* change to git dependency
* add manual trigger to app image build to test
* use latest swipeType lib to test including binaries
* use clipboard to paste swipe output
* use new prediction engine, move swipe type related logic to its own module
* update appimage bulid to support building ort runtime
* implement swipe selection ui in keyboard overlay
* only add motion data for the device that started the swipe
* process swipes on separate thread to not block ui
* add toggle to settings.rs
* default swipe to type config to false
* remove println, minor fix
* use correct key_dimensions
* use latest swipe_type library version
* optionally use wayvr compositor clipboard when pasting
* remove unused imports
* fix typo in log::error
* fix within key positioning
* run swipes on AltGr keycaps
---
.github/workflows/build-appimage.yml | 3 +-
Cargo.lock | 1099 ++++++++++++++++-
dash-frontend/assets/lang/en.json | 2 +
dash-frontend/src/tab/settings/mod.rs | 4 +
.../src/tab/settings/tab_features.rs | 1 +
wayvr/Cargo.toml | 2 +
wayvr/src/assets/gui/keyboard.xml | 17 +-
wayvr/src/backend/wayvr/client.rs | 15 +-
wayvr/src/backend/wayvr/mod.rs | 5 +-
wayvr/src/config.rs | 2 +
wayvr/src/main.rs | 1 +
wayvr/src/overlays/keyboard/builder.rs | 221 +++-
wayvr/src/overlays/keyboard/layout.rs | 3 +-
wayvr/src/overlays/keyboard/mod.rs | 201 ++-
wayvr/src/overlays/keyboard/swipe_type.rs | 238 ++++
wayvr/src/subsystem/hid/mod.rs | 2 +
wgui/src/event.rs | 4 +
wlx-common/src/config.rs | 3 +
18 files changed, 1758 insertions(+), 65 deletions(-)
create mode 100644 wayvr/src/overlays/keyboard/swipe_type.rs
diff --git a/.github/workflows/build-appimage.yml b/.github/workflows/build-appimage.yml
index 9eb37b2c..826fc778 100644
--- a/.github/workflows/build-appimage.yml
+++ b/.github/workflows/build-appimage.yml
@@ -1,6 +1,7 @@
name: Build AppImage
on:
+ workflow_dispatch:
push:
branches:
- 'main'
@@ -15,7 +16,7 @@ env:
jobs:
build_appimage:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./wayvr
diff --git a/Cargo.lock b/Cargo.lock
index 18302e7a..847a98bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -33,6 +33,17 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures 0.2.17",
+]
+
[[package]]
name = "ahash"
version = "0.8.12"
@@ -195,9 +206,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.100"
+version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "appendlist"
@@ -233,6 +244,27 @@ version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
+[[package]]
+name = "arboard"
+version = "3.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
+dependencies = [
+ "clipboard-win",
+ "image",
+ "log",
+ "objc2 0.6.3",
+ "objc2-app-kit 0.3.2",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-foundation 0.3.2",
+ "parking_lot",
+ "percent-encoding",
+ "windows-sys 0.60.2",
+ "wl-clipboard-rs",
+ "x11rb",
+]
+
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
@@ -665,6 +697,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+[[package]]
+name = "base64ct"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
+
[[package]]
name = "bcdec_rs"
version = "0.2.0"
@@ -800,6 +838,37 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
+[[package]]
+name = "bzip2"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c"
+dependencies = [
+ "libbz2-rs-sys",
+]
+
+[[package]]
+name = "cached-path"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e79c94134331f0107c9f5b7caaf15f118ec91de67dcd3fa1145921e9fc27fa"
+dependencies = [
+ "flate2",
+ "fs2",
+ "glob",
+ "infer",
+ "log",
+ "rand 0.10.0",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "sha2",
+ "tar",
+ "tempfile",
+ "thiserror 2.0.17",
+ "zip",
+]
+
[[package]]
name = "calloop"
version = "0.13.0"
@@ -878,6 +947,17 @@ dependencies = [
"nom 7.1.3",
]
+[[package]]
+name = "cfb"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
+dependencies = [
+ "byteorder",
+ "fnv",
+ "uuid",
+]
+
[[package]]
name = "cfg-expr"
version = "0.15.8"
@@ -910,6 +990,17 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "chacha20"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
+ "rand_core 0.10.0",
+]
+
[[package]]
name = "chrono"
version = "0.4.42"
@@ -934,6 +1025,16 @@ dependencies = [
"phf",
]
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
[[package]]
name = "clang-sys"
version = "1.8.1"
@@ -985,6 +1086,15 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
+[[package]]
+name = "clipboard-win"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
+dependencies = [
+ "error-code",
+]
+
[[package]]
name = "cmake"
version = "0.1.57"
@@ -1076,6 +1186,12 @@ dependencies = [
"tiny-keccak",
]
+[[package]]
+name = "constant_time_eq"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
+
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -1231,6 +1347,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "cpufeatures"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -1464,6 +1589,22 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "deflate64"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
+
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+dependencies = [
+ "pem-rfc7468",
+ "zeroize",
+]
+
[[package]]
name = "deranged"
version = "0.5.5"
@@ -1494,6 +1635,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
+ "subtle",
]
[[package]]
@@ -1745,6 +1887,12 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "error-code"
+version = "3.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
+
[[package]]
name = "etagere"
version = "0.2.15"
@@ -1806,6 +1954,26 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+[[package]]
+name = "fax"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
+dependencies = [
+ "fax_derive",
+]
+
+[[package]]
+name = "fax_derive"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.113",
+]
+
[[package]]
name = "fdeflate"
version = "0.3.7"
@@ -1815,6 +1983,17 @@ dependencies = [
"simd-adler32",
]
+[[package]]
+name = "filetime"
+version = "0.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "libredox",
+]
+
[[package]]
name = "find-msvc-tools"
version = "0.1.6"
@@ -1840,6 +2019,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
dependencies = [
"crc32fast",
+ "libz-rs-sys",
"miniz_oxide",
]
@@ -1950,6 +2130,22 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "fs2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "fst"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
+
[[package]]
name = "futures"
version = "0.3.31"
@@ -2102,10 +2298,26 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
- "r-efi",
+ "r-efi 5.3.0",
"wasip2",
]
+[[package]]
+name = "getrandom"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi 6.0.0",
+ "rand_core 0.10.0",
+ "wasip2",
+ "wasip3",
+ "wasm-bindgen",
+]
+
[[package]]
name = "gif"
version = "0.14.1"
@@ -2262,6 +2474,21 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "hmac-sha256"
+version = "1.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec9d92d097f4749b64e8cc33d924d9f40a2d4eb91402b458014b781f5733d60f"
+
[[package]]
name = "hound"
version = "3.5.1"
@@ -2335,6 +2562,45 @@ dependencies = [
"want",
]
+[[package]]
+name = "hyper-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
+dependencies = [
+ "bytes",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "iana-time-zone"
version = "0.1.64"
@@ -2440,6 +2706,12 @@ dependencies = [
"zerovec",
]
+[[package]]
+name = "id-arena"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
+
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -2517,8 +2789,9 @@ dependencies = [
"png 0.18.0",
"ravif",
"rayon",
- "zune-core",
- "zune-jpeg",
+ "tiff",
+ "zune-core 0.5.0",
+ "zune-jpeg 0.5.8",
]
[[package]]
@@ -2575,6 +2848,8 @@ checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
+ "serde",
+ "serde_core",
]
[[package]]
@@ -2583,6 +2858,24 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
+[[package]]
+name = "infer"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7"
+dependencies = [
+ "cfb",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
+dependencies = [
+ "generic-array",
+]
+
[[package]]
name = "input-linux"
version = "0.7.1"
@@ -2629,6 +2922,22 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "ipnet"
+version = "2.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
+
+[[package]]
+name = "iri-string"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -2795,12 +3104,24 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
[[package]]
name = "lebe"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
+[[package]]
+name = "libbz2-rs-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
+
[[package]]
name = "libc"
version = "0.2.179"
@@ -2904,6 +3225,15 @@ dependencies = [
"system-deps",
]
+[[package]]
+name = "libz-rs-sys"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415"
+dependencies = [
+ "zlib-rs",
+]
+
[[package]]
name = "linebender_resource_handle"
version = "0.1.1"
@@ -2980,6 +3310,21 @@ dependencies = [
"hashbrown 0.16.1",
]
+[[package]]
+name = "lzma-rust2"
+version = "0.15.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1670343e58806300d87950e3401e820b519b9384281bbabfb15e3636689ffd69"
+
+[[package]]
+name = "lzma-rust2"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae"
+dependencies = [
+ "sha2",
+]
+
[[package]]
name = "mach2"
version = "0.4.3"
@@ -2995,7 +3340,17 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
- "regex-automata",
+ "regex-automata 0.4.13",
+]
+
+[[package]]
+name = "matrixmultiply"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
+dependencies = [
+ "autocfg",
+ "rawpointer",
]
[[package]]
@@ -3014,6 +3369,16 @@ version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+[[package]]
+name = "memmap"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "memmap2"
version = "0.9.9"
@@ -3130,6 +3495,21 @@ dependencies = [
"tempfile",
]
+[[package]]
+name = "ndarray"
+version = "0.17.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d"
+dependencies = [
+ "matrixmultiply",
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "portable-atomic",
+ "portable-atomic-util",
+ "rawpointer",
+]
+
[[package]]
name = "ndk"
version = "0.9.0"
@@ -3264,6 +3644,15 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "num-conv"
version = "0.2.0"
@@ -3373,6 +3762,18 @@ dependencies = [
"objc2-quartz-core 0.2.2",
]
+[[package]]
+name = "objc2-app-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
+dependencies = [
+ "bitflags 2.10.0",
+ "objc2 0.6.3",
+ "objc2-core-graphics",
+ "objc2-foundation 0.3.2",
+]
+
[[package]]
name = "objc2-audio-toolbox"
version = "0.3.2"
@@ -3457,6 +3858,19 @@ dependencies = [
"objc2 0.6.3",
]
+[[package]]
+name = "objc2-core-graphics"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
+dependencies = [
+ "bitflags 2.10.0",
+ "dispatch2",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+ "objc2-io-surface",
+]
+
[[package]]
name = "objc2-core-image"
version = "0.2.2"
@@ -3521,6 +3935,17 @@ dependencies = [
"objc2-core-foundation",
]
+[[package]]
+name = "objc2-io-surface"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
+dependencies = [
+ "bitflags 2.10.0",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+]
+
[[package]]
name = "objc2-link-presentation"
version = "0.2.2"
@@ -3529,7 +3954,7 @@ checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
dependencies = [
"block2",
"objc2 0.5.2",
- "objc2-app-kit",
+ "objc2-app-kit 0.2.2",
"objc2-foundation 0.2.2",
]
@@ -3754,6 +4179,40 @@ dependencies = [
"pin-project-lite",
]
+[[package]]
+name = "ort"
+version = "2.0.0-rc.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7de3af33d24a745ffb8fab904b13478438d1cd52868e6f17735ef6e1f8bf133"
+dependencies = [
+ "ndarray",
+ "ort-sys",
+ "smallvec",
+ "tracing",
+ "ureq",
+]
+
+[[package]]
+name = "ort-sys"
+version = "2.0.0-rc.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7b497d21a8b6fbb4b5a544f8fadb77e801a09ae0add9e411d31c6f89e3c1e90"
+dependencies = [
+ "hmac-sha256",
+ "lzma-rust2 0.15.7",
+ "ureq",
+]
+
+[[package]]
+name = "os_pipe"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "ouroboros"
version = "0.18.5"
@@ -3861,6 +4320,25 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
+[[package]]
+name = "pbkdf2"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
+dependencies = [
+ "digest",
+ "hmac",
+]
+
+[[package]]
+name = "pem-rfc7468"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
+dependencies = [
+ "base64ct",
+]
+
[[package]]
name = "percent-encoding"
version = "2.3.2"
@@ -4090,6 +4568,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+[[package]]
+name = "ppmd-rust"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24"
+
[[package]]
name = "ppv-lite86"
version = "0.2.21"
@@ -4295,6 +4779,12 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
[[package]]
name = "rand"
version = "0.7.3"
@@ -4330,6 +4820,17 @@ dependencies = [
"rand_core 0.9.3",
]
+[[package]]
+name = "rand"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
+dependencies = [
+ "chacha20",
+ "getrandom 0.4.2",
+ "rand_core 0.10.0",
+]
+
[[package]]
name = "rand_chacha"
version = "0.2.2"
@@ -4387,6 +4888,12 @@ dependencies = [
"getrandom 0.3.4",
]
+[[package]]
+name = "rand_core"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
+
[[package]]
name = "rand_hc"
version = "0.2.0"
@@ -4488,6 +4995,12 @@ dependencies = [
"objc2-quartz-core 0.3.2",
]
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
[[package]]
name = "rayon"
version = "1.11.0"
@@ -4554,14 +5067,24 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.12.2"
+version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
+checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata",
- "regex-syntax",
+ "regex-automata 0.4.13",
+ "regex-syntax 0.8.8",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "fst",
+ "regex-syntax 0.6.29",
]
[[package]]
@@ -4572,15 +5095,59 @@ checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax",
+ "regex-syntax 0.8.8",
]
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
+[[package]]
+name = "reqwest"
+version = "0.12.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-tls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "native-tls",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-native-tls",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
[[package]]
name = "resvg"
version = "0.45.1"
@@ -4763,6 +5330,15 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "rustls-pki-types"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
+dependencies = [
+ "zeroize",
+]
+
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -4963,6 +5539,18 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
@@ -4976,6 +5564,17 @@ dependencies = [
"unsafe-libyaml",
]
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "digest",
+]
+
[[package]]
name = "sha2"
version = "0.10.9"
@@ -4983,7 +5582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
- "cpufeatures",
+ "cpufeatures 0.2.17",
"digest",
]
@@ -5271,6 +5870,17 @@ dependencies = [
"windows-sys 0.60.2",
]
+[[package]]
+name = "socks"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
+dependencies = [
+ "byteorder",
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
@@ -5319,6 +5929,28 @@ dependencies = [
"syn 2.0.113",
]
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "super-swipe-type"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22126517bdf94f41b3d0af625fdf69b5ff131b7dd19a59866d3c00a56072af88"
+dependencies = [
+ "anyhow",
+ "cached-path",
+ "fst",
+ "memmap",
+ "ort",
+ "regex",
+ "regex-automata 0.1.10",
+ "vector2",
+]
+
[[package]]
name = "svg_fmt"
version = "0.4.5"
@@ -5417,6 +6049,15 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
[[package]]
name = "synstructure"
version = "0.13.2"
@@ -5476,6 +6117,17 @@ dependencies = [
"slotmap",
]
+[[package]]
+name = "tar"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
+dependencies = [
+ "filetime",
+ "libc",
+ "xattr",
+]
+
[[package]]
name = "target-lexicon"
version = "0.12.16"
@@ -5553,6 +6205,20 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "tiff"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
+dependencies = [
+ "fax",
+ "flate2",
+ "half",
+ "quick-error",
+ "weezl",
+ "zune-jpeg 0.4.21",
+]
+
[[package]]
name = "time"
version = "0.3.47"
@@ -5561,6 +6227,7 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
+ "js-sys",
"num-conv",
"powerfmt",
"serde_core",
@@ -5670,6 +6337,16 @@ dependencies = [
"syn 2.0.113",
]
+[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
[[package]]
name = "tokio-util"
version = "0.7.18"
@@ -5760,6 +6437,51 @@ dependencies = [
"winnow",
]
+[[package]]
+name = "tower"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
+dependencies = [
+ "bitflags 2.10.0",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "iri-string",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
[[package]]
name = "tracing"
version = "0.1.44"
@@ -5813,7 +6535,7 @@ dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
- "regex-automata",
+ "regex-automata 0.4.13",
"sharded-slab",
"smallvec",
"thread_local",
@@ -5822,6 +6544,17 @@ dependencies = [
"tracing-log",
]
+[[package]]
+name = "tree_magic_mini"
+version = "3.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
+dependencies = [
+ "memchr",
+ "nom 8.0.0",
+ "petgraph",
+]
+
[[package]]
name = "try-lock"
version = "0.2.5"
@@ -5837,6 +6570,12 @@ dependencies = [
"core_maths",
]
+[[package]]
+name = "typed-path"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
+
[[package]]
name = "typeid"
version = "1.0.3"
@@ -5925,12 +6664,48 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
+[[package]]
+name = "ureq"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc"
+dependencies = [
+ "base64",
+ "der",
+ "log",
+ "native-tls",
+ "percent-encoding",
+ "rustls-pki-types",
+ "socks",
+ "ureq-proto",
+ "utf-8",
+ "webpki-root-certs",
+]
+
+[[package]]
+name = "ureq-proto"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
+dependencies = [
+ "base64",
+ "http",
+ "httparse",
+ "log",
+]
+
[[package]]
name = "url"
version = "2.5.7"
@@ -5965,6 +6740,12 @@ dependencies = [
"xmlwriter",
]
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
[[package]]
name = "utf8_iter"
version = "1.0.4"
@@ -6013,6 +6794,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+[[package]]
+name = "vector2"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f562f8d4f41867568374e1a63bd12914795f558319f9bf9b0582c4d96a8de04"
+
[[package]]
name = "version-compare"
version = "0.2.1"
@@ -6126,7 +6913,16 @@ version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
- "wit-bindgen",
+ "wit-bindgen 0.46.0",
+]
+
+[[package]]
+name = "wasip3"
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
+dependencies = [
+ "wit-bindgen 0.51.0",
]
[[package]]
@@ -6187,6 +6983,40 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "wasm-encoder"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
+dependencies = [
+ "leb128fmt",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
+dependencies = [
+ "anyhow",
+ "indexmap 2.12.1",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
+dependencies = [
+ "bitflags 2.10.0",
+ "hashbrown 0.15.5",
+ "indexmap 2.12.1",
+ "semver",
+]
+
[[package]]
name = "wayland-backend"
version = "0.3.12"
@@ -6343,6 +7173,7 @@ name = "wayvr"
version = "26.2.1"
dependencies = [
"anyhow",
+ "arboard",
"ash",
"bytes",
"chrono",
@@ -6382,6 +7213,7 @@ dependencies = [
"smithay",
"smol",
"strum",
+ "super-swipe-type",
"sysinfo",
"thiserror 2.0.17",
"tracing",
@@ -6450,6 +7282,15 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "webpki-root-certs"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "weezl"
version = "0.1.12"
@@ -6952,7 +7793,7 @@ dependencies = [
"memmap2",
"ndk",
"objc2 0.5.2",
- "objc2-app-kit",
+ "objc2-app-kit 0.2.2",
"objc2-foundation 0.2.2",
"objc2-ui-kit",
"orbclient",
@@ -6995,6 +7836,112 @@ version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+dependencies = [
+ "wit-bindgen-rust-macro",
+]
+
+[[package]]
+name = "wit-bindgen-core"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
+dependencies = [
+ "anyhow",
+ "heck 0.5.0",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
+dependencies = [
+ "anyhow",
+ "heck 0.5.0",
+ "indexmap 2.12.1",
+ "prettyplease",
+ "syn 2.0.113",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.113",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
+dependencies = [
+ "anyhow",
+ "bitflags 2.10.0",
+ "indexmap 2.12.1",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap 2.12.1",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
+
+[[package]]
+name = "wl-clipboard-rs"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3"
+dependencies = [
+ "libc",
+ "log",
+ "os_pipe",
+ "rustix 1.1.3",
+ "thiserror 2.0.17",
+ "tree_magic_mini",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-protocols-wlr",
+]
+
[[package]]
name = "wlx-capture"
version = "0.6.0"
@@ -7073,6 +8020,16 @@ version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
+[[package]]
+name = "xattr"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
+dependencies = [
+ "libc",
+ "rustix 1.1.3",
+]
+
[[package]]
name = "xcb"
version = "1.7.0"
@@ -7315,6 +8272,26 @@ dependencies = [
"synstructure",
]
+[[package]]
+name = "zeroize"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.113",
+]
+
[[package]]
name = "zerotrie"
version = "0.2.3"
@@ -7348,12 +8325,91 @@ dependencies = [
"syn 2.0.113",
]
+[[package]]
+name = "zip"
+version = "8.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b680f2a0cd479b4cff6e1233c483fdead418106eae419dc60200ae9850f6d004"
+dependencies = [
+ "aes",
+ "bzip2",
+ "constant_time_eq",
+ "crc32fast",
+ "deflate64",
+ "flate2",
+ "getrandom 0.4.2",
+ "hmac",
+ "indexmap 2.12.1",
+ "lzma-rust2 0.16.2",
+ "memchr",
+ "pbkdf2",
+ "ppmd-rust",
+ "sha1",
+ "time",
+ "typed-path",
+ "zeroize",
+ "zopfli",
+ "zstd",
+]
+
+[[package]]
+name = "zlib-rs"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3"
+
[[package]]
name = "zmij"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e0d8dffbae3d840f64bda38e28391faef673a7b5a6017840f2a106c8145868"
+[[package]]
+name = "zopfli"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
+dependencies = [
+ "bumpalo",
+ "crc32fast",
+ "log",
+ "simd-adler32",
+]
+
+[[package]]
+name = "zstd"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.16+zstd.1.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "zune-core"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
+
[[package]]
name = "zune-core"
version = "0.5.0"
@@ -7369,13 +8425,22 @@ dependencies = [
"simd-adler32",
]
+[[package]]
+name = "zune-jpeg"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
+dependencies = [
+ "zune-core 0.4.12",
+]
+
[[package]]
name = "zune-jpeg"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5"
dependencies = [
- "zune-core",
+ "zune-core 0.5.0",
]
[[package]]
diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json
index beb5eff9..96b56062 100644
--- a/dash-frontend/assets/lang/en.json
+++ b/dash-frontend/assets/lang/en.json
@@ -63,6 +63,8 @@
"KEYBOARD_MIDDLE_CLICK": "Keyboard middle click",
"KEYBOARD_MIDDLE_CLICK_HELP": "Modifier to use when typing\nwith purple laser",
"KEYBOARD_SOUND_ENABLED": "Keyboard sounds",
+ "KEYBOARD_SWIPE_TO_TYPE_ENABLED": "Keyboard swipe to type",
+ "KEYBOARD_SWIPE_TO_TYPE_ENABLED_HELP": "Only works for English! Results will be drastically worse on non qwerty keyboards.",
"LANGUAGE": "Language",
"LEFT_HANDED_MOUSE": "Left-handed mouse",
"LEFT_HANDED_MOUSE_HELP": "Use this if mouse buttons are swapped",
diff --git a/dash-frontend/src/tab/settings/mod.rs b/dash-frontend/src/tab/settings/mod.rs
index bcd79abf..55eb02a9 100644
--- a/dash-frontend/src/tab/settings/mod.rs
+++ b/dash-frontend/src/tab/settings/mod.rs
@@ -231,6 +231,7 @@ enum SettingType {
InvertScrollDirectionY,
KeyboardMiddleClick,
KeyboardSoundEnabled,
+ KeyboardSwipeToTypeEnabled,
Language,
LeftHandedMouse,
LongPressDuration,
@@ -283,6 +284,7 @@ impl SettingType {
Self::HideUsername => &mut config.hide_username,
Self::OpaqueBackground => &mut config.opaque_background,
Self::XwaylandByDefault => &mut config.xwayland_by_default,
+ Self::KeyboardSwipeToTypeEnabled => &mut config.keyboard_swipe_to_type_enabled,
_ => panic!("Requested bool for non-bool SettingType"),
}
}
@@ -381,6 +383,7 @@ impl SettingType {
Self::InvertScrollDirectionY => Ok("APP_SETTINGS.INVERT_SCROLL_DIRECTION_Y"),
Self::KeyboardMiddleClick => Ok("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK"),
Self::KeyboardSoundEnabled => Ok("APP_SETTINGS.KEYBOARD_SOUND_ENABLED"),
+ Self::KeyboardSwipeToTypeEnabled => Ok("APP_SETTINGS.KEYBOARD_SWIPE_TO_TYPE_ENABLED"),
Self::Language => Ok("APP_SETTINGS.LANGUAGE"),
Self::LeftHandedMouse => Ok("APP_SETTINGS.LEFT_HANDED_MOUSE"),
Self::LongPressDuration => Ok("APP_SETTINGS.LONG_PRESS_DURATION"),
@@ -417,6 +420,7 @@ impl SettingType {
Self::GridOpacity => Some("APP_SETTINGS.GRID_OPACITY_HELP"),
Self::HandsfreePointer => Some("APP_SETTINGS.HANDSFREE_POINTER_HELP"),
Self::KeyboardMiddleClick => Some("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK_HELP"),
+ Self::KeyboardSwipeToTypeEnabled => Some("APP_SETTINGS.KEYBOARD_SWIPE_TO_TYPE_ENABLED_HELP"),
Self::LeftHandedMouse => Some("APP_SETTINGS.LEFT_HANDED_MOUSE_HELP"),
Self::ScreenRenderDown => Some("APP_SETTINGS.SCREEN_RENDER_DOWN_HELP"),
Self::UprightScreenFix => Some("APP_SETTINGS.UPRIGHT_SCREEN_FIX_HELP"),
diff --git a/dash-frontend/src/tab/settings/tab_features.rs b/dash-frontend/src/tab/settings/tab_features.rs
index eb0e9865..264a40fd 100644
--- a/dash-frontend/src/tab/settings/tab_features.rs
+++ b/dash-frontend/src/tab/settings/tab_features.rs
@@ -9,6 +9,7 @@ pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
options_checkbox(mp, c, SettingType::NotificationsEnabled)?;
options_checkbox(mp, c, SettingType::NotificationsSoundEnabled)?;
options_checkbox(mp, c, SettingType::KeyboardSoundEnabled)?;
+ options_checkbox(mp, c, SettingType::KeyboardSwipeToTypeEnabled)?;
options_checkbox(mp, c, SettingType::SpaceDragUnlocked)?;
options_checkbox(mp, c, SettingType::SpaceRotateUnlocked)?;
options_slider_f32(mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5)?;
diff --git a/wayvr/Cargo.toml b/wayvr/Cargo.toml
index 843ee83f..eb7e052a 100644
--- a/wayvr/Cargo.toml
+++ b/wayvr/Cargo.toml
@@ -90,6 +90,8 @@ xcb = { version = "1.6.0", features = [
"as-raw-xcb-connection",
], optional = true }
xkbcommon = { version = "0.8.0" } # 0.9.0 breaks keymap import on some distros
+arboard = { version="3.6.1", features = ["wayland-data-control", "wl-clipboard-rs"] }
+super-swipe-type = "0.2.2"
[build-dependencies]
regex.workspace = true
diff --git a/wayvr/src/assets/gui/keyboard.xml b/wayvr/src/assets/gui/keyboard.xml
index 4d6fc321..405823d8 100644
--- a/wayvr/src/assets/gui/keyboard.xml
+++ b/wayvr/src/assets/gui/keyboard.xml
@@ -13,6 +13,11 @@
width="${width}" height="${height}" min_width="${width}" min_height="${height}" max_width="${width}" max_height="${height}"
/>
+
+
@@ -90,6 +95,13 @@
+
+
+
+
+
+
+
@@ -231,7 +243,10 @@
-
+
+
+
+
diff --git a/wayvr/src/backend/wayvr/client.rs b/wayvr/src/backend/wayvr/client.rs
index 862fcc5d..662eee26 100644
--- a/wayvr/src/backend/wayvr/client.rs
+++ b/wayvr/src/backend/wayvr/client.rs
@@ -7,10 +7,12 @@ use smithay::{
reexports::wayland_server,
utils::SerialCounter,
};
+use smithay::input::Seat;
+use smithay::wayland::selection::data_device::set_data_device_selection;
use xkbcommon::xkb;
use crate::backend::wayvr::{ExternalProcessRequest, WayVRTask};
-
+use crate::backend::wayvr::comp::Application;
use super::{
ProcessWayVREnv,
comp::{self, ClientState},
@@ -26,6 +28,7 @@ pub struct WayVRCompositor {
pub state: comp::Application,
pub seat_keyboard: KeyboardHandle,
pub seat_pointer: PointerHandle,
+ pub seat: Seat,
pub serial_counter: SerialCounter,
pub wayland_env: super::WaylandEnv,
@@ -68,6 +71,7 @@ impl WayVRCompositor {
display: wayland_server::Display,
seat_keyboard: KeyboardHandle,
seat_pointer: PointerHandle,
+ seat: Seat,
) -> anyhow::Result {
let (wayland_env, listener) = create_wayland_listener()?;
@@ -81,6 +85,7 @@ impl WayVRCompositor {
serial_counter: SerialCounter::new(),
clients: Vec::new(),
toplevel_surf_count: 0,
+ seat,
})
}
@@ -200,6 +205,14 @@ impl WayVRCompositor {
);
}
+ pub fn set_clipboard_text(&mut self, text: String) {
+ set_data_device_selection::(
+ &self.state.display_handle,
+ &self.seat,
+ vec!["text/plain;charset=utf-8".to_string(), "text/plain".to_string()],
+ text.as_bytes().into(),
+ );
+ }
pub fn set_keymap(&mut self, keymap: &xkb::Keymap) -> anyhow::Result<()> {
// Smithay only accepts keymaps in a string form due to thread safety concerns
self.seat_keyboard
diff --git a/wayvr/src/backend/wayvr/mod.rs b/wayvr/src/backend/wayvr/mod.rs
index 5d83f02b..119248bf 100644
--- a/wayvr/src/backend/wayvr/mod.rs
+++ b/wayvr/src/backend/wayvr/mod.rs
@@ -241,7 +241,7 @@ impl WvrServerState {
};
Ok(Self {
- manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?,
+ manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer, seat)?,
processes: ProcessVec::new(),
wm: window::WindowManager::new(),
ticks: 0,
@@ -608,6 +608,9 @@ impl WvrServerState {
pub fn send_key(&mut self, virtual_key: u32, down: bool) {
self.manager.send_key(virtual_key, down);
}
+ pub fn set_clipboard_text(&mut self, text: String) {
+ self.manager.set_clipboard_text(text);
+ }
pub fn set_keymap(&mut self, keymap: &xkb::Keymap) -> anyhow::Result<()> {
self.manager.set_keymap(keymap)
diff --git a/wayvr/src/config.rs b/wayvr/src/config.rs
index 9eb28d2b..ab19a337 100644
--- a/wayvr/src/config.rs
+++ b/wayvr/src/config.rs
@@ -140,6 +140,7 @@ pub struct AutoSettings {
pub context_menu_hold_and_release: bool,
pub capture_method: CaptureMethod,
pub keyboard_middle_click_mode: AltModifier,
+ pub keyboard_swipe_to_type_enabled: bool,
pub autostart_apps: Vec,
pub handsfree_pointer: HandsfreePointer,
pub language: Option,
@@ -191,6 +192,7 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
context_menu_hold_and_release: config.context_menu_hold_and_release,
capture_method: config.capture_method,
keyboard_middle_click_mode: config.keyboard_middle_click_mode,
+ keyboard_swipe_to_type_enabled: config.keyboard_swipe_to_type_enabled,
autostart_apps: config.autostart_apps.clone(),
handsfree_pointer: config.handsfree_pointer,
language: config.language,
diff --git a/wayvr/src/main.rs b/wayvr/src/main.rs
index f39bf450..8c692ca9 100644
--- a/wayvr/src/main.rs
+++ b/wayvr/src/main.rs
@@ -281,6 +281,7 @@ fn logging_init(args: &mut Args) {
.from_env_lossy()
.add_directive("symphonia_core::probe=warn".parse().unwrap())
.add_directive("symphonia_bundle_mp3=warn".parse().unwrap())
+ .add_directive("ort=warn".parse().unwrap())
.add_directive("zbus=warn".parse().unwrap())
.add_directive("usvg=error".parse().unwrap())
.add_directive("resvg=error".parse().unwrap())
diff --git a/wayvr/src/overlays/keyboard/builder.rs b/wayvr/src/overlays/keyboard/builder.rs
index b96b0368..c1b957f5 100644
--- a/wayvr/src/overlays/keyboard/builder.rs
+++ b/wayvr/src/overlays/keyboard/builder.rs
@@ -1,5 +1,4 @@
use std::{collections::HashMap, rc::Rc, time::Duration};
-
use crate::{
app_misc,
gui::{
@@ -11,8 +10,9 @@ use crate::{
subsystem::hid::XkbKeymap,
windowing::backend::OverlayEventData,
};
-use anyhow::Context;
+use anyhow::{bail, Context};
use glam::{FloatExt, Mat4, Vec2, vec2, vec3};
+use slotmap::Key;
use wgui::{
animation::{Animation, AnimationEasing},
assets::AssetPath,
@@ -25,11 +25,10 @@ use wgui::{
taffy::{self, prelude::length},
widget::{EventResult, div::WidgetDiv, rectangle::WidgetRectangle},
};
-
-use super::{
- KeyButtonData, KeyState, KeyboardState, handle_press, handle_release,
- layout::{self, KeyCapType},
-};
+use wgui::event::StyleSetRequest;
+use wgui::layout::LayoutTask;
+use wgui::taffy::Display;
+use super::{KeyButtonData, KeyState, KeyboardState, handle_press, handle_release, layout::{self, KeyCapType}, handle_mouse_motion, init_swipe_type_manager};
const PIXELS_PER_UNIT: f32 = 60.;
@@ -41,6 +40,147 @@ fn new_doc_params(panel: &mut GuiPanel) -> ParseDocumentParams<'s
}
}
+pub(super) fn update_swipe_prediction_bar(
+ panel: &mut GuiPanel,
+ app: &mut AppState
+) -> anyhow::Result {
+ let mut elements_changed = false;
+
+ let (accent_color, anim_mult) = {
+ let theme = &app.wgui_theme;
+ (theme.accent_color, theme.animation_mult)
+ };
+
+ if let Some(recv) = panel.state.swipe_candidate_receiver.as_mut()
+ && let Ok(candidates) = recv.try_recv() {
+
+ let predictions_root = panel.parser_state
+ .get_widget_id("swipe_predictions_root")
+ .unwrap_or_default();
+
+ if predictions_root.is_null() {
+ return Ok(elements_changed)
+ }
+ let doc_params = new_doc_params(panel);
+
+ panel.layout.remove_children(predictions_root);
+
+ let Some(new_suggestions) = candidates else {
+ return Ok(elements_changed)
+ };
+
+ let mut iter = new_suggestions.iter();
+ let Some(best_prediction) = iter.next() else {
+ bail!("not enough swipe predictions");
+ };
+ if let Some(manager) = panel.state.swipe_typing_manager.as_mut() {
+ manager.select_word(best_prediction, app, panel.state.modifiers);
+ }
+ for (i, prediction) in iter.enumerate() {
+ let mut params = HashMap::new();
+ let id: Rc = Rc::from(format!("Prediction-{i}"));
+ params.insert("id".into(), id.clone());
+ params.insert("text".into(), prediction.clone().into());
+
+ panel.parser_state.instantiate_template(
+ &doc_params,
+ "KeyPrediction",
+ &mut panel.layout,
+ predictions_root,
+ params
+ )?;
+
+ if let Ok(widget_id) = panel.parser_state.get_widget_id(&id) {
+ let key_state = {
+ let rect = panel
+ .layout
+ .state
+ .widgets
+ .get_as::(widget_id)
+ .unwrap(); // want panic
+
+ Rc::new(KeyState {
+ // fake button state just so we get key state for anims
+ button_state: KeyButtonData::Modifier {
+ modifier: 0,
+ sticky: core::cell::Cell::new(false),
+ },
+ color: rect.params.color,
+ color2: rect.params.color2,
+ base_border_color: rect.params.border_color,
+ cur_border_color: rect.params.border_color.into(),
+ border: rect.params.border,
+ drawn_state: false.into(),
+ })
+ };
+ panel.add_event_listener(
+ widget_id,
+ EventListenerKind::MousePress,
+ Box::new({
+ let k = key_state.clone();
+ let pred = prediction.clone();
+ move |common, data, app, state| {
+ if let Some(manager) = state.swipe_typing_manager.as_mut() {
+ manager.select_alternate_prediction(&pred, app, state.modifiers);
+ on_press_anim(k.clone(), common, data)
+ }
+ Ok(EventResult::Pass)
+ }
+ })
+ );
+ panel.add_event_listener(
+ widget_id,
+ EventListenerKind::MouseEnter,
+ Box::new({
+ let k = key_state.clone();
+ move |common, data, _app, _state| {
+ on_enter_anim(
+ k.clone(),
+ common,
+ data,
+ accent_color,
+ anim_mult,
+ 0.0,
+ );
+ Ok(EventResult::Pass)
+ }
+ })
+ );
+ panel.add_event_listener(
+ widget_id,
+ EventListenerKind::MouseLeave,
+ Box::new({
+ let k = key_state.clone();
+ move |common, data, _app, _state | {
+ on_leave_anim(
+ k.clone(),
+ common,
+ data,
+ accent_color,
+ anim_mult,
+ 0.0,
+ );
+ Ok(EventResult::Pass)
+ }
+ })
+ );
+ panel.add_event_listener(
+ widget_id,
+ EventListenerKind::MouseRelease,
+ Box::new({
+ let k = key_state.clone();
+ move |common, data, _app, _state| {
+ on_release_anim(k.clone(), common, data);
+ Ok(EventResult::Pass)
+ }
+ })
+ );
+ }
+ }
+ elements_changed = true;
+ }
+ Ok(elements_changed)
+}
#[allow(clippy::too_many_lines, clippy::significant_drop_tightening)]
pub(super) fn create_keyboard_panel(
app: &mut AppState,
@@ -113,7 +253,7 @@ pub(super) fn create_keyboard_panel(
params.insert(Rc::from("width"), Rc::from(key_width.to_string()));
params.insert(Rc::from("height"), Rc::from(key_height.to_string()));
- let mut label = key.label.into_iter();
+ let mut label = key.label.clone().into_iter();
label
.next()
.and_then(|s| params.insert("text".into(), s.into()));
@@ -169,6 +309,9 @@ pub(super) fn create_keyboard_panel(
})
};
+ let key_cap_type: Rc = Rc::from(key.cap_type);
+ let key_label: Rc> = Rc::from(key.label);
+
let width_mul = 1. / my_size_f32;
panel.add_event_listener(
@@ -186,6 +329,8 @@ pub(super) fn create_keyboard_panel(
anim_mult,
width_mul,
);
+
+
Ok(EventResult::Pass)
}
}),
@@ -205,6 +350,7 @@ pub(super) fn create_keyboard_panel(
anim_mult,
width_mul,
);
+
Ok(EventResult::Pass)
}
}),
@@ -214,26 +360,49 @@ pub(super) fn create_keyboard_panel(
EventListenerKind::MousePress,
Box::new({
let k = key_state.clone();
+ let k_label = key_label.clone();
+ let k_cap_type = key_cap_type.clone();
move |common, data, app, state| {
let CallbackMetadata::MouseButton(button) = data.metadata else {
panic!("CallbackMetadata should contain MouseButton!");
};
+ let within_key_pos = data.metadata.get_mouse_pos_normalized(&common.alterables.transform_stack);
- handle_press(app, &k, state, button);
+ handle_press(app, &k, &k_label, &k_cap_type, &within_key_pos, state, button, button.device);
on_press_anim(k.clone(), common, data);
Ok(EventResult::Pass)
}
}),
);
+ panel.add_event_listener(
+ widget_id,
+ EventListenerKind::MouseMotion,
+ Box::new({
+ let k = key_state.clone();
+ let k_label = key_label.clone();
+ let k_cap_type = key_cap_type.clone();
+ move |common, data, _app, state| {
+ let within_key_pos = data.metadata.get_mouse_pos_normalized(&common.alterables.transform_stack);
+ let CallbackMetadata::MousePosition(position) = data.metadata else {
+ panic!("CallbackMetadata should contain MousePosition!");
+ };
+
+ handle_mouse_motion(&k, &k_label, &k_cap_type, state, &within_key_pos, position.device);
+ Ok(EventResult::Pass)
+ }
+ }),
+ );
panel.add_event_listener(
widget_id,
EventListenerKind::MouseRelease,
Box::new({
let k = key_state.clone();
+ let k_cap_type = key_cap_type.clone();
move |common, data, app, state| {
- if handle_release(app, &k, state) {
+ if handle_release(app, &k, &k_cap_type, state) {
on_release_anim(k.clone(), common, data);
}
+
Ok(EventResult::Pass)
}
}),
@@ -307,6 +476,38 @@ pub(super) fn create_keyboard_panel(
elems_changed = true;
}
}
+ if !app.session.config.keyboard_swipe_to_type_enabled {
+ panel.state.swipe_typing_manager = None;
+ panel.state.swipe_candidate_receiver = None;
+
+ let predictions_root = panel.parser_state
+ .get_widget_id("swipe_predictions_root")
+ .unwrap_or_default();
+
+ if !predictions_root.is_null() {
+ panel.layout.remove_children(predictions_root);
+
+ panel.layout.tasks.push(LayoutTask::SetWidgetStyle(
+ predictions_root,
+ StyleSetRequest::Display(Display::None),
+ ));
+
+ }
+ }
+ if app.session.config.keyboard_swipe_to_type_enabled && panel.state.swipe_typing_manager.is_none() {
+ init_swipe_type_manager(&mut panel.state);
+
+ let predictions_root = panel.parser_state
+ .get_widget_id("swipe_predictions_root")
+ .unwrap_or_default();
+
+ if !predictions_root.is_null() {
+ panel.layout.tasks.push(LayoutTask::SetWidgetStyle(
+ predictions_root,
+ StyleSetRequest::Display(Display::Flex),
+ ));
+ }
+ }
}
OverlayEventData::CustomCommand { element, command } => {
diff --git a/wayvr/src/overlays/keyboard/layout.rs b/wayvr/src/overlays/keyboard/layout.rs
index 2af0590f..84c489a8 100644
--- a/wayvr/src/overlays/keyboard/layout.rs
+++ b/wayvr/src/overlays/keyboard/layout.rs
@@ -2,7 +2,6 @@ use std::{collections::HashMap, str::FromStr, sync::LazyLock};
use regex::Regex;
use serde::{Deserialize, Serialize};
-
use crate::{
config::{ConfigType, load_known_yaml},
subsystem::hid::{
@@ -230,7 +229,7 @@ pub(super) struct KeyData {
pub(super) cap_type: KeyCapType,
}
-#[derive(Debug)]
+#[derive(Debug, Eq, PartialEq)]
pub enum KeyCapType {
/// Label an SVG
Special,
diff --git a/wayvr/src/overlays/keyboard/mod.rs b/wayvr/src/overlays/keyboard/mod.rs
index 966f5baa..ded5cd33 100644
--- a/wayvr/src/overlays/keyboard/mod.rs
+++ b/wayvr/src/overlays/keyboard/mod.rs
@@ -4,7 +4,7 @@ use std::{
process::{Child, Command},
sync::atomic::Ordering,
};
-
+use std::sync::mpsc::Receiver;
use crate::{
KEYMAP_CHANGE,
backend::{
@@ -27,21 +27,29 @@ use crate::{
},
};
use anyhow::Context;
-use glam::{Affine3A, Quat, Vec3, vec3};
+use glam::{Affine3A, Quat, Vec3, vec3, Vec2};
use regex::Regex;
-use slotmap::{SlotMap, new_key_type};
+use slotmap::{SlotMap, new_key_type, Key};
use wgui::{
drawing,
event::{InternalStateChangeEvent, MouseButtonEvent, MouseButtonIndex},
};
+use wgui::event::StyleSetRequest;
+use wgui::layout::{LayoutTask};
+use wgui::parser::Fetchable;
+use wgui::taffy::Display;
use wlx_common::windowing::{OverlayWindowState, Positioning};
use wlx_common::{
config::AltModifier,
overlays::{BackendAttrib, BackendAttribValue},
};
+use crate::overlays::keyboard::builder::update_swipe_prediction_bar;
+use crate::overlays::keyboard::layout::KeyCapType;
+use crate::overlays::keyboard::swipe_type::SwipeTypingManager;
pub mod builder;
mod layout;
+mod swipe_type;
pub const KEYBOARD_NAME: &str = "kbd";
const AUTO_RELEASE_MODS: [KeyModifier; 5] = [SHIFT, CTRL, ALT, SUPER, META];
@@ -56,6 +64,8 @@ pub fn create_keyboard(app: &mut AppState, wayland: bool) -> anyhow::Result anyhow::Result {
+ state.swipe_typing_manager = Some(engine);
+ state.swipe_candidate_receiver = Some(receiver);
+ },
+ Err(e) => {
+ log::error!("Error occurred while trying to load swipe engine: {}", e);
+ }
+ };
+}
+pub(self) fn hide_swipe_type_manager(panel: &mut GuiPanel) {
+ let predictions_root = panel.parser_state
+ .get_widget_id("swipe_predictions_root")
+ .unwrap_or_default();
+ if !predictions_root.is_null() {
+ panel.layout.tasks.push(LayoutTask::SetWidgetStyle(
+ predictions_root,
+ StyleSetRequest::Display(Display::None),
+ ));
+ }
+}
const fn alt_modifier_to_key(m: AltModifier) -> KeyModifier {
match m {
AltModifier::Shift => SHIFT,
@@ -150,8 +181,19 @@ impl KeyboardBackend {
keymap: Option<&XkbKeymap>,
app: &mut AppState,
) -> anyhow::Result {
- let panel =
- create_keyboard_panel(app, keymap, self.default_state.take(), &self.wlx_layout)?;
+ let mut state = self.default_state.take();
+
+ if app.session.config.keyboard_swipe_to_type_enabled {
+ init_swipe_type_manager(&mut state);
+ }
+
+ log::info!("swipe engine created");
+ let mut panel =
+ create_keyboard_panel(app, keymap, state, &self.wlx_layout)?;
+
+ if !app.session.config.keyboard_swipe_to_type_enabled {
+ hide_swipe_type_manager(&mut panel);
+ }
let id = self.layout_panels.insert(panel);
if let Some(layout_name) = keymap.and_then(|k| k.get_name()) {
@@ -162,6 +204,7 @@ impl KeyboardBackend {
Ok(id)
}
+
fn switch_keymap(&mut self, keymap: &XkbKeymap, app: &mut AppState) -> anyhow::Result {
if !self.wlx_layout.auto_labels.unwrap_or(true) {
return Ok(false);
@@ -176,30 +219,41 @@ impl KeyboardBackend {
if self.active_layout.eq(new_key) {
return Ok(false);
}
- self.internal_switch_keymap(*new_key);
+ self.internal_switch_keymap(*new_key, app);
} else {
let new_key = self.add_new_keymap(Some(keymap), app)?;
- self.internal_switch_keymap(new_key);
+ self.internal_switch_keymap(new_key, app);
}
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::KeyboardChanged));
Ok(true)
}
- fn internal_switch_keymap(&mut self, new_key: KeyboardPanelKey) {
- let state_from = self
+ fn internal_switch_keymap(&mut self, new_key: KeyboardPanelKey, app: &AppState) {
+ let mut state_from = self
.layout_panels
.get_mut(self.active_layout)
.unwrap()
.state
.take();
+ if app.session.config.keyboard_swipe_to_type_enabled {
+ init_swipe_type_manager(&mut state_from);
+ }
+
self.active_layout = new_key;
self.layout_panels
.get_mut(self.active_layout)
.unwrap()
.state = state_from;
+
+ if !app.session.config.keyboard_swipe_to_type_enabled {
+ hide_swipe_type_manager(self.layout_panels
+ .get_mut(self.active_layout)
+ .unwrap()
+ )
+ }
}
fn get_effective_keymap(&mut self) -> anyhow::Result {
@@ -233,6 +287,13 @@ impl KeyboardBackend {
}
}
+ fn update_swipe_prediction_bar(&mut self, app: &mut AppState) -> anyhow::Result<()> {
+ if update_swipe_prediction_bar(self.panel(), app)? {
+ self.panel().process_custom_elems(app);
+ }
+ Ok(())
+ }
+
fn auto_switch_keymap(&mut self, app: &mut AppState) -> anyhow::Result {
let keymap = self.get_effective_keymap()?;
app.hid_provider
@@ -266,6 +327,7 @@ impl OverlayBackend for KeyboardBackend {
});
}
}
+ self.update_swipe_prediction_bar(app)?;
self.panel().should_render(app)
}
fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> {
@@ -327,6 +389,8 @@ struct KeyboardState {
overlay_list: OverlayList,
set_list: SetList,
clock_12h: bool,
+ swipe_typing_manager: Option,
+ swipe_candidate_receiver: Option>>>
}
macro_rules! take_and_leave_default {
@@ -346,6 +410,8 @@ impl KeyboardState {
overlay_list: OverlayList::default(),
set_list: SetList::default(),
clock_12h: self.clock_12h,
+ swipe_typing_manager: None,
+ swipe_candidate_receiver: None,
}
}
}
@@ -386,26 +452,70 @@ enum KeyButtonData {
},
}
+fn handle_mouse_motion(
+ key: &KeyState,
+ key_label: &Vec,
+ key_cap_type: &KeyCapType,
+ keyboard: &mut KeyboardState,
+ within_key_pos: &Option,
+ device: usize
+) {
+ if let Some(swipe_manager) = keyboard.swipe_typing_manager.as_mut()
+ && matches!(*key_cap_type, KeyCapType::Letter | KeyCapType::LetterAltGr)
+ {
+ if !swipe_manager.is_current_swipe_empty() {
+ match &key.button_state {
+ KeyButtonData::Key { vk, pressed } => {
+ if let Some(pos) = within_key_pos {
+ // check because mouse motion can trigger despite hover being false
+ if pos.x >= 0.0 && pos.x <= 1.0 && pos.y >= 0.0 && pos.y <= 1.0 {
+
+ if let Some(label) = key_label.first() {
+ swipe_manager.add_swipe(pos, label.chars().next().unwrap_or_default(), device);
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+
+}
fn handle_press(
app: &mut AppState,
key: &KeyState,
+ key_label: &Vec,
+ key_cap_type: &KeyCapType,
+ within_key_pos: &Option,
keyboard: &mut KeyboardState,
button: MouseButtonEvent,
+ device: usize
) {
match &key.button_state {
KeyButtonData::Key { vk, pressed } => {
- keyboard.modifiers |= match button.index {
- MouseButtonIndex::Right => SHIFT,
- MouseButtonIndex::Middle => keyboard.alt_modifier,
- _ => 0,
- };
-
- app.hid_provider
- .set_modifiers_routed(app.wvr_server.as_mut(), keyboard.modifiers);
- app.hid_provider
- .send_key_routed(app.wvr_server.as_mut(), *vk, true);
- pressed.set(true);
- play_key_click(app);
+ if let Some(swipe_manager) = keyboard.swipe_typing_manager.as_mut()
+ && matches!(*key_cap_type, KeyCapType::Letter | KeyCapType::LetterAltGr)
+ {
+ if let Some(pos) = within_key_pos {
+ if let Some(label) = key_label.first() {
+ swipe_manager.add_swipe(pos, label.chars().next().unwrap_or_default(), device);
+ }
+ }
+ }
+ else {
+ keyboard.modifiers |= match button.index {
+ MouseButtonIndex::Right => SHIFT,
+ MouseButtonIndex::Middle => keyboard.alt_modifier,
+ _ => 0,
+ };
+ app.hid_provider
+ .set_modifiers_routed(app.wvr_server.as_mut(), keyboard.modifiers);
+ app.hid_provider
+ .send_key_routed(app.wvr_server.as_mut(), *vk, true);
+ pressed.set(true);
+ play_key_click(app);
+ }
}
KeyButtonData::Modifier { modifier, sticky } => {
sticky.set(keyboard.modifiers & *modifier == 0);
@@ -435,20 +545,47 @@ fn handle_press(
}
}
-fn handle_release(app: &mut AppState, key: &KeyState, keyboard: &mut KeyboardState) -> bool {
+fn handle_release(app: &mut AppState, key: &KeyState, k_cap_type: &KeyCapType, keyboard: &mut KeyboardState) -> bool {
match &key.button_state {
KeyButtonData::Key { vk, pressed } => {
- pressed.set(false);
+ if let Some(swipe_manager) = keyboard.swipe_typing_manager.as_mut()
+ && matches!(*k_cap_type, KeyCapType::Letter | KeyCapType::LetterAltGr)
+ {
+ if swipe_manager.did_swipe_leave_first_key() {
+ match swipe_manager.predict() {
+ Ok(()) => {},
+ Err(e) => {
+ log::error!("{}", e)
+ }
+ }
+ }
+ else { // pointer must have been released on the same key it was pressed on
+ swipe_manager.reset(); // drop swipe tracking that was started on press
- for m in &AUTO_RELEASE_MODS {
- if keyboard.modifiers & *m != 0 {
- keyboard.modifiers &= !*m;
+ app.hid_provider
+ .send_key_routed(app.wvr_server.as_mut(), *vk, true);
+ pressed.set(true);
+ app.hid_provider
+ .send_key_routed(app.wvr_server.as_mut(), *vk, false);
+ play_key_click(app);
}
}
- app.hid_provider
- .send_key_routed(app.wvr_server.as_mut(), *vk, false);
- app.hid_provider
- .set_modifiers_routed(app.wvr_server.as_mut(), keyboard.modifiers);
+ else {
+ if let Some(swipe_manager) = keyboard.swipe_typing_manager.as_mut() {
+ swipe_manager.reset();
+ }
+ pressed.set(false);
+
+ for m in &AUTO_RELEASE_MODS {
+ if keyboard.modifiers & *m != 0 {
+ keyboard.modifiers &= !*m;
+ }
+ }
+ app.hid_provider
+ .send_key_routed(app.wvr_server.as_mut(), *vk, false);
+ app.hid_provider
+ .set_modifiers_routed(app.wvr_server.as_mut(), keyboard.modifiers);
+ }
true
}
KeyButtonData::Modifier { modifier, sticky } => {
diff --git a/wayvr/src/overlays/keyboard/swipe_type.rs b/wayvr/src/overlays/keyboard/swipe_type.rs
new file mode 100644
index 00000000..428303dc
--- /dev/null
+++ b/wayvr/src/overlays/keyboard/swipe_type.rs
@@ -0,0 +1,238 @@
+use crate::state::AppState;
+use crate::subsystem::hid::{KeyModifier, VirtualKey, CTRL};
+use anyhow::{bail};
+use arboard::Clipboard;
+use glam::Vec2;
+use std::mem;
+use std::sync::mpsc::{sync_channel, Receiver, SyncSender, channel, Sender};
+use std::thread::{self, JoinHandle};
+use std::time::Instant;
+use super_swipe_type::keyboard_manager::QwertyKeyboardGrid;
+use super_swipe_type::swipe_orchestrator::SwipeOrchestrator;
+use super_swipe_type::{SwipePoint};
+use crate::subsystem::input::KeyboardFocus;
+
+const PREDICTION_SUGGESTION_COUNT: usize = 5;
+
+enum PredictionTask {
+ Predict {
+ swipe: Vec,
+ last_word: Option,
+ },
+ Shutdown,
+}
+
+pub struct SwipeTypingManager {
+ keyboard_gird: QwertyKeyboardGrid,
+ current_swipe: Vec,
+ swipe_candidate_sender: SyncSender