Merge pull request #792 from mahkoh/jorth/egui
control-center: add in-process control center
This commit is contained in:
commit
d29dea05fb
94 changed files with 9547 additions and 273 deletions
332
Cargo.lock
generated
332
Cargo.lock
generated
|
|
@ -2,6 +2,22 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ab_glyph"
|
||||||
|
version = "0.2.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
|
||||||
|
dependencies = [
|
||||||
|
"ab_glyph_rasterizer",
|
||||||
|
"owned_ttf_parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ab_glyph_rasterizer"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.25.1"
|
version = "0.25.1"
|
||||||
|
|
@ -100,9 +116,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.101"
|
version = "1.0.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "arrayref"
|
||||||
|
|
@ -148,9 +164,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.10.0"
|
version = "2.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake3"
|
name = "blake3"
|
||||||
|
|
@ -178,9 +194,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.1"
|
version = "3.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
|
|
@ -196,9 +212,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.55"
|
version = "1.2.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
|
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"shlex",
|
"shlex",
|
||||||
|
|
@ -223,9 +239,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.43"
|
version = "0.4.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
|
@ -236,9 +252,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.57"
|
version = "4.5.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a"
|
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
|
@ -246,9 +262,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.57"
|
version = "4.5.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238"
|
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
|
@ -259,9 +275,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_complete"
|
name = "clap_complete"
|
||||||
version = "4.5.65"
|
version = "4.5.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "430b4dc2b5e3861848de79627b2bedc9f3342c7da5173a14eaa5d0f8dc18ae5d"
|
checksum = "c757a3b7e39161a4e56f9365141ada2a6c915a8622c408ab6bb4b5d047371031"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
]
|
]
|
||||||
|
|
@ -275,14 +291,14 @@ dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.7"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
|
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
|
|
@ -337,9 +353,9 @@ checksum = "24efe21bd9a78102d1225f10f0a41d9d5b43f4df7ae8235f39a9c79e4d476c1e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.5.5"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
]
|
]
|
||||||
|
|
@ -365,6 +381,72 @@ dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecolor"
|
||||||
|
version = "0.33.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71ddb8ac7643d1dba1bb02110e804406dd459a838efcb14011ced10556711a8e"
|
||||||
|
dependencies = [
|
||||||
|
"emath",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui"
|
||||||
|
version = "0.33.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a9b567d356674e9a5121ed3fedfb0a7c31e059fe71f6972b691bcd0bfc284e3"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"bitflags",
|
||||||
|
"emath",
|
||||||
|
"epaint",
|
||||||
|
"log",
|
||||||
|
"nohash-hasher",
|
||||||
|
"profiling",
|
||||||
|
"smallvec",
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui_tiles"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ef184e589f0a80560bd3b63017634642d1ba112a8a8d9b29341f7cafd04601f"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"egui",
|
||||||
|
"itertools",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "emath"
|
||||||
|
version = "0.33.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "491bdf728bf25ddd9ad60d4cf1c48588fa82c013a2440b91aa7fc43e34a07c32"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "epaint"
|
||||||
|
version = "0.33.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "009d0dd3c2163823a0abdb899451ecbc78798dec545ee91b43aff1fa790bab62"
|
||||||
|
dependencies = [
|
||||||
|
"ab_glyph",
|
||||||
|
"ahash",
|
||||||
|
"ecolor",
|
||||||
|
"emath",
|
||||||
|
"log",
|
||||||
|
"nohash-hasher",
|
||||||
|
"parking_lot",
|
||||||
|
"profiling",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
|
@ -426,38 +508,38 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
|
|
@ -465,7 +547,6 @@ dependencies = [
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -488,19 +569,19 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi",
|
"r-efi 5.3.0",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi",
|
"r-efi 6.0.0",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.0",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
"wasip3",
|
"wasip3",
|
||||||
|
|
@ -618,6 +699,15 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1697e6b71679da96d5c41bb9035116141baadbf59a60625fd66cb3c9584e7b0"
|
checksum = "f1697e6b71679da96d5c41bb9035116141baadbf59a60625fd66cb3c9584e7b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
|
@ -658,6 +748,8 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
"egui",
|
||||||
|
"egui_tiles",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gpu-alloc",
|
"gpu-alloc",
|
||||||
"gpu-alloc-types",
|
"gpu-alloc-types",
|
||||||
|
|
@ -734,9 +826,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.85"
|
version = "0.3.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|
@ -773,7 +865,7 @@ checksum = "28067e7361c0069c3753795d131653f9ea5333aeb35a3855fb2de66447c48ac8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -790,9 +882,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.181"
|
version = "0.2.183"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5"
|
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
|
@ -806,19 +898,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.12"
|
version = "0.1.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linearize"
|
name = "linearize"
|
||||||
version = "0.1.5"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53d5b35550da9461fb8d3acf71c9925afae570bfc45a11857b55138d25c8604d"
|
checksum = "f6e1430c89633736996fd763822abd252e395dbccaaee33be601b4d59678a93e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"linearize-derive",
|
"linearize-derive",
|
||||||
|
|
@ -835,14 +926,14 @@ checksum = "f657db73fbcad5341c5991ddee6c464d4bfd521575c0dc1a47913e0f434defeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.11.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
|
|
@ -875,6 +966,12 @@ dependencies = [
|
||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nohash-hasher"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
@ -889,7 +986,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -943,6 +1040,15 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "owned_ttf_parser"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
|
||||||
|
dependencies = [
|
||||||
|
"ttf-parser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.5"
|
version = "0.12.5"
|
||||||
|
|
@ -997,7 +1103,7 @@ dependencies = [
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1011,35 +1117,29 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.10"
|
version = "1.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
|
checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-internal",
|
"pin-project-internal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-internal"
|
name = "pin-project-internal"
|
||||||
version = "1.1.10"
|
version = "1.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.16"
|
version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
|
|
@ -1049,9 +1149,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.18.0"
|
version = "0.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
|
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
|
|
@ -1073,7 +1173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1086,19 +1186,25 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "profiling"
|
||||||
version = "0.39.0"
|
version = "1.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2e3bf4aa9d243beeb01a7b3bc30b77cfe2c44e24ec02d751a7104a53c2c49a1"
|
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-xml"
|
||||||
|
version = "0.39.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.44"
|
version = "1.0.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
@ -1109,6 +1215,12 @@ version = "5.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
|
@ -1134,7 +1246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
|
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"getrandom 0.4.1",
|
"getrandom 0.4.2",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1201,9 +1313,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.9"
|
version = "0.8.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
|
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "repc"
|
name = "repc"
|
||||||
|
|
@ -1234,9 +1346,9 @@ checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.1.3"
|
version = "1.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
|
|
@ -1314,7 +1426,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1410,9 +1522,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.114"
|
version = "2.0.117"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -1455,7 +1567,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1538,6 +1650,12 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ttf-parser"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uapi"
|
name = "uapi"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
|
|
@ -1566,9 +1684,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.23"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
|
|
@ -1636,9 +1760,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.108"
|
version = "0.2.114"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
|
@ -1649,9 +1773,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.108"
|
version = "0.2.114"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
|
|
@ -1659,22 +1783,22 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.108"
|
version = "0.2.114"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.108"
|
version = "0.2.114"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
@ -1743,7 +1867,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1754,7 +1878,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1967,7 +2091,7 @@ dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
"wasm-metadata",
|
"wasm-metadata",
|
||||||
"wit-bindgen-core",
|
"wit-bindgen-core",
|
||||||
"wit-component",
|
"wit-component",
|
||||||
|
|
@ -1983,7 +2107,7 @@ dependencies = [
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
"wit-bindgen-core",
|
"wit-bindgen-core",
|
||||||
"wit-bindgen-rust",
|
"wit-bindgen-rust",
|
||||||
]
|
]
|
||||||
|
|
@ -2055,26 +2179,26 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.39"
|
version = "0.8.42"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
|
checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.39"
|
version = "0.8.42"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
|
checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.20"
|
version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7"
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|
|
||||||
34
Cargo.toml
34
Cargo.toml
|
|
@ -69,6 +69,8 @@ opera = "1.0.1"
|
||||||
with_builtin_macros = "0.1.0"
|
with_builtin_macros = "0.1.0"
|
||||||
blake3 = "1.8.2"
|
blake3 = "1.8.2"
|
||||||
run-on-drop = "1.0.0"
|
run-on-drop = "1.0.0"
|
||||||
|
egui = { version = "0.33.3", default-features = false }
|
||||||
|
egui_tiles = { version = "0.14.1", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
repc = "0.1.1"
|
repc = "0.1.1"
|
||||||
|
|
@ -87,6 +89,38 @@ opt-level = 3
|
||||||
[profile.dev.package."smallvec"]
|
[profile.dev.package."smallvec"]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
|
[profile.dev.package."egui"]
|
||||||
|
opt-level = 3
|
||||||
|
debug = "line-tables-only"
|
||||||
|
|
||||||
|
[profile.dev.package."emath"]
|
||||||
|
opt-level = 3
|
||||||
|
debug = "line-tables-only"
|
||||||
|
|
||||||
|
[profile.dev.package."epaint"]
|
||||||
|
opt-level = 3
|
||||||
|
debug = "line-tables-only"
|
||||||
|
|
||||||
|
[profile.dev.package."ab_glyph"]
|
||||||
|
opt-level = 3
|
||||||
|
debug = "line-tables-only"
|
||||||
|
|
||||||
|
[profile.dev.package."ab_glyph_rasterizer"]
|
||||||
|
opt-level = 3
|
||||||
|
debug = "line-tables-only"
|
||||||
|
|
||||||
|
[profile.dev.package."owned_ttf_parser"]
|
||||||
|
opt-level = 3
|
||||||
|
debug = "line-tables-only"
|
||||||
|
|
||||||
|
[profile.dev.package."ttf-parser"]
|
||||||
|
opt-level = 3
|
||||||
|
debug = "line-tables-only"
|
||||||
|
|
||||||
|
[profile.dev.package."ecolor"]
|
||||||
|
opt-level = 3
|
||||||
|
debug = "line-tables-only"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
rc_tracking = []
|
rc_tracking = []
|
||||||
it = []
|
it = []
|
||||||
|
|
|
||||||
|
|
@ -8,23 +8,31 @@ pub struct Tree {
|
||||||
pub shaders: &'static [&'static str],
|
pub shaders: &'static [&'static str],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const TREES: &[Tree] = &[Tree {
|
pub const TREES: &[Tree] = &[
|
||||||
root: "src/gfx_apis/vulkan/shaders",
|
Tree {
|
||||||
hash: "src/gfx_apis/vulkan/shaders_hash.txt",
|
root: "src/gfx_apis/vulkan/shaders",
|
||||||
bin: "src/gfx_apis/vulkan/shaders_bin",
|
hash: "src/gfx_apis/vulkan/shaders_hash.txt",
|
||||||
shaders: &[
|
bin: "src/gfx_apis/vulkan/shaders_bin",
|
||||||
"fill.frag",
|
shaders: &[
|
||||||
"fill.vert",
|
"fill.frag",
|
||||||
"tex.vert",
|
"fill.vert",
|
||||||
"tex.frag",
|
"tex.vert",
|
||||||
"out.vert",
|
"tex.frag",
|
||||||
"out.frag",
|
"out.vert",
|
||||||
"legacy/fill.frag",
|
"out.frag",
|
||||||
"legacy/fill.vert",
|
"legacy/fill.frag",
|
||||||
"legacy/tex.vert",
|
"legacy/fill.vert",
|
||||||
"legacy/tex.frag",
|
"legacy/tex.vert",
|
||||||
],
|
"legacy/tex.frag",
|
||||||
}];
|
],
|
||||||
|
},
|
||||||
|
Tree {
|
||||||
|
root: "src/egui_adapter/shaders",
|
||||||
|
hash: "src/egui_adapter/shaders_hash.txt",
|
||||||
|
bin: "src/egui_adapter/shaders_bin",
|
||||||
|
shaders: &["shader.vert", "shader.frag"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
fn calculate_hash(tree: &Tree) -> anyhow::Result<String> {
|
fn calculate_hash(tree: &Tree) -> anyhow::Result<String> {
|
||||||
let dir = WalkDir::new(tree.root);
|
let dir = WalkDir::new(tree.root);
|
||||||
|
|
|
||||||
|
|
@ -1035,10 +1035,21 @@ impl ConfigClient {
|
||||||
position
|
position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_egui_fonts(&self, proportional: Option<Vec<&str>>, monospace: Option<Vec<&str>>) {
|
||||||
|
self.send(&ClientMessage::SetEguiFonts {
|
||||||
|
proportional,
|
||||||
|
monospace,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_middle_click_paste_enabled(&self, enabled: bool) {
|
pub fn set_middle_click_paste_enabled(&self, enabled: bool) {
|
||||||
self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled });
|
self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_control_center(&self) {
|
||||||
|
self.send(&ClientMessage::OpenControlCenter);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
|
pub fn set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
|
||||||
self.send(&ClientMessage::SetWorkspaceDisplayOrder { order });
|
self.send(&ClientMessage::SetWorkspaceDisplayOrder { order });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -841,6 +841,11 @@ pub enum ClientMessage<'a> {
|
||||||
fds: Vec<(i32, i32)>,
|
fds: Vec<(i32, i32)>,
|
||||||
tag: Option<&'a str>,
|
tag: Option<&'a str>,
|
||||||
},
|
},
|
||||||
|
SetEguiFonts {
|
||||||
|
proportional: Option<Vec<&'a str>>,
|
||||||
|
monospace: Option<Vec<&'a str>>,
|
||||||
|
},
|
||||||
|
OpenControlCenter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -380,3 +380,8 @@ pub fn on_unload(f: impl FnOnce() + 'static) {
|
||||||
pub fn set_middle_click_paste_enabled(enabled: bool) {
|
pub fn set_middle_click_paste_enabled(enabled: bool) {
|
||||||
get!().set_middle_click_paste_enabled(enabled);
|
get!().set_middle_click_paste_enabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the control center.
|
||||||
|
pub fn open_control_center() {
|
||||||
|
get!().open_control_center();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,20 @@ pub fn get_bar_position() -> BarPosition {
|
||||||
get!(BarPosition::Top).get_bar_position()
|
get!(BarPosition::Top).get_bar_position()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the proportional fonts used by egui windows.
|
||||||
|
///
|
||||||
|
/// The default is `["sans-serif", "Noto Sans", "Noto Color Emoji"]`.
|
||||||
|
pub fn set_egui_proportional_fonts<'a>(fonts: impl IntoIterator<Item = &'a str>) {
|
||||||
|
get!().set_egui_fonts(Some(fonts.into_iter().collect()), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the monospace fonts used by egui windows.
|
||||||
|
///
|
||||||
|
/// The default is `["monospace", "Noto Sans Mono", "Noto Color Emoji"]`.
|
||||||
|
pub fn set_egui_monospace_fonts<'a>(fonts: impl IntoIterator<Item = &'a str>) {
|
||||||
|
get!().set_egui_fonts(None, Some(fonts.into_iter().collect()));
|
||||||
|
}
|
||||||
|
|
||||||
/// Elements of the compositor whose color can be changed.
|
/// Elements of the compositor whose color can be changed.
|
||||||
pub mod colors {
|
pub mod colors {
|
||||||
use {
|
use {
|
||||||
|
|
|
||||||
|
|
@ -588,7 +588,6 @@ pub trait BackendDrmDevice {
|
||||||
fn set_flip_margin(&self, margin: u64) {
|
fn set_flip_margin(&self, margin: u64) {
|
||||||
let _ = margin;
|
let _ = margin;
|
||||||
}
|
}
|
||||||
#[expect(dead_code)]
|
|
||||||
fn flip_margin(&self) -> Option<u64> {
|
fn flip_margin(&self) -> Option<u64> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
mod clients;
|
mod clients;
|
||||||
mod color;
|
mod color;
|
||||||
mod color_management;
|
mod color_management;
|
||||||
|
mod control_center;
|
||||||
mod damage_tracking;
|
mod damage_tracking;
|
||||||
mod duration;
|
mod duration;
|
||||||
mod generate;
|
mod generate;
|
||||||
|
|
@ -97,6 +98,8 @@ pub enum Cmd {
|
||||||
Clients(ClientsArgs),
|
Clients(ClientsArgs),
|
||||||
/// Inspect the surface tree.
|
/// Inspect the surface tree.
|
||||||
Tree(TreeArgs),
|
Tree(TreeArgs),
|
||||||
|
/// Opens the control center.
|
||||||
|
ControlCenter,
|
||||||
/// Prints the Jay version and exits.
|
/// Prints the Jay version and exits.
|
||||||
Version,
|
Version,
|
||||||
/// Prints the Jay PID and exits.
|
/// Prints the Jay PID and exits.
|
||||||
|
|
@ -243,5 +246,6 @@ pub fn main() {
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
Cmd::RunTests => crate::it::run_tests(),
|
Cmd::RunTests => crate::it::run_tests(),
|
||||||
Cmd::Reexec(a) => reexec::main(cli.global, a),
|
Cmd::Reexec(a) => reexec::main(cli.global, a),
|
||||||
|
Cmd::ControlCenter => control_center::main(cli.global),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
src/cli/control_center.rs
Normal file
32
src/cli/control_center.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
cli::GlobalArgs,
|
||||||
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
|
wire::{jay_compositor, jay_open_control_center_request},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main(global: GlobalArgs) {
|
||||||
|
with_tool_client(global.log_level, |tc| async move {
|
||||||
|
let cc = ControlCenter { tc: tc.clone() };
|
||||||
|
cc.run().await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ControlCenter {
|
||||||
|
tc: Rc<ToolClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenter {
|
||||||
|
async fn run(self) {
|
||||||
|
let tc = &self.tc;
|
||||||
|
let comp = tc.jay_compositor().await;
|
||||||
|
let id = tc.id();
|
||||||
|
tc.send(jay_compositor::OpenControlCenter { self_id: comp, id });
|
||||||
|
jay_open_control_center_request::Failed::handle(&tc, id, (), |_, ev| {
|
||||||
|
fatal!("Could not open the control center: {}", ev.msg);
|
||||||
|
});
|
||||||
|
tc.round_trip().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ use {
|
||||||
clientmem::{self, ClientMemError},
|
clientmem::{self, ClientMemError},
|
||||||
cmm::{cmm_manager::ColorManager, cmm_primaries::Primaries},
|
cmm::{cmm_manager::ColorManager, cmm_primaries::Primaries},
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
|
control_center::redraw_control_centers,
|
||||||
copy_device::CopyDeviceRegistry,
|
copy_device::CopyDeviceRegistry,
|
||||||
cpu_worker::{CpuWorker, CpuWorkerError},
|
cpu_worker::{CpuWorker, CpuWorkerError},
|
||||||
criteria::{
|
criteria::{
|
||||||
|
|
@ -72,6 +73,7 @@ use {
|
||||||
fdcloser::FdCloser,
|
fdcloser::FdCloser,
|
||||||
nice::{did_elevate_scheduler, elevate_scheduler},
|
nice::{did_elevate_scheduler, elevate_scheduler},
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
|
object_drop_queue::ObjectDropQueue,
|
||||||
oserror::OsError,
|
oserror::OsError,
|
||||||
queue::AsyncQueue,
|
queue::AsyncQueue,
|
||||||
rc_eq::RcEq,
|
rc_eq::RcEq,
|
||||||
|
|
@ -390,6 +392,9 @@ fn start_compositor2(
|
||||||
supports_presentation_feedback: Default::default(),
|
supports_presentation_feedback: Default::default(),
|
||||||
eventfd_cache,
|
eventfd_cache,
|
||||||
lazy_event_sources: Default::default(),
|
lazy_event_sources: Default::default(),
|
||||||
|
bo_drop_queue: Rc::new(ObjectDropQueue::new(&ring)),
|
||||||
|
egg_state: Default::default(),
|
||||||
|
control_centers: Default::default(),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
@ -414,6 +419,7 @@ fn start_compositor2(
|
||||||
let _compositor = engine.spawn("compositor", start_compositor3(state.clone(), test_future));
|
let _compositor = engine.spawn("compositor", start_compositor3(state.clone(), test_future));
|
||||||
ring.run()?;
|
ring.run()?;
|
||||||
state.clear();
|
state.clear();
|
||||||
|
engine.clear();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -594,6 +600,10 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
||||||
"lazy event sources",
|
"lazy event sources",
|
||||||
handle_lazy_event_sources(state.clone()),
|
handle_lazy_event_sources(state.clone()),
|
||||||
),
|
),
|
||||||
|
eng.spawn(
|
||||||
|
"redraw control centers",
|
||||||
|
redraw_control_centers(state.clone()),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -734,8 +744,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
wlr_output_heads: Default::default(),
|
wlr_output_heads: Default::default(),
|
||||||
});
|
});
|
||||||
let schedule = Rc::new(OutputSchedule::new(
|
let schedule = Rc::new(OutputSchedule::new(
|
||||||
&state.ring,
|
state,
|
||||||
&state.eng,
|
|
||||||
&connector_data,
|
&connector_data,
|
||||||
&persistent_state,
|
&persistent_state,
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -478,7 +478,7 @@ impl ConfigProxyHandler {
|
||||||
} else {
|
} else {
|
||||||
Some(self.get_keymap(keymap)?)
|
Some(self.get_keymap(keymap)?)
|
||||||
};
|
};
|
||||||
dev.set_keymap(map);
|
dev.set_keymap(&self.state, map);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -532,13 +532,13 @@ impl ConfigProxyHandler {
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(input_device)?;
|
let dev = self.get_device_handler_data(input_device)?;
|
||||||
let output = self.get_output_node(connector)?;
|
let output = self.get_output_node(connector)?;
|
||||||
dev.set_output(Some(&output.global));
|
dev.set_output(&self.state, Some(&output.global));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_remove_input_mapping(&self, input_device: InputDevice) -> Result<(), CphError> {
|
fn handle_remove_input_mapping(&self, input_device: InputDevice) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(input_device)?;
|
let dev = self.get_device_handler_data(input_device)?;
|
||||||
dev.set_output(None);
|
dev.set_output(&self.state, None);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -788,7 +788,7 @@ impl ConfigProxyHandler {
|
||||||
Some(self.get_seat(seat)?)
|
Some(self.get_seat(seat)?)
|
||||||
};
|
};
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_seat(seat);
|
dev.set_seat(&self.state, seat);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -798,7 +798,7 @@ impl ConfigProxyHandler {
|
||||||
left_handed: bool,
|
left_handed: bool,
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_left_handed(left_handed);
|
dev.set_left_handed(&self.state, left_handed);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -813,31 +813,31 @@ impl ConfigProxyHandler {
|
||||||
ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive,
|
ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive,
|
||||||
_ => return Err(CphError::UnknownAccelProfile(accel_profile)),
|
_ => return Err(CphError::UnknownAccelProfile(accel_profile)),
|
||||||
};
|
};
|
||||||
dev.set_accel_profile(profile);
|
dev.set_accel_profile(&self.state, profile);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_accel_speed(&self, device: InputDevice, speed: f64) -> Result<(), CphError> {
|
fn handle_set_accel_speed(&self, device: InputDevice, speed: f64) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_accel_speed(speed);
|
dev.set_accel_speed(&self.state, speed);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_px_per_wheel_scroll(&self, device: InputDevice, px: f64) -> Result<(), CphError> {
|
fn handle_set_px_per_wheel_scroll(&self, device: InputDevice, px: f64) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_px_per_scroll_wheel(px);
|
dev.set_px_per_scroll_wheel(&self.state, px);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_tap_enabled(&self, device: InputDevice, enabled: bool) -> Result<(), CphError> {
|
fn handle_set_tap_enabled(&self, device: InputDevice, enabled: bool) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_tap_enabled(enabled);
|
dev.set_tap_enabled(&self.state, enabled);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_drag_enabled(&self, device: InputDevice, enabled: bool) -> Result<(), CphError> {
|
fn handle_set_drag_enabled(&self, device: InputDevice, enabled: bool) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_drag_enabled(enabled);
|
dev.set_drag_enabled(&self.state, enabled);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -847,7 +847,7 @@ impl ConfigProxyHandler {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_natural_scrolling_enabled(enabled);
|
dev.set_natural_scrolling_enabled(&self.state, enabled);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -857,7 +857,7 @@ impl ConfigProxyHandler {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_drag_lock_enabled(enabled);
|
dev.set_drag_lock_enabled(&self.state, enabled);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -867,7 +867,7 @@ impl ConfigProxyHandler {
|
||||||
matrix: [[f64; 2]; 2],
|
matrix: [[f64; 2]; 2],
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_transform_matrix(matrix);
|
dev.set_transform_matrix(&self.state, matrix);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -877,7 +877,7 @@ impl ConfigProxyHandler {
|
||||||
matrix: [[f32; 3]; 2],
|
matrix: [[f32; 3]; 2],
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_calibration_matrix(matrix);
|
dev.set_calibration_matrix(&self.state, matrix);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -893,7 +893,7 @@ impl ConfigProxyHandler {
|
||||||
CLICK_METHOD_CLICKFINGER => InputDeviceClickMethod::Clickfinger,
|
CLICK_METHOD_CLICKFINGER => InputDeviceClickMethod::Clickfinger,
|
||||||
_ => return Err(CphError::UnknownClickMethod(click_method)),
|
_ => return Err(CphError::UnknownClickMethod(click_method)),
|
||||||
};
|
};
|
||||||
dev.set_click_method(method);
|
dev.set_click_method(&self.state, method);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -903,7 +903,7 @@ impl ConfigProxyHandler {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let dev = self.get_device_handler_data(device)?;
|
let dev = self.get_device_handler_data(device)?;
|
||||||
dev.set_middle_button_emulation_enabled(enabled);
|
dev.set_middle_button_emulation_enabled(&self.state, enabled);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -951,8 +951,10 @@ impl ConfigProxyHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_flip_margin(&self, device: DrmDevice, margin: Duration) -> Result<(), CphError> {
|
fn handle_set_flip_margin(&self, device: DrmDevice, margin: Duration) -> Result<(), CphError> {
|
||||||
self.get_drm_device(device)?
|
self.get_drm_device(device)?.set_flip_margin(
|
||||||
.set_flip_margin(margin.as_nanos().try_into().unwrap_or(u64::MAX));
|
&self.state,
|
||||||
|
margin.as_nanos().try_into().unwrap_or(u64::MAX),
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -987,7 +989,7 @@ impl ConfigProxyHandler {
|
||||||
match device {
|
match device {
|
||||||
Some(dev) => self
|
Some(dev) => self
|
||||||
.get_drm_device(dev)?
|
.get_drm_device(dev)?
|
||||||
.set_direct_scanout_enabled(enabled),
|
.set_direct_scanout_enabled(&self.state, enabled),
|
||||||
_ => self.state.direct_scanout_enabled.set(enabled),
|
_ => self.state.direct_scanout_enabled.set(enabled),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -1123,11 +1125,11 @@ impl ConfigProxyHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_idle(&self, timeout: Duration) {
|
fn handle_set_idle(&self, timeout: Duration) {
|
||||||
self.state.idle.set_timeout(timeout);
|
self.state.idle.set_timeout(&self.state, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_idle_grace_period(&self, period: Duration) {
|
fn handle_set_idle_grace_period(&self, period: Duration) {
|
||||||
self.state.idle.set_grace_period(period);
|
self.state.idle.set_grace_period(&self.state, period);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_explicit_sync_enabled(&self, enabled: bool) {
|
fn handle_set_explicit_sync_enabled(&self, enabled: bool) {
|
||||||
|
|
@ -1484,7 +1486,7 @@ impl ConfigProxyHandler {
|
||||||
match connector {
|
match connector {
|
||||||
Some(c) => {
|
Some(c) => {
|
||||||
let connector = self.get_output_node(c)?;
|
let connector = self.get_output_node(c)?;
|
||||||
connector.schedule.set_cursor_hz(hz);
|
connector.schedule.set_cursor_hz(&self.state, hz);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let Some((hz, _)) = map_cursor_hz(hz) else {
|
let Some((hz, _)) = map_cursor_hz(hz) else {
|
||||||
|
|
@ -1807,6 +1809,16 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_set_egui_fonts(&self, proportional: Option<Vec<&str>>, monospace: Option<Vec<&str>>) {
|
||||||
|
self.state.set_egui_fonts(proportional, monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_open_control_center(&self) {
|
||||||
|
if let Err(e) = self.state.open_control_center() {
|
||||||
|
log::error!("Could not open control center: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_set_log_level(&self, level: ConfigLogLevel) {
|
fn handle_set_log_level(&self, level: ConfigLogLevel) {
|
||||||
self.state.set_log_level(level.into());
|
self.state.set_log_level(level.into());
|
||||||
}
|
}
|
||||||
|
|
@ -3311,6 +3323,11 @@ impl ConfigProxyHandler {
|
||||||
fds,
|
fds,
|
||||||
tag,
|
tag,
|
||||||
} => self.handle_run(prog, args, env, fds, tag).wrn("run")?,
|
} => self.handle_run(prog, args, env, fds, tag).wrn("run")?,
|
||||||
|
ClientMessage::SetEguiFonts {
|
||||||
|
proportional,
|
||||||
|
monospace,
|
||||||
|
} => self.handle_set_egui_fonts(proportional, monospace),
|
||||||
|
ClientMessage::OpenControlCenter => self.handle_open_control_center(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
681
src/control_center.rs
Normal file
681
src/control_center.rs
Normal file
|
|
@ -0,0 +1,681 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
control_center::{
|
||||||
|
cc_clients::{ClientPane, ClientsPane},
|
||||||
|
cc_color_management::ColorManagementPane,
|
||||||
|
cc_compositor::CompositorPane,
|
||||||
|
cc_gpus::GpusPane,
|
||||||
|
cc_idle::IdlePane,
|
||||||
|
cc_input::InputPane,
|
||||||
|
cc_look_and_feel::LookAndFeelPane,
|
||||||
|
cc_outputs::OutputsPane,
|
||||||
|
cc_window::{WindowPane, WindowSearchPane},
|
||||||
|
cc_xwayland::XwaylandPane,
|
||||||
|
},
|
||||||
|
egui_adapter::egui_platform::{
|
||||||
|
EggError, EggWindow, EggWindowOwner,
|
||||||
|
icons::{ICON_CLOSE, ICON_DRAG_INDICATOR, ICON_INFO},
|
||||||
|
},
|
||||||
|
macros::Bitflag,
|
||||||
|
state::State,
|
||||||
|
utils::{
|
||||||
|
asyncevent::AsyncEvent, copyhashmap::CopyHashMap,
|
||||||
|
event_listener::LazyEventSourceListener, numcell::NumCell, static_text::StaticText,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
egui::{
|
||||||
|
Align, CentralPanel, Checkbox, Color32, ComboBox, Context, CursorIcon, DragValue, Frame,
|
||||||
|
Grid, InnerResponse, Label, Layout, Response, Rgba, RichText, ScrollArea, Sense, SidePanel,
|
||||||
|
Stroke, TextBuffer, TextEdit, Ui, UiBuilder, Visuals, Widget, WidgetText, emath::Numeric,
|
||||||
|
vec2,
|
||||||
|
},
|
||||||
|
egui_tiles::{ResizeState, TabState, Tile, TileId, Tiles, Tree},
|
||||||
|
linearize::{Linearize, LinearizeExt},
|
||||||
|
std::{
|
||||||
|
cell::RefCell,
|
||||||
|
hash::Hash,
|
||||||
|
mem,
|
||||||
|
ops::{Deref, DerefMut, RangeInclusive},
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod cc_clients;
|
||||||
|
mod cc_color_management;
|
||||||
|
mod cc_compositor;
|
||||||
|
mod cc_criterion;
|
||||||
|
mod cc_gpus;
|
||||||
|
mod cc_idle;
|
||||||
|
mod cc_input;
|
||||||
|
mod cc_look_and_feel;
|
||||||
|
mod cc_outputs;
|
||||||
|
mod cc_sidebar;
|
||||||
|
mod cc_window;
|
||||||
|
mod cc_xwayland;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ControlCenterError {
|
||||||
|
#[error("Could not get the egg context")]
|
||||||
|
GetEggContext(#[source] EggError),
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_ids!(ControlCenterIds, ControlCenterId, u64);
|
||||||
|
|
||||||
|
pub async fn redraw_control_centers(state: Rc<State>) {
|
||||||
|
let cc = &state.control_centers;
|
||||||
|
loop {
|
||||||
|
cc.redraw.triggered().await;
|
||||||
|
let interests = cc.change.take();
|
||||||
|
for cc in cc.control_centers.lock().values() {
|
||||||
|
if cc.inner.interests.interests.get().intersects(interests) {
|
||||||
|
cc.inner.window.request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ControlCenters {
|
||||||
|
ids: ControlCenterIds,
|
||||||
|
change: NumCell<ControlCenterInterest>,
|
||||||
|
redraw: AsyncEvent,
|
||||||
|
control_centers: CopyHashMap<ControlCenterId, Rc<ControlCenter>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
ControlCenterInterest: u32;
|
||||||
|
CCI_COMPOSITOR,
|
||||||
|
CCI_IDLE,
|
||||||
|
CCI_COLOR_MANAGEMENT,
|
||||||
|
CCI_XWAYLAND,
|
||||||
|
CCI_OUTPUTS,
|
||||||
|
CCI_GPUS,
|
||||||
|
CCI_INPUT,
|
||||||
|
CCI_LOOK_AND_FEEL,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ControlCenter {
|
||||||
|
inner: Rc<ControlCenterInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_ids!(PaneIds, PaneId, u64);
|
||||||
|
|
||||||
|
struct ControlCenterInner {
|
||||||
|
id: ControlCenterId,
|
||||||
|
state: Rc<State>,
|
||||||
|
tree: RefCell<Settings>,
|
||||||
|
window: Rc<EggWindow>,
|
||||||
|
pane_ids: PaneIds,
|
||||||
|
interests: Rc<Interests>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Interests {
|
||||||
|
interests: NumCell<ControlCenterInterest>,
|
||||||
|
interests_array: [NumCell<u64>; <ControlCenterInterest as Bitflag>::Type::BITS as usize],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
tree: Tree<Pane>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pane {
|
||||||
|
id: PaneId,
|
||||||
|
ps: PaneState,
|
||||||
|
own_interests: ControlCenterInterest,
|
||||||
|
cc_interests: Rc<Interests>,
|
||||||
|
ty: PaneType,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PaneState {
|
||||||
|
errors: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PaneType {
|
||||||
|
Compositor(CompositorPane),
|
||||||
|
Idle(IdlePane),
|
||||||
|
ColorManagement(ColorManagementPane),
|
||||||
|
Xwayland(XwaylandPane),
|
||||||
|
Outputs(Box<OutputsPane>),
|
||||||
|
GPUs(GpusPane),
|
||||||
|
Input(InputPane),
|
||||||
|
LookAndFeel(LookAndFeelPane),
|
||||||
|
Clients(ClientsPane),
|
||||||
|
Client(ClientPane),
|
||||||
|
WindowSearch(WindowSearchPane),
|
||||||
|
Window(WindowPane),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CcBehavior<'a> {
|
||||||
|
cc: &'a Rc<ControlCenterInner>,
|
||||||
|
close: Option<TileId>,
|
||||||
|
open: Option<PaneType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenters {
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.control_centers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pane {
|
||||||
|
fn title(&self, res: &mut String) {
|
||||||
|
match &self.ty {
|
||||||
|
PaneType::Compositor(v) => v.title(res),
|
||||||
|
PaneType::Idle(v) => v.title(res),
|
||||||
|
PaneType::ColorManagement(v) => v.title(res),
|
||||||
|
PaneType::Xwayland(v) => v.title(res),
|
||||||
|
PaneType::Outputs(v) => v.title(res),
|
||||||
|
PaneType::GPUs(v) => v.title(res),
|
||||||
|
PaneType::Input(v) => v.title(res),
|
||||||
|
PaneType::LookAndFeel(v) => v.title(res),
|
||||||
|
PaneType::Clients(v) => v.title(res),
|
||||||
|
PaneType::Client(v) => v.title(res),
|
||||||
|
PaneType::WindowSearch(v) => v.title(res),
|
||||||
|
PaneType::Window(v) => v.title(res),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
|
||||||
|
match &mut self.ty {
|
||||||
|
PaneType::Compositor(p) => p.show(ui),
|
||||||
|
PaneType::Idle(p) => p.show(ui),
|
||||||
|
PaneType::ColorManagement(p) => p.show(ui),
|
||||||
|
PaneType::Xwayland(p) => p.show(behavior, ui),
|
||||||
|
PaneType::Outputs(p) => p.show(&mut self.ps, ui),
|
||||||
|
PaneType::GPUs(p) => p.show(ui),
|
||||||
|
PaneType::Input(p) => p.show(&mut self.ps, ui),
|
||||||
|
PaneType::LookAndFeel(p) => p.show(ui),
|
||||||
|
PaneType::Clients(p) => p.show(behavior, ui),
|
||||||
|
PaneType::Client(p) => p.show(behavior, ui),
|
||||||
|
PaneType::WindowSearch(p) => p.show(behavior, ui),
|
||||||
|
PaneType::Window(p) => p.show(behavior, ui),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaneType {
|
||||||
|
fn interest(&self) -> ControlCenterInterest {
|
||||||
|
match self {
|
||||||
|
PaneType::Compositor(_) => CCI_COMPOSITOR,
|
||||||
|
PaneType::Idle(_) => CCI_IDLE,
|
||||||
|
PaneType::ColorManagement(_) => CCI_COLOR_MANAGEMENT,
|
||||||
|
PaneType::Xwayland(_) => CCI_XWAYLAND,
|
||||||
|
PaneType::Outputs(_) => CCI_OUTPUTS,
|
||||||
|
PaneType::GPUs(_) => CCI_GPUS,
|
||||||
|
PaneType::Input(_) => CCI_INPUT,
|
||||||
|
PaneType::LookAndFeel(_) => CCI_LOOK_AND_FEEL,
|
||||||
|
PaneType::Clients(_) => ControlCenterInterest::none(),
|
||||||
|
PaneType::Client(_) => ControlCenterInterest::none(),
|
||||||
|
PaneType::WindowSearch(_) => ControlCenterInterest::none(),
|
||||||
|
PaneType::Window(_) => ControlCenterInterest::none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl egui_tiles::Behavior<Pane> for CcBehavior<'_> {
|
||||||
|
fn pane_ui(&mut self, ui: &mut Ui, tile_id: TileId, pane: &mut Pane) -> egui_tiles::UiResponse {
|
||||||
|
let mut drag = false;
|
||||||
|
Frame::central_panel(ui.style()).show(ui, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
drag = ui
|
||||||
|
.add(icon_label(ICON_DRAG_INDICATOR).sense(Sense::drag()))
|
||||||
|
.total_drag_delta()
|
||||||
|
.map(|d| d.length() >= 5.0)
|
||||||
|
.unwrap_or(false);
|
||||||
|
let mut title = String::new();
|
||||||
|
pane.title(&mut title);
|
||||||
|
if ui
|
||||||
|
.add(icon_label(&title).sense(Sense::click()))
|
||||||
|
.middle_clicked()
|
||||||
|
{
|
||||||
|
self.close = Some(tile_id);
|
||||||
|
}
|
||||||
|
if ui
|
||||||
|
.add(icon_label(ICON_CLOSE).sense(Sense::click()))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.close = Some(tile_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.separator();
|
||||||
|
show_errors(ui, &mut pane.ps);
|
||||||
|
ui.scope_builder(UiBuilder::new().id(("pane", pane.id)), |ui| {
|
||||||
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
ui.allocate_space(vec2(ui.available_width(), 0.0));
|
||||||
|
pane.show(self, ui);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if drag {
|
||||||
|
egui_tiles::UiResponse::DragStarted
|
||||||
|
} else {
|
||||||
|
egui_tiles::UiResponse::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_title_for_pane(&mut self, _pane: &Pane) -> WidgetText {
|
||||||
|
"".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_hover_cursor_icon(&self) -> CursorIcon {
|
||||||
|
CursorIcon::Default
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_title_for_tile(&mut self, tiles: &Tiles<Pane>, tile_id: TileId) -> WidgetText {
|
||||||
|
fn add_title(tiles: &Tiles<Pane>, res: &mut String, first: &mut bool, tile_id: TileId) {
|
||||||
|
if !mem::take(first) {
|
||||||
|
res.push_str("/");
|
||||||
|
}
|
||||||
|
let Some(tile) = tiles.get(tile_id) else {
|
||||||
|
res.push_str("MISSING TILE");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
match tile {
|
||||||
|
Tile::Pane(p) => p.title(res),
|
||||||
|
Tile::Container(c) => {
|
||||||
|
let mut first = true;
|
||||||
|
for &tile_id in c.children() {
|
||||||
|
add_title(tiles, res, &mut first, tile_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut res = String::new();
|
||||||
|
let mut first = true;
|
||||||
|
add_title(tiles, &mut res, &mut first, tile_id);
|
||||||
|
res.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_tab_button(
|
||||||
|
&mut self,
|
||||||
|
_tiles: &Tiles<Pane>,
|
||||||
|
tile_id: TileId,
|
||||||
|
button_response: Response,
|
||||||
|
) -> Response {
|
||||||
|
if button_response.middle_clicked() {
|
||||||
|
self.close = Some(tile_id);
|
||||||
|
}
|
||||||
|
button_response
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize_stroke(&self, style: &egui::Style, resize_state: ResizeState) -> Stroke {
|
||||||
|
match resize_state {
|
||||||
|
ResizeState::Idle => style.visuals.widgets.noninteractive.bg_stroke,
|
||||||
|
ResizeState::Hovering => style.visuals.widgets.hovered.fg_stroke,
|
||||||
|
ResizeState::Dragging => style.visuals.widgets.active.fg_stroke,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_bar_color(&self, visuals: &Visuals) -> Color32 {
|
||||||
|
(Rgba::from(visuals.panel_fill) * Rgba::from_gray(0.8)).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_bg_color(
|
||||||
|
&self,
|
||||||
|
visuals: &Visuals,
|
||||||
|
_tiles: &Tiles<Pane>,
|
||||||
|
_tile_id: TileId,
|
||||||
|
state: &TabState,
|
||||||
|
) -> Color32 {
|
||||||
|
match state.active {
|
||||||
|
true => visuals.panel_fill,
|
||||||
|
false => self.tab_bar_color(visuals),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EggWindowOwner for ControlCenterInner {
|
||||||
|
fn close(&self) {
|
||||||
|
self.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(self: Rc<Self>, ctx: &Context) {
|
||||||
|
let settings = &mut *self.tree.borrow_mut();
|
||||||
|
SidePanel::left("sidebar").show(ctx, |ui| self.show_sidebar(&mut settings.tree, ui));
|
||||||
|
CentralPanel::default()
|
||||||
|
.frame(
|
||||||
|
Frame::central_panel(&ctx.style())
|
||||||
|
.outer_margin(0.0)
|
||||||
|
.inner_margin(0.0),
|
||||||
|
)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
let tree = &mut settings.tree;
|
||||||
|
let mut behavior = CcBehavior {
|
||||||
|
cc: &self,
|
||||||
|
close: Default::default(),
|
||||||
|
open: Default::default(),
|
||||||
|
};
|
||||||
|
tree.ui(&mut behavior, ui);
|
||||||
|
if let Some(close) = behavior.close {
|
||||||
|
tree.set_visible(close, false);
|
||||||
|
tree.remove_recursively(close);
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
}
|
||||||
|
if let Some(ty) = behavior.open {
|
||||||
|
self.open(tree, ty);
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn open_control_center(self: &Rc<Self>) -> Result<Rc<ControlCenter>, ControlCenterError> {
|
||||||
|
let ctx = self
|
||||||
|
.get_egg_context()
|
||||||
|
.map_err(ControlCenterError::GetEggContext)?;
|
||||||
|
let window = ctx.create_window("Control Center");
|
||||||
|
let cc = Rc::new(ControlCenter {
|
||||||
|
inner: Rc::new(ControlCenterInner {
|
||||||
|
id: self.control_centers.ids.next(),
|
||||||
|
window,
|
||||||
|
state: self.clone(),
|
||||||
|
tree: RefCell::new(Settings {
|
||||||
|
tree: Tree::new_tabs("abcd", vec![]),
|
||||||
|
}),
|
||||||
|
pane_ids: Default::default(),
|
||||||
|
interests: Default::default(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
cc.inner.window.set_owner(Some(cc.inner.clone()));
|
||||||
|
self.control_centers
|
||||||
|
.control_centers
|
||||||
|
.set(cc.inner.id, cc.clone());
|
||||||
|
Ok(cc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger_cci(&self, cci: ControlCenterInterest) {
|
||||||
|
self.control_centers.change.or_assign(cci);
|
||||||
|
self.control_centers.redraw.trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
fn close(&self) {
|
||||||
|
self.window.set_owner(None);
|
||||||
|
self.tree.borrow_mut().tree = Tree::empty("");
|
||||||
|
self.state.control_centers.control_centers.remove(&self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ControlCenter {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.inner.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
fn create_pane(&self, ty: PaneType) -> Pane {
|
||||||
|
let pane = Pane {
|
||||||
|
id: self.pane_ids.next(),
|
||||||
|
ps: PaneState {
|
||||||
|
errors: Default::default(),
|
||||||
|
},
|
||||||
|
own_interests: ty.interest(),
|
||||||
|
cc_interests: self.interests.clone(),
|
||||||
|
ty,
|
||||||
|
};
|
||||||
|
let own = pane.own_interests;
|
||||||
|
for (idx, v) in pane.cc_interests.interests_array.iter().enumerate() {
|
||||||
|
let interest = ControlCenterInterest(1 << idx);
|
||||||
|
if own.intersects(interest) && v.fetch_add(1) == 0 {
|
||||||
|
pane.cc_interests.interests.or_assign(interest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pane
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&self, tree: &mut Tree<Pane>, ty: PaneType) {
|
||||||
|
let pane = self.create_pane(ty);
|
||||||
|
let id = tree.tiles.insert_pane(pane);
|
||||||
|
if let Some(root) = tree.root
|
||||||
|
&& let Some(tile) = tree.tiles.get_mut(root)
|
||||||
|
{
|
||||||
|
match tile {
|
||||||
|
Tile::Container(c) => {
|
||||||
|
c.add_child(id);
|
||||||
|
}
|
||||||
|
Tile::Pane(_) => {
|
||||||
|
let root = tree.tiles.insert_tab_tile(vec![root, id]);
|
||||||
|
tree.root = Some(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tree.root = Some(id);
|
||||||
|
}
|
||||||
|
tree.make_active(|t, _| t == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Pane {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let own = self.own_interests;
|
||||||
|
for (idx, v) in self.cc_interests.interests_array.iter().enumerate() {
|
||||||
|
let interest = ControlCenterInterest(1 << idx);
|
||||||
|
if own.intersects(interest) && v.fetch_sub(1) == 1 {
|
||||||
|
self.cc_interests.interests.and_assign(!interest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn icon_label(icon: &str) -> Label {
|
||||||
|
Label::new(icon).selectable(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grid_label(ui: &mut Ui, label: &str) {
|
||||||
|
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||||
|
ui.label(label);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grid_label_ui<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
|
||||||
|
ui.with_layout(Layout::right_to_left(Align::Center), add_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tip(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
|
||||||
|
icon_label(ICON_INFO).ui(ui).on_hover_ui(add_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_edit(ui: &mut Ui, v: &mut dyn TextBuffer) -> Response {
|
||||||
|
TextEdit::singleline(v)
|
||||||
|
.clip_text(false)
|
||||||
|
.min_size(vec2(200.0, 0.0))
|
||||||
|
.ui(ui)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_errors(ui: &mut Ui, pane: &mut PaneState) {
|
||||||
|
if pane.errors.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut to_remove = None;
|
||||||
|
for (idx, e) in pane.errors.iter().enumerate() {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
Frame::new().inner_margin(5.0).show(ui, |ui| {
|
||||||
|
if ui.button(ICON_CLOSE).clicked() {
|
||||||
|
to_remove = Some(idx);
|
||||||
|
}
|
||||||
|
ui.label(
|
||||||
|
RichText::new("Error:")
|
||||||
|
.strong()
|
||||||
|
.color(ui.style().visuals.error_fg_color),
|
||||||
|
);
|
||||||
|
ui.add(Label::new(e).wrap());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(idx) = to_remove {
|
||||||
|
pane.errors.remove(idx);
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grid<R>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
id_salt: impl Hash,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> InnerResponse<R> {
|
||||||
|
let mut spacing = ui.spacing().item_spacing;
|
||||||
|
spacing.x *= 3.0;
|
||||||
|
Grid::new(id_salt).spacing(spacing).show(ui, add_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn row<R>(ui: &mut Ui, name: &str, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
|
||||||
|
row_ui(ui, name, |_| (), add_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn row_ui<R, S>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
label: impl FnOnce(&mut Ui) -> S,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> R {
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label_ui(ui, |ui| {
|
||||||
|
ui.label(name);
|
||||||
|
label(ui);
|
||||||
|
});
|
||||||
|
add_contents(ui)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool(ui: &mut Ui, name: &str, old: bool, set: impl FnOnce(bool)) {
|
||||||
|
bool_ui(ui, name, |_| (), old, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool_ui<R>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
label: impl FnOnce(&mut Ui) -> R,
|
||||||
|
mut v: bool,
|
||||||
|
set: impl FnOnce(bool),
|
||||||
|
) {
|
||||||
|
row_ui(ui, name, label, |ui| {
|
||||||
|
if Checkbox::without_text(&mut v).ui(ui).changed() {
|
||||||
|
set(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_only_bool(ui: &mut Ui, name: &str, old: bool) {
|
||||||
|
read_only_bool_ui(ui, name, |_| (), old);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_only_bool_ui<R>(ui: &mut Ui, name: &str, label: impl FnOnce(&mut Ui) -> R, mut v: bool) {
|
||||||
|
row_ui(ui, name, label, |ui| {
|
||||||
|
ui.add_enabled_ui(false, |ui| Checkbox::without_text(&mut v).ui(ui));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn combo_box<T>(ui: &mut Ui, name: &str, old: T, set: impl FnOnce(T))
|
||||||
|
where
|
||||||
|
T: StaticText + Linearize + PartialEq + Copy,
|
||||||
|
{
|
||||||
|
combo_box_ui(ui, name, |_| (), old, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn combo_box_ui<R, T>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
label: impl FnOnce(&mut Ui) -> R,
|
||||||
|
mut v: T,
|
||||||
|
set: impl FnOnce(T),
|
||||||
|
) where
|
||||||
|
T: StaticText + Linearize + PartialEq + Copy,
|
||||||
|
{
|
||||||
|
row_ui(ui, name, label, |ui| {
|
||||||
|
let old = v;
|
||||||
|
ComboBox::from_id_salt(name)
|
||||||
|
.selected_text(v.text())
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for s in T::variants() {
|
||||||
|
ui.selectable_value(&mut v, s, s.text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if old != v {
|
||||||
|
set(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drag_value<N>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
old: N,
|
||||||
|
range: RangeInclusive<N>,
|
||||||
|
speed: f64,
|
||||||
|
set: impl FnOnce(N),
|
||||||
|
) where
|
||||||
|
N: Numeric,
|
||||||
|
{
|
||||||
|
drag_value_ui(ui, name, |_| (), old, range, speed, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drag_value_ui<R, N>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
label: impl FnOnce(&mut Ui) -> R,
|
||||||
|
mut v: N,
|
||||||
|
range: RangeInclusive<N>,
|
||||||
|
speed: f64,
|
||||||
|
set: impl FnOnce(N),
|
||||||
|
) where
|
||||||
|
N: Numeric,
|
||||||
|
{
|
||||||
|
row_ui(ui, name, label, |ui| {
|
||||||
|
if DragValue::new(&mut v)
|
||||||
|
.range(range)
|
||||||
|
.speed(speed)
|
||||||
|
.ui(ui)
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
set(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label(ui: &mut Ui, name: &str, text: impl Into<WidgetText>) {
|
||||||
|
row(ui, name, |ui| ui.label(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
trait GridExt {
|
||||||
|
fn row(&mut self) -> impl DerefMut<Target = Ui>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GridExt for Ui {
|
||||||
|
fn row(&mut self) -> impl DerefMut<Target = Ui> {
|
||||||
|
GridRow { ui: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GridRow<'a> {
|
||||||
|
ui: &'a mut Ui,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for GridRow<'_> {
|
||||||
|
type Target = Ui;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.ui
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for GridRow<'_> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut *self.ui
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for GridRow<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.end_row();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LazyEventSourceListener for ControlCenterInner {
|
||||||
|
fn triggered(self: Rc<Self>) {
|
||||||
|
self.window.request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
463
src/control_center/cc_clients.rs
Normal file
463
src/control_center/cc_clients.rs
Normal file
|
|
@ -0,0 +1,463 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientId},
|
||||||
|
control_center::{
|
||||||
|
CcBehavior, ControlCenterInner, PaneType,
|
||||||
|
cc_criterion::{CcCriterion, CritImpl, CritRegex},
|
||||||
|
cc_window::show_window_collapsible,
|
||||||
|
grid, icon_label, label, read_only_bool,
|
||||||
|
},
|
||||||
|
criteria::{CritMgrExt, CritUpstreamNode, crit_leaf::CritLeafMatcher},
|
||||||
|
egui_adapter::egui_platform::icons::ICON_OPEN_IN_NEW,
|
||||||
|
state::State,
|
||||||
|
tree::ToplevelData,
|
||||||
|
utils::{
|
||||||
|
copyhashmap::CopyHashMap, static_text::StaticText,
|
||||||
|
toplevel_identifier::ToplevelIdentifier,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
|
egui::{
|
||||||
|
CollapsingHeader, DragValue, Sense, TextFormat, Ui, Widget, cache::CacheTrait,
|
||||||
|
text::LayoutJob,
|
||||||
|
},
|
||||||
|
linearize::Linearize,
|
||||||
|
std::{
|
||||||
|
any::Any,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum ClientCrit {
|
||||||
|
SandboxEngine(CritRegex),
|
||||||
|
SandboxAppId(CritRegex),
|
||||||
|
SandboxInstanceId(CritRegex),
|
||||||
|
Sandboxed,
|
||||||
|
Uid(i32),
|
||||||
|
Pid(i32),
|
||||||
|
IsXwayland,
|
||||||
|
Comm(CritRegex),
|
||||||
|
Exe(CritRegex),
|
||||||
|
Tag(CritRegex),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Linearize)]
|
||||||
|
pub enum ClientCritTy {
|
||||||
|
SandboxEngine,
|
||||||
|
SandboxAppId,
|
||||||
|
SandboxInstanceId,
|
||||||
|
Sandboxed,
|
||||||
|
Uid,
|
||||||
|
Pid,
|
||||||
|
IsXwayland,
|
||||||
|
Comm,
|
||||||
|
Exe,
|
||||||
|
Tag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ClientCrit {
|
||||||
|
fn default() -> Self {
|
||||||
|
ClientCrit::Comm(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticText for ClientCritTy {
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
ClientCritTy::SandboxEngine => "Sandbox Engine",
|
||||||
|
ClientCritTy::SandboxAppId => "Sandbox App ID",
|
||||||
|
ClientCritTy::SandboxInstanceId => "Sandbox Instance ID",
|
||||||
|
ClientCritTy::Sandboxed => "Sandboxed",
|
||||||
|
ClientCritTy::Uid => "UID",
|
||||||
|
ClientCritTy::Pid => "PID",
|
||||||
|
ClientCritTy::IsXwayland => "Is Xwayland",
|
||||||
|
ClientCritTy::Comm => "Comm",
|
||||||
|
ClientCritTy::Exe => "Exe",
|
||||||
|
ClientCritTy::Tag => "Tag",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CritImpl for ClientCrit {
|
||||||
|
type Type = ClientCritTy;
|
||||||
|
type Target = Rc<Client>;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type {
|
||||||
|
macro_rules! map {
|
||||||
|
($($n:ident,)*) => {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
Self::$n { .. } => ClientCritTy::$n,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
map! {
|
||||||
|
SandboxEngine,
|
||||||
|
SandboxAppId,
|
||||||
|
SandboxInstanceId,
|
||||||
|
Sandboxed,
|
||||||
|
Uid,
|
||||||
|
Pid,
|
||||||
|
IsXwayland,
|
||||||
|
Comm,
|
||||||
|
Exe,
|
||||||
|
Tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_ty(ty: Self::Type) -> Self {
|
||||||
|
match ty {
|
||||||
|
ClientCritTy::SandboxEngine => Self::SandboxEngine(Default::default()),
|
||||||
|
ClientCritTy::SandboxAppId => Self::SandboxAppId(Default::default()),
|
||||||
|
ClientCritTy::SandboxInstanceId => Self::SandboxInstanceId(Default::default()),
|
||||||
|
ClientCritTy::Sandboxed => Self::Sandboxed,
|
||||||
|
ClientCritTy::Uid => Self::Uid(Default::default()),
|
||||||
|
ClientCritTy::Pid => Self::Pid(Default::default()),
|
||||||
|
ClientCritTy::IsXwayland => Self::IsXwayland,
|
||||||
|
ClientCritTy::Comm => Self::Comm(Default::default()),
|
||||||
|
ClientCritTy::Exe => Self::Exe(Default::default()),
|
||||||
|
ClientCritTy::Tag => Self::Tag(Default::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self, ui: &mut Ui) -> bool {
|
||||||
|
match self {
|
||||||
|
ClientCrit::SandboxEngine(v) => v.show(ui),
|
||||||
|
ClientCrit::SandboxAppId(v) => v.show(ui),
|
||||||
|
ClientCrit::SandboxInstanceId(v) => v.show(ui),
|
||||||
|
ClientCrit::Sandboxed => false,
|
||||||
|
ClientCrit::Uid(v) => DragValue::new(v).ui(ui).changed(),
|
||||||
|
ClientCrit::Pid(v) => DragValue::new(v).ui(ui).changed(),
|
||||||
|
ClientCrit::IsXwayland => false,
|
||||||
|
ClientCrit::Comm(v) => v.show(ui),
|
||||||
|
ClientCrit::Exe(v) => v.show(ui),
|
||||||
|
ClientCrit::Tag(v) => v.show(ui),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_crit(&self, state: &Rc<State>) -> Option<Rc<dyn CritUpstreamNode<Self::Target>>> {
|
||||||
|
let m = &state.cl_matcher_manager;
|
||||||
|
let res = match self {
|
||||||
|
ClientCrit::SandboxEngine(v) => m.sandbox_engine(v.to_crit()?),
|
||||||
|
ClientCrit::SandboxAppId(v) => m.sandbox_app_id(v.to_crit()?),
|
||||||
|
ClientCrit::SandboxInstanceId(v) => m.sandbox_instance_id(v.to_crit()?),
|
||||||
|
ClientCrit::Sandboxed => m.sandboxed(),
|
||||||
|
ClientCrit::Uid(v) => m.uid(*v),
|
||||||
|
ClientCrit::Pid(v) => m.pid(*v),
|
||||||
|
ClientCrit::IsXwayland => m.is_xwayland(),
|
||||||
|
ClientCrit::Comm(v) => m.comm(v.to_crit()?),
|
||||||
|
ClientCrit::Exe(v) => m.exe(v.to_crit()?),
|
||||||
|
ClientCrit::Tag(v) => m.tag(v.to_crit()?),
|
||||||
|
};
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(
|
||||||
|
state: &State,
|
||||||
|
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
state.cl_matcher_manager.not(upstream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(
|
||||||
|
state: &State,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
all: bool,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
state.cl_matcher_manager.list(upstream, all)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exactly(
|
||||||
|
state: &State,
|
||||||
|
n: usize,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
state.cl_matcher_manager.exactly(upstream, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ClientsPane {
|
||||||
|
state: Rc<State>,
|
||||||
|
filter: bool,
|
||||||
|
criterion: CcCriterion<ClientCrit>,
|
||||||
|
matched: Rc<Matched>,
|
||||||
|
leaf: Option<Rc<CritLeafMatcher<Rc<Client>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Matched {
|
||||||
|
slf: Weak<ControlCenterInner>,
|
||||||
|
clients: CopyHashMap<ClientId, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Matched {
|
||||||
|
fn request_frame(&self) {
|
||||||
|
if let Some(slf) = self.slf.upgrade() {
|
||||||
|
slf.window.request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_clients_pane(self: &Rc<Self>) -> ClientsPane {
|
||||||
|
let mut pane = ClientsPane {
|
||||||
|
state: self.state.clone(),
|
||||||
|
filter: false,
|
||||||
|
criterion: Default::default(),
|
||||||
|
matched: Rc::new(Matched {
|
||||||
|
slf: Rc::downgrade(self),
|
||||||
|
clients: Default::default(),
|
||||||
|
}),
|
||||||
|
leaf: Default::default(),
|
||||||
|
};
|
||||||
|
pane.update_matcher();
|
||||||
|
pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientsPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Clients");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
|
||||||
|
if ui.checkbox(&mut self.filter, "Filter").changed() && !self.filter {
|
||||||
|
self.criterion = Default::default();
|
||||||
|
self.update_matcher();
|
||||||
|
}
|
||||||
|
let mut clear_clients = false;
|
||||||
|
if self.filter && self.criterion.show(ui) {
|
||||||
|
clear_clients = self.update_matcher();
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
let mut clients: Vec<_> = self.matched.clients.lock().keys().copied().collect();
|
||||||
|
clients.sort();
|
||||||
|
for id in clients {
|
||||||
|
let Ok(client) = self.state.clients.get(id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
show_client_collapsible(behavior, ui, &client);
|
||||||
|
}
|
||||||
|
if clear_clients {
|
||||||
|
self.matched.clients.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_matcher(&mut self) -> bool {
|
||||||
|
let mut clear_clients = false;
|
||||||
|
let state = &self.state;
|
||||||
|
if let Some(new) = self.criterion.to_crit(state) {
|
||||||
|
clear_clients = true;
|
||||||
|
let matched = self.matched.clone();
|
||||||
|
let leaf = state.cl_matcher_manager.leaf(&new, move |data| {
|
||||||
|
matched.clients.set(data, ());
|
||||||
|
matched.request_frame();
|
||||||
|
Box::new({
|
||||||
|
let matched = matched.clone();
|
||||||
|
move || {
|
||||||
|
matched.clients.remove(&data);
|
||||||
|
matched.request_frame();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
state.cl_matcher_manager.rematch_all(state);
|
||||||
|
self.leaf = Some(leaf);
|
||||||
|
}
|
||||||
|
clear_clients
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ClientPane {
|
||||||
|
client: Rc<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_client_pane(self: &Rc<Self>, client: &Rc<Client>) -> ClientPane {
|
||||||
|
ClientPane {
|
||||||
|
client: client.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Client ");
|
||||||
|
res.push_str(&self.client.pid_info.comm);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
|
||||||
|
show_client(behavior, ui, &self.client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_client_collapsible(behavior: &mut CcBehavior, ui: &mut Ui, client: &Rc<Client>) {
|
||||||
|
let mut layout_job = LayoutJob::default();
|
||||||
|
layout_job.append(
|
||||||
|
"Client",
|
||||||
|
0.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.inactive.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
layout_job.append(
|
||||||
|
&client.id.to_string(),
|
||||||
|
10.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.active.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
layout_job.append(
|
||||||
|
&client.pid_info.comm,
|
||||||
|
10.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.inactive.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
CollapsingHeader::new(layout_job)
|
||||||
|
.id_salt(("client", client.id))
|
||||||
|
.show(ui, |ui| {
|
||||||
|
if icon_label(ICON_OPEN_IN_NEW)
|
||||||
|
.sense(Sense::CLICK)
|
||||||
|
.ui(ui)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
behavior.open = Some(PaneType::Client(behavior.cc.create_client_pane(client)));
|
||||||
|
}
|
||||||
|
show_client(behavior, ui, client)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_client(behavior: &mut CcBehavior<'_>, ui: &mut Ui, client: &Client) {
|
||||||
|
grid(ui, ("client", client.id), |ui| {
|
||||||
|
label(ui, "ID", client.id.to_string());
|
||||||
|
label(ui, "PID", client.pid_info.pid.to_string());
|
||||||
|
label(ui, "UID", client.pid_info.uid.to_string());
|
||||||
|
label(ui, "comm", &client.pid_info.comm);
|
||||||
|
label(ui, "exe", &client.pid_info.exe);
|
||||||
|
if client.acceptor.sandboxed {
|
||||||
|
read_only_bool(ui, "Sandboxed", true);
|
||||||
|
}
|
||||||
|
if client.acceptor.secure {
|
||||||
|
read_only_bool(ui, "Secure", true);
|
||||||
|
}
|
||||||
|
if client.is_xwayland {
|
||||||
|
read_only_bool(ui, "Xwayland", true);
|
||||||
|
}
|
||||||
|
if let Some(v) = &client.acceptor.sandbox_engine {
|
||||||
|
label(ui, "Sandbox Engine", v);
|
||||||
|
}
|
||||||
|
if let Some(v) = &client.acceptor.app_id {
|
||||||
|
label(ui, "App ID", v);
|
||||||
|
}
|
||||||
|
if let Some(v) = &client.acceptor.instance_id {
|
||||||
|
label(ui, "Instance ID", v);
|
||||||
|
}
|
||||||
|
if let Some(v) = &client.acceptor.tag {
|
||||||
|
label(ui, "Tag", v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if ui.button("Kill").clicked() {
|
||||||
|
client.state.clients.kill(client.id);
|
||||||
|
}
|
||||||
|
ui.collapsing("Capabilities", |ui| {
|
||||||
|
ui.add_enabled_ui(false, |ui| {
|
||||||
|
for (k, v) in client.effective_caps.get().to_map() {
|
||||||
|
if v {
|
||||||
|
ui.checkbox(&mut true, k.text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.collapsing("Windows", |ui| {
|
||||||
|
let matcher = ui.memory_mut(|m| {
|
||||||
|
m.caches
|
||||||
|
.cache::<ClientWindowMatchersCache>()
|
||||||
|
.get(behavior.cc, client.id)
|
||||||
|
.clone()
|
||||||
|
});
|
||||||
|
let mut windows: Vec<_> = matcher.windows.lock().keys().copied().collect();
|
||||||
|
windows.sort();
|
||||||
|
for id in windows {
|
||||||
|
let Some(window) = client.state.toplevels.get(&id).and_then(|v| v.upgrade()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
show_window_collapsible(behavior, ui, &window);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ClientWindowMatchersCache {
|
||||||
|
generation: u64,
|
||||||
|
matchers: AHashMap<ClientId, CachedWindowMatcher>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CachedWindowMatcher {
|
||||||
|
generation: u64,
|
||||||
|
_matcher: Rc<CritLeafMatcher<ToplevelData>>,
|
||||||
|
matchers: Rc<WindowMatchers>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WindowMatchers {
|
||||||
|
cc: Weak<ControlCenterInner>,
|
||||||
|
windows: CopyHashMap<ToplevelIdentifier, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientWindowMatchersCache {
|
||||||
|
fn get(&mut self, cc: &Rc<ControlCenterInner>, id: ClientId) -> &Rc<WindowMatchers> {
|
||||||
|
let res = self.matchers.entry(id).or_insert_with(|| {
|
||||||
|
let state = &cc.state;
|
||||||
|
let node = state.cl_matcher_manager.id(id);
|
||||||
|
let node = state.tl_matcher_manager.client(state, &node);
|
||||||
|
let matchers = Rc::new(WindowMatchers {
|
||||||
|
cc: Rc::downgrade(&cc),
|
||||||
|
windows: Default::default(),
|
||||||
|
});
|
||||||
|
let matchers2 = matchers.clone();
|
||||||
|
let matcher = state.tl_matcher_manager.leaf(&node, move |id| {
|
||||||
|
matchers2.windows.set(id, ());
|
||||||
|
if let Some(cc) = matchers2.cc.upgrade() {
|
||||||
|
cc.window.request_redraw();
|
||||||
|
}
|
||||||
|
let matchers2 = matchers2.clone();
|
||||||
|
Box::new(move || {
|
||||||
|
matchers2.windows.remove(&id);
|
||||||
|
if let Some(cc) = matchers2.cc.upgrade() {
|
||||||
|
cc.window.request_redraw();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let res = CachedWindowMatcher {
|
||||||
|
generation: 0,
|
||||||
|
_matcher: matcher,
|
||||||
|
matchers,
|
||||||
|
};
|
||||||
|
state.cl_matcher_manager.rematch_all(state);
|
||||||
|
state.tl_matcher_manager.rematch_all(state);
|
||||||
|
res
|
||||||
|
});
|
||||||
|
res.generation = self.generation;
|
||||||
|
&res.matchers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Sync for ClientWindowMatchersCache {}
|
||||||
|
unsafe impl Send for ClientWindowMatchersCache {}
|
||||||
|
|
||||||
|
impl CacheTrait for ClientWindowMatchersCache {
|
||||||
|
fn update(&mut self) {
|
||||||
|
self.matchers.retain(|_, m| m.generation == self.generation);
|
||||||
|
self.generation += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.matchers.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/control_center/cc_color_management.rs
Normal file
36
src/control_center/cc_color_management.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
control_center::{ControlCenterInner, bool, grid, read_only_bool},
|
||||||
|
state::State,
|
||||||
|
},
|
||||||
|
egui::Ui,
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ColorManagementPane {
|
||||||
|
state: Rc<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_color_management_pane(self: &Rc<Self>) -> ColorManagementPane {
|
||||||
|
ColorManagementPane {
|
||||||
|
state: self.state.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorManagementPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Color Management");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, ui: &mut Ui) {
|
||||||
|
let s = &self.state;
|
||||||
|
grid(ui, "settings", |ui| {
|
||||||
|
bool(ui, "Enabled", s.color_management_enabled.get(), |b| {
|
||||||
|
s.set_color_management_enabled(b);
|
||||||
|
});
|
||||||
|
read_only_bool(ui, "Available", s.color_management_available());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/control_center/cc_compositor.rs
Normal file
88
src/control_center/cc_compositor.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
compositor::{LIBEI_SOCKET, WAYLAND_DISPLAY},
|
||||||
|
control_center::{ControlCenterInner, bool, combo_box, grid, label, row},
|
||||||
|
state::State,
|
||||||
|
version::VERSION,
|
||||||
|
},
|
||||||
|
egui::{DragValue, OpenUrl, Ui, Widget},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CompositorPane {
|
||||||
|
state: Rc<State>,
|
||||||
|
switch_to_vt: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_compositor_pane(self: &Rc<Self>) -> CompositorPane {
|
||||||
|
CompositorPane {
|
||||||
|
state: self.state.clone(),
|
||||||
|
switch_to_vt: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompositorPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Compositor");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, ui: &mut Ui) {
|
||||||
|
let s = &self.state;
|
||||||
|
grid(ui, "compositor", |ui| {
|
||||||
|
row(ui, "Repository", |ui| {
|
||||||
|
let url = "https://github.com/mahkoh/jay";
|
||||||
|
if ui.link(url).clicked() {
|
||||||
|
ui.ctx().open_url(OpenUrl::new_tab(url));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
label(ui, "Version", VERSION);
|
||||||
|
label(ui, "PID", s.pid.to_string());
|
||||||
|
if let Some(acceptor) = s.acceptor.get() {
|
||||||
|
label(ui, WAYLAND_DISPLAY, acceptor.socket_name());
|
||||||
|
}
|
||||||
|
if let Some(dir) = &s.config_dir {
|
||||||
|
label(ui, "Config DIR", dir);
|
||||||
|
}
|
||||||
|
bool(ui, "Libei Socket", s.enable_ei_acceptor.get(), |v| {
|
||||||
|
s.set_ei_socket_enabled(v);
|
||||||
|
});
|
||||||
|
if let Some(a) = s.ei_acceptor.get() {
|
||||||
|
label(ui, LIBEI_SOCKET, a.socket_name());
|
||||||
|
}
|
||||||
|
combo_box(
|
||||||
|
ui,
|
||||||
|
"Workspace Display Order",
|
||||||
|
s.workspace_display_order.get(),
|
||||||
|
|o| s.set_workspace_display_order(o),
|
||||||
|
);
|
||||||
|
if let Some(logger) = &s.logger {
|
||||||
|
combo_box(ui, "Log Level", logger.level(), |l| s.set_log_level(l));
|
||||||
|
row(ui, "Log File", |ui| {
|
||||||
|
let path = logger.path().to_string();
|
||||||
|
if ui
|
||||||
|
.link(&path)
|
||||||
|
.on_hover_text_at_pointer("Copy to clipboard")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
ui.ctx().copy_text(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if ui.button("Quit").clicked() {
|
||||||
|
s.quit();
|
||||||
|
}
|
||||||
|
if ui.button("Reload Config").clicked() {
|
||||||
|
s.reload_config();
|
||||||
|
}
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let button = ui.button("Switch to VT");
|
||||||
|
DragValue::new(&mut self.switch_to_vt).ui(ui);
|
||||||
|
if button.clicked() {
|
||||||
|
s.backend.get().switch_to(self.switch_to_vt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
253
src/control_center/cc_criterion.rs
Normal file
253
src/control_center/cc_criterion.rs
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
criteria::{CritLiteralOrRegex, CritUpstreamNode},
|
||||||
|
egui_adapter::egui_platform::icons::ICON_CLOSE,
|
||||||
|
state::State,
|
||||||
|
utils::{numcell::NumCell, static_text::StaticText},
|
||||||
|
},
|
||||||
|
ahash::AHashSet,
|
||||||
|
egui::{ComboBox, DragValue, Ui, UiBuilder, Widget},
|
||||||
|
isnt::std_1::collections::IsntHashSetExt,
|
||||||
|
linearize::{Linearize, LinearizeExt},
|
||||||
|
regex::Regex,
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum CcCriterion<T> {
|
||||||
|
Not(Box<Self>),
|
||||||
|
List(Vec<Self>, bool),
|
||||||
|
Exactly(usize, Vec<Self>),
|
||||||
|
T(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for CcCriterion<T>
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::T(T::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Linearize)]
|
||||||
|
enum CompoundCritTy {
|
||||||
|
Not,
|
||||||
|
All,
|
||||||
|
Any,
|
||||||
|
Exactly,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
|
enum CritTy<T> {
|
||||||
|
Compound(CompoundCritTy),
|
||||||
|
T(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticText for CompoundCritTy {
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Not => "Not",
|
||||||
|
Self::All => "All",
|
||||||
|
Self::Any => "Any",
|
||||||
|
Self::Exactly => "Exactly",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> StaticText for CritTy<T>
|
||||||
|
where
|
||||||
|
T: StaticText,
|
||||||
|
{
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Compound(t) => t.text(),
|
||||||
|
Self::T(t) => t.text(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritImpl: Default {
|
||||||
|
type Type: Copy + Eq + PartialEq + StaticText + Linearize;
|
||||||
|
type Target;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type;
|
||||||
|
fn from_ty(ty: Self::Type) -> Self;
|
||||||
|
#[must_use]
|
||||||
|
fn show(&mut self, ui: &mut Ui) -> bool;
|
||||||
|
|
||||||
|
fn to_crit(&self, state: &Rc<State>) -> Option<Rc<dyn CritUpstreamNode<Self::Target>>>;
|
||||||
|
fn not(
|
||||||
|
state: &State,
|
||||||
|
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>>;
|
||||||
|
fn list(
|
||||||
|
state: &State,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
all: bool,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>>;
|
||||||
|
fn exactly(
|
||||||
|
state: &State,
|
||||||
|
n: usize,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CcCriterion<T>
|
||||||
|
where
|
||||||
|
T: CritImpl,
|
||||||
|
{
|
||||||
|
#[must_use]
|
||||||
|
pub fn show(&mut self, ui: &mut Ui) -> bool {
|
||||||
|
let mut changed = false;
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let mut v = self.ty();
|
||||||
|
let old = v;
|
||||||
|
ComboBox::from_id_salt("ty")
|
||||||
|
.selected_text(v.text())
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for s in CompoundCritTy::variants() {
|
||||||
|
ui.selectable_value(&mut v, CritTy::Compound(s), s.text());
|
||||||
|
}
|
||||||
|
for s in T::Type::variants() {
|
||||||
|
ui.selectable_value(&mut v, CritTy::T(s), s.text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if old != v {
|
||||||
|
*self = match v {
|
||||||
|
CritTy::Compound(CompoundCritTy::Not) => {
|
||||||
|
CcCriterion::Not(Default::default())
|
||||||
|
}
|
||||||
|
CritTy::Compound(CompoundCritTy::All) => {
|
||||||
|
CcCriterion::List(Default::default(), true)
|
||||||
|
}
|
||||||
|
CritTy::Compound(CompoundCritTy::Any) => {
|
||||||
|
CcCriterion::List(Default::default(), false)
|
||||||
|
}
|
||||||
|
CritTy::Compound(CompoundCritTy::Exactly) => {
|
||||||
|
CcCriterion::Exactly(1, Default::default())
|
||||||
|
}
|
||||||
|
CritTy::T(t) => CcCriterion::T(T::from_ty(t)),
|
||||||
|
};
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
CcCriterion::Not(n) => changed |= n.show(ui),
|
||||||
|
CcCriterion::List(_, _) => {}
|
||||||
|
CcCriterion::Exactly(n, _) => {
|
||||||
|
changed |= DragValue::new(n).ui(ui).changed();
|
||||||
|
}
|
||||||
|
CcCriterion::T(t) => changed |= t.show(ui),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
match self {
|
||||||
|
CcCriterion::Not(_) => {}
|
||||||
|
CcCriterion::List(v, _) | CcCriterion::Exactly(_, v) => {
|
||||||
|
ui.indent("compound", |ui| {
|
||||||
|
let mut to_remove = AHashSet::new();
|
||||||
|
for (idx, v) in v.iter_mut().enumerate() {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button(ICON_CLOSE).clicked() {
|
||||||
|
changed = true;
|
||||||
|
to_remove.insert(idx);
|
||||||
|
}
|
||||||
|
ui.scope_builder(UiBuilder::new().id_salt(idx), |ui| {
|
||||||
|
changed |= v.show(ui);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let i = NumCell::new(0);
|
||||||
|
v.retain(|_| to_remove.not_contains(&i.fetch_add(1)));
|
||||||
|
if ui.button("Add").clicked() {
|
||||||
|
v.push(CcCriterion::default());
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
CcCriterion::T(_) => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ty(&self) -> CritTy<T::Type> {
|
||||||
|
match self {
|
||||||
|
CcCriterion::Not(_) => CritTy::Compound(CompoundCritTy::Not),
|
||||||
|
CcCriterion::List(_, true) => CritTy::Compound(CompoundCritTy::All),
|
||||||
|
CcCriterion::List(_, false) => CritTy::Compound(CompoundCritTy::Any),
|
||||||
|
CcCriterion::Exactly(_, _) => CritTy::Compound(CompoundCritTy::Exactly),
|
||||||
|
CcCriterion::T(t) => CritTy::T(t.ty()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_crit(&self, state: &Rc<State>) -> Option<Rc<dyn CritUpstreamNode<T::Target>>> {
|
||||||
|
match self {
|
||||||
|
CcCriterion::Not(t) => Some(T::not(state, &t.to_crit(state)?)),
|
||||||
|
CcCriterion::List(v, all) => {
|
||||||
|
let mut upstream = Vec::with_capacity(v.len());
|
||||||
|
for v in v {
|
||||||
|
upstream.push(v.to_crit(state)?);
|
||||||
|
}
|
||||||
|
Some(T::list(state, &upstream, *all))
|
||||||
|
}
|
||||||
|
CcCriterion::Exactly(n, v) => {
|
||||||
|
let mut upstream = Vec::with_capacity(v.len());
|
||||||
|
for v in v {
|
||||||
|
upstream.push(v.to_crit(state)?);
|
||||||
|
}
|
||||||
|
Some(T::exactly(state, *n, &upstream))
|
||||||
|
}
|
||||||
|
CcCriterion::T(t) => t.to_crit(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any(&self, mut any: impl FnMut(&T) -> bool) -> bool {
|
||||||
|
self.any_(&mut any)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn any_(&self, any: &mut impl FnMut(&T) -> bool) -> bool {
|
||||||
|
match self {
|
||||||
|
CcCriterion::Not(v) => v.any_(any),
|
||||||
|
CcCriterion::List(v, _) => v.iter().any(|v| v.any_(any)),
|
||||||
|
CcCriterion::Exactly(_, v) => v.iter().any(|v| v.any_(any)),
|
||||||
|
CcCriterion::T(t) => any(t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CritRegex {
|
||||||
|
pub text: String,
|
||||||
|
pub regex: Option<Option<Regex>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CritRegex {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
text: Default::default(),
|
||||||
|
regex: Some(Some(Regex::new("").unwrap())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CritRegex {
|
||||||
|
pub fn show(&mut self, ui: &mut Ui) -> bool {
|
||||||
|
let mut is_regex = self.regex.is_some();
|
||||||
|
let mut changed = false;
|
||||||
|
changed |= ui.text_edit_singleline(&mut self.text).changed();
|
||||||
|
changed |= ui.checkbox(&mut is_regex, "Regex").changed();
|
||||||
|
if changed {
|
||||||
|
self.regex = is_regex.then(|| Regex::new(&self.text).ok());
|
||||||
|
}
|
||||||
|
if let Some(None) = self.regex {
|
||||||
|
ui.label("Error: Invalid regex");
|
||||||
|
}
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_crit(&self) -> Option<CritLiteralOrRegex> {
|
||||||
|
match &self.regex {
|
||||||
|
None => Some(CritLiteralOrRegex::Literal(self.text.clone())),
|
||||||
|
Some(v) => Some(CritLiteralOrRegex::Regex(v.clone()?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
146
src/control_center/cc_gpus.rs
Normal file
146
src/control_center/cc_gpus.rs
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
control_center::{
|
||||||
|
ControlCenterInner, GridExt, bool, combo_box, grid, grid_label, label, row,
|
||||||
|
},
|
||||||
|
egui_adapter::egui_platform::icons::{ICON_ADD, ICON_REMOVE},
|
||||||
|
state::{DrmDevData, State},
|
||||||
|
},
|
||||||
|
egui::{Checkbox, CollapsingHeader, DragValue, TextFormat, Ui, Widget, text::LayoutJob},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct GpusPane {
|
||||||
|
state: Rc<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_gpus_pane(self: &Rc<Self>) -> GpusPane {
|
||||||
|
GpusPane {
|
||||||
|
state: self.state.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpusPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("GPUs");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, ui: &mut Ui) {
|
||||||
|
let devs = self.state.drm_devs.lock();
|
||||||
|
let mut devs: Vec<_> = devs.iter().collect();
|
||||||
|
devs.sort_by_key(|d| d.0);
|
||||||
|
for dev in devs {
|
||||||
|
self.show_dev(ui, dev.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_dev(&self, ui: &mut Ui, dev: &DrmDevData) {
|
||||||
|
let title_buf;
|
||||||
|
let title = match dev.devnode.as_deref() {
|
||||||
|
Some(t) => t,
|
||||||
|
_ => {
|
||||||
|
let dev_t = dev.dev.dev_t();
|
||||||
|
title_buf = format!("{}:{}", uapi::major(dev_t), uapi::minor(dev_t));
|
||||||
|
&title_buf
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut layout_job = LayoutJob::default();
|
||||||
|
layout_job.append(
|
||||||
|
title,
|
||||||
|
0.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.active.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if let Some(model) = &dev.model {
|
||||||
|
layout_job.append(
|
||||||
|
model,
|
||||||
|
10.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.inactive.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ui.collapsing(layout_job, |ui| {
|
||||||
|
grid(ui, ("settings", dev.dev.id()), |ui| {
|
||||||
|
macro_rules! string {
|
||||||
|
($field:ident, $name:expr) => {
|
||||||
|
if let Some(v) = &dev.$field {
|
||||||
|
label(ui, $name, v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
string!(vendor, "Vendor");
|
||||||
|
string!(model, "Model");
|
||||||
|
string!(devnode, "Devnode");
|
||||||
|
string!(syspath, "Syspath");
|
||||||
|
if let Some(v) = dev.pci_id {
|
||||||
|
label(ui, "PCI ID", format!("{:x}:{:x}", v.vendor, v.model));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let v = dev.dev.dev_t();
|
||||||
|
label(ui, "Dev", format!("{}:{}", uapi::major(v), uapi::minor(v)));
|
||||||
|
}
|
||||||
|
combo_box(ui, "API", dev.dev.gtx_api(), |v| dev.dev.set_gfx_api(v));
|
||||||
|
row(ui, "Primary Device", |ui| {
|
||||||
|
let mut v = dev.dev.is_render_device();
|
||||||
|
let old = v;
|
||||||
|
ui.add_enabled(!v, Checkbox::without_text(&mut v));
|
||||||
|
if v != old {
|
||||||
|
dev.dev.make_render_device();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bool(
|
||||||
|
ui,
|
||||||
|
"Direct Scanout",
|
||||||
|
dev.dev.direct_scanout_enabled(),
|
||||||
|
|v| dev.set_direct_scanout_enabled(&self.state, v),
|
||||||
|
);
|
||||||
|
if let Some(mut v) = dev.dev.flip_margin() {
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label(ui, "Flip Margin");
|
||||||
|
let old = v;
|
||||||
|
let denom = 1_000_000.0;
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let mut s = v as f64 / denom;
|
||||||
|
let res = DragValue::new(&mut s)
|
||||||
|
.range(0.0..=50.0)
|
||||||
|
.speed(0.1)
|
||||||
|
.fixed_decimals(1)
|
||||||
|
.ui(ui);
|
||||||
|
if res.changed() {
|
||||||
|
v = (s * denom) as u64;
|
||||||
|
}
|
||||||
|
if ui.button(ICON_REMOVE).clicked() {
|
||||||
|
v = v.saturating_sub(100_000);
|
||||||
|
}
|
||||||
|
if ui.button(ICON_ADD).clicked() {
|
||||||
|
v += 100_000;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if old != v {
|
||||||
|
dev.set_flip_margin(&self.state, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CollapsingHeader::new("Connectors")
|
||||||
|
.default_open(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
let mut cs: Vec<_> = dev
|
||||||
|
.connectors
|
||||||
|
.lock()
|
||||||
|
.values()
|
||||||
|
.map(|v| v.connector.kernel_id().to_string())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
cs.sort();
|
||||||
|
for c in cs {
|
||||||
|
ui.label(c);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/control_center/cc_idle.rs
Normal file
72
src/control_center/cc_idle.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
control_center::{ControlCenterInner, grid, row},
|
||||||
|
state::State,
|
||||||
|
},
|
||||||
|
egui::{CollapsingHeader, DragValue, Ui, Widget},
|
||||||
|
std::{rc::Rc, time::Duration},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct IdlePane {
|
||||||
|
state: Rc<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_idle_pane(self: &Rc<Self>) -> IdlePane {
|
||||||
|
IdlePane {
|
||||||
|
state: self.state.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdlePane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Idle");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, ui: &mut Ui) {
|
||||||
|
grid(ui, "sliders", |ui| {
|
||||||
|
for interval in [true, false] {
|
||||||
|
let label = match interval {
|
||||||
|
true => "Interval",
|
||||||
|
false => "Grace period",
|
||||||
|
};
|
||||||
|
let idle = &self.state.idle;
|
||||||
|
let field = match interval {
|
||||||
|
true => &idle.timeout,
|
||||||
|
false => &idle.grace_period,
|
||||||
|
};
|
||||||
|
row(ui, label, |ui| {
|
||||||
|
let secs = field.get().as_secs();
|
||||||
|
let mut minutes = secs / 60;
|
||||||
|
let mut seconds = secs % 60;
|
||||||
|
let mut changed = false;
|
||||||
|
changed |= DragValue::new(&mut minutes).ui(ui).changed();
|
||||||
|
ui.label("minutes");
|
||||||
|
changed |= DragValue::new(&mut seconds).range(0..=59).ui(ui).changed();
|
||||||
|
ui.label("seconds");
|
||||||
|
if changed {
|
||||||
|
let duration =
|
||||||
|
Duration::from_secs(minutes.saturating_mul(60).saturating_add(seconds));
|
||||||
|
match interval {
|
||||||
|
true => idle.set_timeout(&self.state, duration),
|
||||||
|
false => idle.set_grace_period(&self.state, duration),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let inhibitors = self.state.idle.inhibitors.lock();
|
||||||
|
let mut is: Vec<_> = inhibitors.values().collect();
|
||||||
|
is.sort_by_key(|is| is.inhibit_id);
|
||||||
|
CollapsingHeader::new(format!("Inhibitors ({})", is.len()))
|
||||||
|
.id_salt("Inhibitors")
|
||||||
|
.show(ui, |ui| {
|
||||||
|
for i in is {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(&i.client.pid_info.comm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
679
src/control_center/cc_input.rs
Normal file
679
src/control_center/cc_input.rs
Normal file
|
|
@ -0,0 +1,679 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
backend::{InputDeviceCapability, InputDeviceId},
|
||||||
|
control_center::{
|
||||||
|
ControlCenterInner, GridExt, PaneState, bool, bool_ui, combo_box, combo_box_ui,
|
||||||
|
drag_value, drag_value_ui, grid, grid_label, grid_label_ui, label, text_edit, tip,
|
||||||
|
},
|
||||||
|
egui_adapter::egui_platform::icons::ICON_PENDING,
|
||||||
|
ifs::{
|
||||||
|
wl_output::WlOutputGlobal,
|
||||||
|
wl_seat::{SeatId, WlSeatGlobal},
|
||||||
|
},
|
||||||
|
kbvm::KbvmMap,
|
||||||
|
state::{DeviceHandlerData, State},
|
||||||
|
utils::{errorfmt::ErrorFmt, static_text::StaticText},
|
||||||
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
|
egui::{
|
||||||
|
CollapsingHeader, ComboBox, DragValue, Event, Grid, Id, TextBuffer, TextFormat, Ui,
|
||||||
|
UiBuilder, ViewportCommand, Widget, emath::Numeric, text::LayoutJob,
|
||||||
|
},
|
||||||
|
isnt::std_1::string::IsntStringExt,
|
||||||
|
jay_config::keyboard::syms::KeySym,
|
||||||
|
kbvm::Keysym,
|
||||||
|
linearize::LinearizeExt,
|
||||||
|
rand::random,
|
||||||
|
std::{mem, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct InputPane {
|
||||||
|
state: Rc<State>,
|
||||||
|
paste_requested: Option<Id>,
|
||||||
|
keymaps: AHashMap<Key, KeymapState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
|
enum Key {
|
||||||
|
Seat(SeatId),
|
||||||
|
Dev(InputDeviceId),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KeymapState {
|
||||||
|
seed: u64,
|
||||||
|
rules_default: bool,
|
||||||
|
rules: String,
|
||||||
|
model_default: bool,
|
||||||
|
model: String,
|
||||||
|
layouts: String,
|
||||||
|
variants: String,
|
||||||
|
options: String,
|
||||||
|
backup: Option<Rc<KbvmMap>>,
|
||||||
|
pointer_revert_key: Keysym,
|
||||||
|
pointer_revert_key_str: Option<String>,
|
||||||
|
unknown_pointer_revert_key: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for KeymapState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
seed: random(),
|
||||||
|
rules_default: true,
|
||||||
|
rules: Default::default(),
|
||||||
|
model_default: true,
|
||||||
|
model: Default::default(),
|
||||||
|
layouts: Default::default(),
|
||||||
|
variants: Default::default(),
|
||||||
|
options: Default::default(),
|
||||||
|
backup: Default::default(),
|
||||||
|
pointer_revert_key: Default::default(),
|
||||||
|
pointer_revert_key_str: None,
|
||||||
|
unknown_pointer_revert_key: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_input_pane(self: &Rc<Self>) -> InputPane {
|
||||||
|
InputPane {
|
||||||
|
state: self.state.clone(),
|
||||||
|
paste_requested: Default::default(),
|
||||||
|
keymaps: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Input");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, ps: &mut PaneState, ui: &mut Ui) {
|
||||||
|
let state = self.state.clone();
|
||||||
|
let seats = state.globals.seats.lock();
|
||||||
|
let mut seats: Vec<_> = seats.values().collect();
|
||||||
|
seats.sort_by_key(|d| d.seat_name());
|
||||||
|
for seat in &seats {
|
||||||
|
self.show_seat(ps, ui, seat);
|
||||||
|
}
|
||||||
|
let outputs = state.globals.outputs.lock();
|
||||||
|
let mut outputs: Vec<_> = outputs.values().collect();
|
||||||
|
outputs.sort_by_key(|o| &o.connector.name);
|
||||||
|
let dev = &*state.input_device_handlers.borrow();
|
||||||
|
let mut dev: Vec<_> = dev.values().collect();
|
||||||
|
dev.sort_by_key(|d| d.data.device.name());
|
||||||
|
for dev in dev {
|
||||||
|
self.show_device(ps, ui, &dev.data, &seats, &outputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_seat(&mut self, ps: &mut PaneState, ui: &mut Ui, seat: &Rc<WlSeatGlobal>) {
|
||||||
|
let mut layout_job = LayoutJob::default();
|
||||||
|
layout_job.append(
|
||||||
|
"Seat",
|
||||||
|
0.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.inactive.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
layout_job.append(
|
||||||
|
seat.seat_name(),
|
||||||
|
10.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.active.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let ks = self.keymaps.entry(Key::Seat(seat.id())).or_default();
|
||||||
|
CollapsingHeader::new(layout_job)
|
||||||
|
.id_salt(("seat", seat.id()))
|
||||||
|
.show(ui, |ui| {
|
||||||
|
grid(ui, ("seat", seat.id()), |ui| {
|
||||||
|
let mut dv = |name: &str, get: &dyn Fn(&mut (i32, i32)) -> &mut i32| {
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label(ui, name);
|
||||||
|
let mut v = seat.get_rate();
|
||||||
|
let old = v;
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let v = get(&mut v);
|
||||||
|
DragValue::new(v).range(0..=i32::MAX).ui(ui);
|
||||||
|
if ui.button("-20").clicked() {
|
||||||
|
*v = v.saturating_sub(20).max(0)
|
||||||
|
}
|
||||||
|
if ui.button("+20").clicked() {
|
||||||
|
*v = v.saturating_add(20);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if v != old {
|
||||||
|
seat.set_rate(v.0, v.1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dv("Repeat Rate", &|v| &mut v.0);
|
||||||
|
dv("Repeat Delay", &|v| &mut v.1);
|
||||||
|
drag_value(
|
||||||
|
ui,
|
||||||
|
"Cursor Size",
|
||||||
|
seat.cursor_group().cursor_size(),
|
||||||
|
0..=u32::MAX,
|
||||||
|
1.0,
|
||||||
|
|v| seat.cursor_group().set_cursor_size(v),
|
||||||
|
);
|
||||||
|
bool_ui(
|
||||||
|
ui,
|
||||||
|
"Simple IM",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label("A simple input method based on Xcompose files.");
|
||||||
|
ui.label(concat!(
|
||||||
|
"If you're not using another input method, you should ",
|
||||||
|
"leave this enabled as it will work for sandboxed ",
|
||||||
|
"applications, which regular Xcompose will not.",
|
||||||
|
));
|
||||||
|
ui.label(concat!(
|
||||||
|
"The `enable-unicode-input` action can be used to input ",
|
||||||
|
"characters by their unicode value.",
|
||||||
|
));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
seat.simple_im_enabled(),
|
||||||
|
|b| seat.set_simple_im_enabled(b),
|
||||||
|
);
|
||||||
|
bool_ui(
|
||||||
|
ui,
|
||||||
|
"Hardware Cursor",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label(
|
||||||
|
"Allow this seat to use the hardware cursor, if available.",
|
||||||
|
);
|
||||||
|
ui.label("Only one seat can use the hardware cursor at a time.");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
seat.cursor_group().hardware_cursor(),
|
||||||
|
|b| seat.cursor_group().set_hardware_cursor(b),
|
||||||
|
);
|
||||||
|
{
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
let v = seat.pointer_revert_key();
|
||||||
|
let v = Keysym(v.0);
|
||||||
|
if mem::replace(&mut ks.pointer_revert_key, v) != v {
|
||||||
|
ks.pointer_revert_key_str = None;
|
||||||
|
}
|
||||||
|
let name = ks
|
||||||
|
.pointer_revert_key_str
|
||||||
|
.get_or_insert_with(|| v.name().unwrap_or_default().to_string());
|
||||||
|
grid_label_ui(ui, |ui| {
|
||||||
|
ui.label("Pointer Revert Key");
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label(concat!(
|
||||||
|
"Pressing this key reverts the pointer to the default state, ",
|
||||||
|
"breaking grabs, drags, etc.",
|
||||||
|
));
|
||||||
|
ui.label(
|
||||||
|
"Setting this to `NoSymbol` effectively disables this feature.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.text_edit_singleline(name).changed() {
|
||||||
|
let v = Keysym::from_str(name);
|
||||||
|
ks.unknown_pointer_revert_key = v.is_none();
|
||||||
|
if let Some(v) = v {
|
||||||
|
ks.pointer_revert_key = v;
|
||||||
|
seat.set_pointer_revert_key(KeySym(v.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ks.unknown_pointer_revert_key {
|
||||||
|
ui.label("Error: Unknown key");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
bool(ui, "Focus Follows Mouse", seat.focus_follows_mouse(), |v| {
|
||||||
|
seat.set_focus_follows_mouse(v);
|
||||||
|
});
|
||||||
|
combo_box_ui(
|
||||||
|
ui,
|
||||||
|
"Fallback Output Mode",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label(concat!(
|
||||||
|
"This determines the output to use in operations where no ",
|
||||||
|
"output is explicitly specified.",
|
||||||
|
));
|
||||||
|
ui.label(concat!(
|
||||||
|
"For example, when a new window is opened, this determines ",
|
||||||
|
"where the window will be opened.",
|
||||||
|
));
|
||||||
|
ui.label("`Cursor` refers to the output that contains the cursor.");
|
||||||
|
ui.label(
|
||||||
|
"`Focus` refers to the output that has the keyboard focus.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
seat.fallback_output_mode(),
|
||||||
|
|v| seat.set_fallback_output_mode(v),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
ui.label("Focus History");
|
||||||
|
ui.indent("focus-history", |ui| {
|
||||||
|
let mut v = seat.focus_history_visible();
|
||||||
|
if ui.checkbox(&mut v, "Only Visible").changed() {
|
||||||
|
seat.focus_history_set_visible(v);
|
||||||
|
}
|
||||||
|
let mut v = seat.focus_history_same_workspace();
|
||||||
|
if ui.checkbox(&mut v, "Same Workspace").changed() {
|
||||||
|
seat.focus_history_set_same_workspace(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if ui.button("Reload Simple IM").clicked() {
|
||||||
|
seat.reload_simple_im();
|
||||||
|
}
|
||||||
|
show_keymap(
|
||||||
|
&self.state,
|
||||||
|
ps,
|
||||||
|
&mut self.paste_requested,
|
||||||
|
ks,
|
||||||
|
ui,
|
||||||
|
Some(&seat.keymap()),
|
||||||
|
|m| seat.set_seat_keymap(m),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_device(
|
||||||
|
&mut self,
|
||||||
|
ps: &mut PaneState,
|
||||||
|
ui: &mut Ui,
|
||||||
|
dev: &Rc<DeviceHandlerData>,
|
||||||
|
seats: &[&Rc<WlSeatGlobal>],
|
||||||
|
outputs: &[&Rc<WlOutputGlobal>],
|
||||||
|
) {
|
||||||
|
let mut layout_job = LayoutJob::default();
|
||||||
|
layout_job.append(
|
||||||
|
"Device",
|
||||||
|
0.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.inactive.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
layout_job.append(
|
||||||
|
&dev.device.name(),
|
||||||
|
10.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.active.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let dev_id = dev.device.id();
|
||||||
|
CollapsingHeader::new(layout_job)
|
||||||
|
.id_salt(("device", dev_id))
|
||||||
|
.show(ui, |ui| {
|
||||||
|
grid(ui, ("device", dev_id), |ui| {
|
||||||
|
{
|
||||||
|
let old = dev.seat.get();
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label(ui, "Seat");
|
||||||
|
let mut seat = old.as_ref();
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let mut cb = ComboBox::from_id_salt("seat");
|
||||||
|
if let Some(seat) = seat {
|
||||||
|
cb = cb.selected_text(seat.seat_name());
|
||||||
|
}
|
||||||
|
cb.show_ui(ui, |ui| {
|
||||||
|
for s in seats {
|
||||||
|
ui.selectable_value(&mut seat, Some(s), s.seat_name());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if ui.button("Detach").clicked() {
|
||||||
|
seat = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if seat != old.as_ref() {
|
||||||
|
dev.set_seat(&self.state, seat.cloned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
macro_rules! string {
|
||||||
|
($field:ident, $name:expr) => {
|
||||||
|
if let Some(v) = &dev.$field {
|
||||||
|
label(ui, $name, v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
string!(syspath, "Syspath");
|
||||||
|
string!(devnode, "Devnode");
|
||||||
|
{
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label(ui, "Capabilities");
|
||||||
|
let mut s = String::new();
|
||||||
|
for cap in InputDeviceCapability::variants() {
|
||||||
|
if dev.device.has_capability(cap) {
|
||||||
|
if s.is_not_empty() {
|
||||||
|
s.push_str(" | ");
|
||||||
|
}
|
||||||
|
s.push_str(cap.text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.label(s);
|
||||||
|
}
|
||||||
|
if let Some(old) = dev.device.natural_scrolling_enabled() {
|
||||||
|
bool(ui, "Natural Scrolling", old, |v| {
|
||||||
|
dev.set_natural_scrolling_enabled(&self.state, v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if dev.device.has_capability(InputDeviceCapability::Pointer) {
|
||||||
|
drag_value_ui(
|
||||||
|
ui,
|
||||||
|
"Scroll Distance (px)",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label(concat!(
|
||||||
|
"This only applies to applications that use the ",
|
||||||
|
"legacy px scrolling events.",
|
||||||
|
));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
dev.px_per_scroll_wheel.get(),
|
||||||
|
-f64::INFINITY..=f64::INFINITY,
|
||||||
|
0.1,
|
||||||
|
|v| dev.set_px_per_scroll_wheel(&self.state, v),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(old) = dev.device.accel_profile() {
|
||||||
|
combo_box(ui, "Accel Profile", old, |v| {
|
||||||
|
dev.set_accel_profile(&self.state, v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(old) = dev.device.accel_speed() {
|
||||||
|
drag_value(ui, "Accel Speed", old, 0.0..=1.0, 0.01, |v| {
|
||||||
|
dev.set_accel_speed(&self.state, v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(old) = dev.device.click_method() {
|
||||||
|
combo_box(ui, "Click Method", old, |v| {
|
||||||
|
dev.set_click_method(&self.state, v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(old) = dev.device.tap_enabled() {
|
||||||
|
bool(ui, "Tap Enabled", old, |v| {
|
||||||
|
dev.set_tap_enabled(&self.state, v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(old) = dev.device.drag_enabled() {
|
||||||
|
bool(ui, "Tap Drag Enabled", old, |v| {
|
||||||
|
dev.set_drag_enabled(&self.state, v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(old) = dev.device.drag_lock_enabled() {
|
||||||
|
bool(ui, "Tap Drag Lock Enabled", old, |v| {
|
||||||
|
dev.set_drag_lock_enabled(&self.state, v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(old) = dev.device.left_handed() {
|
||||||
|
bool(ui, "Left Handed", old, |v| {
|
||||||
|
dev.set_left_handed(&self.state, v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(old) = dev.device.middle_button_emulation_enabled() {
|
||||||
|
bool(ui, "Middle Button Emulation", old, |v| {
|
||||||
|
dev.set_middle_button_emulation_enabled(&self.state, v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label_ui(ui, |ui| {
|
||||||
|
ui.label("Output");
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label("This applies to touch and tablet input.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let old = dev.output.get().and_then(|v| v.global.get());
|
||||||
|
let old = old.as_ref();
|
||||||
|
let mut v = old;
|
||||||
|
let mut cb = ComboBox::from_id_salt("output");
|
||||||
|
if let Some(v) = v {
|
||||||
|
cb = cb.selected_text(&*v.connector.name);
|
||||||
|
}
|
||||||
|
cb.show_ui(ui, |ui| {
|
||||||
|
for &output in outputs {
|
||||||
|
ui.selectable_value(
|
||||||
|
&mut v,
|
||||||
|
Some(output),
|
||||||
|
&*output.connector.name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if v != old {
|
||||||
|
dev.set_output(&self.state, v.map(|v| &**v));
|
||||||
|
}
|
||||||
|
if ui.button("Detach").clicked() {
|
||||||
|
dev.set_output(&self.state, None);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
matrix_ui(
|
||||||
|
ui,
|
||||||
|
"Transform Matrix",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label("This matrix is applied to relative pointer movements.");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
dev.device
|
||||||
|
.has_capability(InputDeviceCapability::Pointer)
|
||||||
|
.then(|| {
|
||||||
|
dev.device
|
||||||
|
.transform_matrix()
|
||||||
|
.unwrap_or([[1.0, 0.0], [0.0, 1.0]])
|
||||||
|
}),
|
||||||
|
|v| dev.set_transform_matrix(&self.state, v),
|
||||||
|
);
|
||||||
|
matrix(
|
||||||
|
ui,
|
||||||
|
"Calibration Matrix",
|
||||||
|
dev.device.calibration_matrix(),
|
||||||
|
|v| dev.set_calibration_matrix(&self.state, v),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if dev.device.has_capability(InputDeviceCapability::Keyboard) {
|
||||||
|
ui.collapsing("Device Keymap", |ui| {
|
||||||
|
let ks = self.keymaps.entry(Key::Dev(dev_id)).or_default();
|
||||||
|
let map = dev.keymap.get();
|
||||||
|
ui.add_enabled_ui(map.is_some(), |ui| {
|
||||||
|
if ui.button("Use Seat Keymap").clicked() {
|
||||||
|
ks.backup(map.as_ref());
|
||||||
|
dev.set_keymap(&self.state, None);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
show_keymap(
|
||||||
|
&self.state,
|
||||||
|
ps,
|
||||||
|
&mut self.paste_requested,
|
||||||
|
ks,
|
||||||
|
ui,
|
||||||
|
map.as_ref(),
|
||||||
|
|m| {
|
||||||
|
dev.set_keymap(&self.state, Some(m.clone()));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeymapState {
|
||||||
|
fn backup(&mut self, map: Option<&Rc<KbvmMap>>) {
|
||||||
|
if self.backup.is_none()
|
||||||
|
&& let Some(map) = map
|
||||||
|
{
|
||||||
|
self.backup = Some(map.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_keymap(
|
||||||
|
state: &State,
|
||||||
|
ps: &mut PaneState,
|
||||||
|
paste_requested: &mut Option<Id>,
|
||||||
|
ks: &mut KeymapState,
|
||||||
|
ui: &mut Ui,
|
||||||
|
map: Option<&Rc<KbvmMap>>,
|
||||||
|
set_map: impl Fn(&Rc<KbvmMap>),
|
||||||
|
) {
|
||||||
|
ui.scope_builder(UiBuilder::new().id(("keymap-settings", ks.seed)), |ui| {
|
||||||
|
ui.add_enabled_ui(map.is_some(), |ui| {
|
||||||
|
if ui.button("Copy Keymap").clicked()
|
||||||
|
&& let Some(map) = map
|
||||||
|
{
|
||||||
|
ui.ctx().copy_text(map.map_text.clone());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let backup = |ks: &mut KeymapState| {
|
||||||
|
ks.backup(map);
|
||||||
|
};
|
||||||
|
if ui.button("Load Default Keymap").clicked() {
|
||||||
|
backup(ks);
|
||||||
|
set_map(&state.default_keymap);
|
||||||
|
}
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.add_enabled_ui(map.is_some(), |ui| {
|
||||||
|
if ui.button("Backup Keymap").clicked() {
|
||||||
|
ks.backup = None;
|
||||||
|
backup(ks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some(backup) = &ks.backup
|
||||||
|
&& ui.button("Restore Keymap").clicked()
|
||||||
|
{
|
||||||
|
set_map(backup);
|
||||||
|
ks.backup = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut label = "Load Keymap from Clipboard".to_string();
|
||||||
|
if *paste_requested == Some(ui.id()) {
|
||||||
|
label.push_str(" ");
|
||||||
|
label.push_str(ICON_PENDING);
|
||||||
|
}
|
||||||
|
let button = ui.button(label);
|
||||||
|
if button.clicked() {
|
||||||
|
*paste_requested = Some(ui.id());
|
||||||
|
button.request_focus();
|
||||||
|
ui.ctx().send_viewport_cmd(ViewportCommand::RequestPaste);
|
||||||
|
} else if *paste_requested == Some(ui.id()) && button.has_focus() {
|
||||||
|
ui.input(|e| {
|
||||||
|
let map = e
|
||||||
|
.events
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| match e {
|
||||||
|
Event::Paste(s) => Some(s),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.next();
|
||||||
|
let Some(map) = map else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
*paste_requested = None;
|
||||||
|
let map = match state.kb_ctx.parse_keymap(map.as_bytes()) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => {
|
||||||
|
let error = format!("Could not parse keymap: {}", ErrorFmt(e));
|
||||||
|
ps.errors.push(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
backup(ks);
|
||||||
|
set_map(&map);
|
||||||
|
});
|
||||||
|
} else if *paste_requested == Some(ui.id()) {
|
||||||
|
*paste_requested = None;
|
||||||
|
}
|
||||||
|
ui.collapsing("Create Keymap from Names", |ui| {
|
||||||
|
grid(ui, ("keymap-from-names", ui.id()), |ui| {
|
||||||
|
let defaulted =
|
||||||
|
|ui: &mut Ui, name: &str, default: &mut bool, text: &mut dyn TextBuffer| {
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label(ui, name);
|
||||||
|
ui.add_enabled_ui(!*default, |ui| {
|
||||||
|
text_edit(ui, text);
|
||||||
|
});
|
||||||
|
ui.checkbox(default, "Default");
|
||||||
|
};
|
||||||
|
let required = |ui: &mut Ui, name, text| {
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label(ui, name);
|
||||||
|
text_edit(ui, text);
|
||||||
|
};
|
||||||
|
defaulted(ui, "Rules", &mut ks.rules_default, &mut ks.rules);
|
||||||
|
defaulted(ui, "Model", &mut ks.model_default, &mut ks.model);
|
||||||
|
required(ui, "Layouts", &mut ks.layouts);
|
||||||
|
required(ui, "Variants", &mut ks.variants);
|
||||||
|
required(ui, "Options", &mut ks.options);
|
||||||
|
});
|
||||||
|
if ui.button("Load").clicked() {
|
||||||
|
'set_map: {
|
||||||
|
let map = state.kb_ctx.keymap_from_rmlvo(
|
||||||
|
(!ks.rules_default).then_some(&ks.rules),
|
||||||
|
(!ks.model_default).then_some(&ks.model),
|
||||||
|
Some(&ks.layouts),
|
||||||
|
Some(&ks.variants),
|
||||||
|
Some(&ks.options),
|
||||||
|
);
|
||||||
|
let map = match map {
|
||||||
|
Ok(map) => map,
|
||||||
|
Err(e) => {
|
||||||
|
let error = format!("Could not parse keymap: {}", ErrorFmt(e));
|
||||||
|
ps.errors.push(error);
|
||||||
|
break 'set_map;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
backup(ks);
|
||||||
|
set_map(&map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matrix<T, const W: usize>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
old: Option<[[T; W]; 2]>,
|
||||||
|
set: impl FnOnce([[T; W]; 2]),
|
||||||
|
) where
|
||||||
|
T: Numeric,
|
||||||
|
{
|
||||||
|
matrix_ui(ui, name, |_| (), old, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matrix_ui<R, T, const W: usize>(
|
||||||
|
ui: &mut Ui,
|
||||||
|
name: &str,
|
||||||
|
label: impl FnOnce(&mut Ui) -> R,
|
||||||
|
old: Option<[[T; W]; 2]>,
|
||||||
|
set: impl FnOnce([[T; W]; 2]),
|
||||||
|
) where
|
||||||
|
T: Numeric,
|
||||||
|
{
|
||||||
|
if let Some(mut m) = old {
|
||||||
|
let old = m;
|
||||||
|
let ui = &mut *ui.row();
|
||||||
|
grid_label_ui(ui, |ui| {
|
||||||
|
ui.label(name);
|
||||||
|
label(ui);
|
||||||
|
});
|
||||||
|
Grid::new(name).show(ui, |ui| {
|
||||||
|
for row in &mut m {
|
||||||
|
for cell in row {
|
||||||
|
DragValue::new(cell).speed(0.01).ui(ui);
|
||||||
|
}
|
||||||
|
ui.end_row();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if old != m {
|
||||||
|
set(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
163
src/control_center/cc_look_and_feel.rs
Normal file
163
src/control_center/cc_look_and_feel.rs
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
cmm::cmm_eotf::Eotf,
|
||||||
|
control_center::{
|
||||||
|
ControlCenterInner, bool, bool_ui, combo_box, drag_value, grid, grid_label, row,
|
||||||
|
text_edit, tip,
|
||||||
|
},
|
||||||
|
gfx_api::AlphaMode,
|
||||||
|
state::State,
|
||||||
|
theme::{Color, ThemeColor, ThemeSized},
|
||||||
|
utils::static_text::StaticText,
|
||||||
|
},
|
||||||
|
egui::Ui,
|
||||||
|
isnt::std_1::primitive::IsntStrExt,
|
||||||
|
linearize::LinearizeExt,
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct LookAndFeelPane {
|
||||||
|
state: Rc<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_look_and_feel_pane(self: &Rc<Self>) -> LookAndFeelPane {
|
||||||
|
LookAndFeelPane {
|
||||||
|
state: self.state.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LookAndFeelPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Look and Feel");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, ui: &mut Ui) {
|
||||||
|
let t = &self.state.theme;
|
||||||
|
grid(ui, "settings", |ui| {
|
||||||
|
bool(ui, "Show Bar", self.state.show_bar.get(), |v| {
|
||||||
|
self.state.set_show_bar(v)
|
||||||
|
});
|
||||||
|
combo_box(ui, "Bar Position", t.bar_position.get(), |p| {
|
||||||
|
self.state.set_bar_position(p);
|
||||||
|
});
|
||||||
|
bool(ui, "Show Titles", t.show_titles.get(), |v| {
|
||||||
|
self.state.set_show_titles(v)
|
||||||
|
});
|
||||||
|
bool_ui(
|
||||||
|
ui,
|
||||||
|
"Primary Selection",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label("Requires applications to be restarted.");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
self.state.enable_primary_selection.get(),
|
||||||
|
|v| self.state.set_primary_selection_enabled(v),
|
||||||
|
);
|
||||||
|
bool_ui(
|
||||||
|
ui,
|
||||||
|
"UI Drag",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label("Allows dragging workspaces and tiled windows with the mouse.");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
self.state.ui_drag_enabled.get(),
|
||||||
|
|v| self.state.set_ui_drag_enabled(v),
|
||||||
|
);
|
||||||
|
drag_value(
|
||||||
|
ui,
|
||||||
|
"UI Drag Threshold (px)",
|
||||||
|
self.state.ui_drag_threshold_squared.get().isqrt(),
|
||||||
|
1..=i32::MAX,
|
||||||
|
1.0,
|
||||||
|
|v| {
|
||||||
|
self.state.set_ui_drag_threshold(v);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
bool_ui(
|
||||||
|
ui,
|
||||||
|
"Float Pin Icon",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label("Show the pin icon even if the window is not pinned.");
|
||||||
|
ui.label("Pinned floating windows are shown on all workspaces.");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
self.state.show_pin_icon.get(),
|
||||||
|
|v| self.state.set_show_pin_icon(v),
|
||||||
|
);
|
||||||
|
bool_ui(
|
||||||
|
ui,
|
||||||
|
"Float Above Fullscreen",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label("Show floating windows above fullscreen windows.");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
self.state.float_above_fullscreen.get(),
|
||||||
|
|v| self.state.set_float_above_fullscreen(v),
|
||||||
|
);
|
||||||
|
row(ui, "Font", |ui| {
|
||||||
|
let mut v = self.state.theme.font.get().to_string();
|
||||||
|
if text_edit(ui, &mut v).changed() {
|
||||||
|
self.state.set_font(&v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
row(ui, "Title Font", |ui| {
|
||||||
|
let mut v = t
|
||||||
|
.title_font
|
||||||
|
.get()
|
||||||
|
.map(|v| v.to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
|
if text_edit(ui, &mut v).changed() {
|
||||||
|
self.state.set_title_font(v.is_not_empty().then_some(&v));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
row(ui, "Bar Font", |ui| {
|
||||||
|
let mut v = t.bar_font.get().map(|v| v.to_string()).unwrap_or_default();
|
||||||
|
if text_edit(ui, &mut v).changed() {
|
||||||
|
self.state.set_bar_font(v.is_not_empty().then_some(&v));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if ui.button("Reset Sizes").clicked() {
|
||||||
|
self.state.reset_sizes();
|
||||||
|
}
|
||||||
|
if ui.button("Reset Colors").clicked() {
|
||||||
|
self.state.reset_colors();
|
||||||
|
}
|
||||||
|
if ui.button("Reset Fonts").clicked() {
|
||||||
|
self.state.reset_fonts();
|
||||||
|
}
|
||||||
|
ui.collapsing("Sizes", |ui| {
|
||||||
|
grid(ui, "Sizes", |ui| {
|
||||||
|
for v in ThemeSized::variants() {
|
||||||
|
let f = v.field(&self.state.theme);
|
||||||
|
drag_value(ui, v.text(), f.get(), v.min()..=v.max(), 1.0, |i| {
|
||||||
|
self.state.set_size(v, i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.collapsing("Colors", |ui| {
|
||||||
|
grid(ui, "Colors", |ui| {
|
||||||
|
for tc in ThemeColor::variants() {
|
||||||
|
let f = tc.field(t);
|
||||||
|
let mut v = f.get().to_array(Eotf::Linear);
|
||||||
|
grid_label(ui, tc.text());
|
||||||
|
let changed = ui.color_edit_button_rgba_premultiplied(&mut v).changed();
|
||||||
|
ui.end_row();
|
||||||
|
if changed {
|
||||||
|
let [r, g, b, a] = v;
|
||||||
|
let c =
|
||||||
|
Color::new(Eotf::Linear, AlphaMode::PremultipliedOptical, r, g, b, a);
|
||||||
|
self.state.set_color(tc, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
1711
src/control_center/cc_outputs.rs
Normal file
1711
src/control_center/cc_outputs.rs
Normal file
File diff suppressed because it is too large
Load diff
92
src/control_center/cc_sidebar.rs
Normal file
92
src/control_center/cc_sidebar.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
use {
|
||||||
|
crate::control_center::{ControlCenterInner, Pane, PaneType},
|
||||||
|
egui::{Align, Layout, ScrollArea, Ui, ViewportCommand},
|
||||||
|
egui_tiles::Tree,
|
||||||
|
linearize::{Linearize, LinearizeExt},
|
||||||
|
std::{rc::Rc, sync::LazyLock},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Linearize)]
|
||||||
|
enum PaneName {
|
||||||
|
Compositor,
|
||||||
|
Idle,
|
||||||
|
ColorManagement,
|
||||||
|
Xwayland,
|
||||||
|
Outputs,
|
||||||
|
GPUs,
|
||||||
|
Input,
|
||||||
|
LookAndFeel,
|
||||||
|
Clients,
|
||||||
|
WindowSearch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaneName {
|
||||||
|
fn name(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
PaneName::Compositor => "Compositor",
|
||||||
|
PaneName::Idle => "Idle",
|
||||||
|
PaneName::ColorManagement => "Color Management",
|
||||||
|
PaneName::Xwayland => "Xwayland",
|
||||||
|
PaneName::Outputs => "Outputs",
|
||||||
|
PaneName::GPUs => "GPUs",
|
||||||
|
PaneName::Input => "Input",
|
||||||
|
PaneName::LookAndFeel => "Look and Feel",
|
||||||
|
PaneName::Clients => "Clients",
|
||||||
|
PaneName::WindowSearch => "Window Search",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static TYPES: LazyLock<Vec<PaneName>> = LazyLock::new(|| {
|
||||||
|
let mut res: Vec<_> = PaneName::variants().collect();
|
||||||
|
res.sort_by_key(|t| t.name());
|
||||||
|
res
|
||||||
|
});
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn show_sidebar(self: &Rc<Self>, tree: &mut Tree<Pane>, ui: &mut Ui) {
|
||||||
|
ui.with_layout(
|
||||||
|
Layout::top_down(Align::Center).with_cross_justify(true),
|
||||||
|
|ui| {
|
||||||
|
ui.add_space(6.0);
|
||||||
|
if ui.button("Close").clicked() {
|
||||||
|
ui.ctx().send_viewport_cmd(ViewportCommand::Close);
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
for &ty in &*TYPES {
|
||||||
|
if ui.button(ty.name()).clicked() {
|
||||||
|
let ty = match ty {
|
||||||
|
PaneName::Compositor => {
|
||||||
|
PaneType::Compositor(self.create_compositor_pane())
|
||||||
|
}
|
||||||
|
PaneName::Idle => PaneType::Idle(self.create_idle_pane()),
|
||||||
|
PaneName::ColorManagement => {
|
||||||
|
PaneType::ColorManagement(self.create_color_management_pane())
|
||||||
|
}
|
||||||
|
PaneName::Xwayland => {
|
||||||
|
PaneType::Xwayland(self.create_xwayland_pane())
|
||||||
|
}
|
||||||
|
PaneName::Outputs => {
|
||||||
|
PaneType::Outputs(Box::new(self.create_outputs_pane()))
|
||||||
|
}
|
||||||
|
PaneName::GPUs => PaneType::GPUs(self.create_gpus_pane()),
|
||||||
|
PaneName::Input => PaneType::Input(self.create_input_pane()),
|
||||||
|
PaneName::LookAndFeel => {
|
||||||
|
PaneType::LookAndFeel(self.create_look_and_feel_pane())
|
||||||
|
}
|
||||||
|
PaneName::Clients => PaneType::Clients(self.create_clients_pane()),
|
||||||
|
PaneName::WindowSearch => {
|
||||||
|
PaneType::WindowSearch(self.create_window_search_pane())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.open(tree, ty);
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.add_space(3.0);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
481
src/control_center/cc_window.rs
Normal file
481
src/control_center/cc_window.rs
Normal file
|
|
@ -0,0 +1,481 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
control_center::{
|
||||||
|
CcBehavior, ControlCenterInner, PaneType,
|
||||||
|
cc_clients::{ClientCrit, show_client_collapsible},
|
||||||
|
cc_criterion::{CcCriterion, CritImpl, CritRegex},
|
||||||
|
grid, icon_label, label, read_only_bool,
|
||||||
|
},
|
||||||
|
criteria::{CritMgrExt, CritUpstreamNode, crit_leaf::CritLeafMatcher},
|
||||||
|
egui_adapter::egui_platform::icons::ICON_OPEN_IN_NEW,
|
||||||
|
state::State,
|
||||||
|
tree::{NodeId, ToplevelData, ToplevelNode, ToplevelType},
|
||||||
|
utils::{
|
||||||
|
copyhashmap::CopyHashMap,
|
||||||
|
event_listener::{EventListener, LazyEventSourceListener},
|
||||||
|
static_text::StaticText,
|
||||||
|
toplevel_identifier::ToplevelIdentifier,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
|
egui::{CollapsingHeader, Sense, TextFormat, Ui, Widget, cache::CacheTrait, text::LayoutJob},
|
||||||
|
isnt::std_1::primitive::IsntStrExt,
|
||||||
|
jay_config::window::{
|
||||||
|
ContentType, GAME_CONTENT, NO_CONTENT_TYPE, PHOTO_CONTENT, VIDEO_CONTENT,
|
||||||
|
},
|
||||||
|
linearize::Linearize,
|
||||||
|
std::{
|
||||||
|
any::Any,
|
||||||
|
mem,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WindowClit {
|
||||||
|
Client(CcCriterion<ClientCrit>),
|
||||||
|
Title(CritRegex),
|
||||||
|
AppId(CritRegex),
|
||||||
|
Floating,
|
||||||
|
Visible,
|
||||||
|
Urgent,
|
||||||
|
Fullscreen,
|
||||||
|
Tag(CritRegex),
|
||||||
|
XClass(CritRegex),
|
||||||
|
XInstance(CritRegex),
|
||||||
|
XRole(CritRegex),
|
||||||
|
Workspace(CritRegex),
|
||||||
|
ContentTypes(ContentType),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Linearize)]
|
||||||
|
enum WindowCritTy {
|
||||||
|
Client,
|
||||||
|
Title,
|
||||||
|
AppId,
|
||||||
|
Floating,
|
||||||
|
Visible,
|
||||||
|
Urgent,
|
||||||
|
Fullscreen,
|
||||||
|
Tag,
|
||||||
|
XClass,
|
||||||
|
XInstance,
|
||||||
|
XRole,
|
||||||
|
Workspace,
|
||||||
|
ContentTypes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WindowClit {
|
||||||
|
fn default() -> Self {
|
||||||
|
WindowClit::Title(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticText for WindowCritTy {
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
WindowCritTy::Client => "Client",
|
||||||
|
WindowCritTy::Title => "Title",
|
||||||
|
WindowCritTy::AppId => "App ID",
|
||||||
|
WindowCritTy::Floating => "Floating",
|
||||||
|
WindowCritTy::Visible => "Visible",
|
||||||
|
WindowCritTy::Urgent => "Urgent",
|
||||||
|
WindowCritTy::Fullscreen => "Fullscreen",
|
||||||
|
WindowCritTy::Tag => "Tag",
|
||||||
|
WindowCritTy::XClass => "X Class",
|
||||||
|
WindowCritTy::XInstance => "X Instance",
|
||||||
|
WindowCritTy::XRole => "X Role",
|
||||||
|
WindowCritTy::Workspace => "Workspace",
|
||||||
|
WindowCritTy::ContentTypes => "Content Types",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CritImpl for WindowClit {
|
||||||
|
type Type = WindowCritTy;
|
||||||
|
type Target = ToplevelData;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type {
|
||||||
|
macro_rules! map {
|
||||||
|
($($n:ident,)*) => {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
Self::$n { .. } => WindowCritTy::$n,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
map! {
|
||||||
|
Client,
|
||||||
|
Title,
|
||||||
|
AppId,
|
||||||
|
Floating,
|
||||||
|
Visible,
|
||||||
|
Urgent,
|
||||||
|
Fullscreen,
|
||||||
|
Tag,
|
||||||
|
XClass,
|
||||||
|
XInstance,
|
||||||
|
XRole,
|
||||||
|
Workspace,
|
||||||
|
ContentTypes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_ty(ty: Self::Type) -> Self {
|
||||||
|
match ty {
|
||||||
|
WindowCritTy::Client => Self::Client(Default::default()),
|
||||||
|
WindowCritTy::Title => Self::Title(Default::default()),
|
||||||
|
WindowCritTy::AppId => Self::AppId(Default::default()),
|
||||||
|
WindowCritTy::Floating => Self::Floating,
|
||||||
|
WindowCritTy::Visible => Self::Visible,
|
||||||
|
WindowCritTy::Urgent => Self::Urgent,
|
||||||
|
WindowCritTy::Fullscreen => Self::Fullscreen,
|
||||||
|
WindowCritTy::Tag => Self::Tag(Default::default()),
|
||||||
|
WindowCritTy::XClass => Self::XClass(Default::default()),
|
||||||
|
WindowCritTy::XInstance => Self::XInstance(Default::default()),
|
||||||
|
WindowCritTy::XRole => Self::XRole(Default::default()),
|
||||||
|
WindowCritTy::Workspace => Self::Workspace(Default::default()),
|
||||||
|
WindowCritTy::ContentTypes => {
|
||||||
|
Self::ContentTypes(PHOTO_CONTENT | VIDEO_CONTENT | GAME_CONTENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self, ui: &mut Ui) -> bool {
|
||||||
|
match self {
|
||||||
|
WindowClit::Client(v) => v.show(ui),
|
||||||
|
WindowClit::Title(v) => v.show(ui),
|
||||||
|
WindowClit::AppId(v) => v.show(ui),
|
||||||
|
WindowClit::Floating => false,
|
||||||
|
WindowClit::Visible => false,
|
||||||
|
WindowClit::Urgent => false,
|
||||||
|
WindowClit::Fullscreen => false,
|
||||||
|
WindowClit::Tag(v) => v.show(ui),
|
||||||
|
WindowClit::XClass(v) => v.show(ui),
|
||||||
|
WindowClit::XInstance(v) => v.show(ui),
|
||||||
|
WindowClit::XRole(v) => v.show(ui),
|
||||||
|
WindowClit::Workspace(v) => v.show(ui),
|
||||||
|
WindowClit::ContentTypes(v) => show_content_types(ui, v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_crit(&self, state: &Rc<State>) -> Option<Rc<dyn CritUpstreamNode<Self::Target>>> {
|
||||||
|
let m = &state.tl_matcher_manager;
|
||||||
|
let res = match self {
|
||||||
|
WindowClit::Client(v) => m.client(state, &v.to_crit(state)?),
|
||||||
|
WindowClit::Title(v) => m.title(v.to_crit()?),
|
||||||
|
WindowClit::AppId(v) => m.app_id(v.to_crit()?),
|
||||||
|
WindowClit::Floating => m.floating(),
|
||||||
|
WindowClit::Visible => m.visible(),
|
||||||
|
WindowClit::Urgent => m.urgent(),
|
||||||
|
WindowClit::Fullscreen => m.fullscreen(),
|
||||||
|
WindowClit::Tag(v) => m.tag(v.to_crit()?),
|
||||||
|
WindowClit::XClass(v) => m.class(v.to_crit()?),
|
||||||
|
WindowClit::XInstance(v) => m.instance(v.to_crit()?),
|
||||||
|
WindowClit::XRole(v) => m.role(v.to_crit()?),
|
||||||
|
WindowClit::Workspace(v) => m.workspace(v.to_crit()?),
|
||||||
|
WindowClit::ContentTypes(v) => m.content_type(*v),
|
||||||
|
};
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(
|
||||||
|
state: &State,
|
||||||
|
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
state.tl_matcher_manager.not(upstream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(
|
||||||
|
state: &State,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
all: bool,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
state.tl_matcher_manager.list(upstream, all)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exactly(
|
||||||
|
state: &State,
|
||||||
|
n: usize,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
state.tl_matcher_manager.exactly(upstream, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WindowSearchPane {
|
||||||
|
state: Rc<State>,
|
||||||
|
criterion: CcCriterion<WindowClit>,
|
||||||
|
matched: Rc<Matched>,
|
||||||
|
leaf: Option<Rc<CritLeafMatcher<ToplevelData>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Matched {
|
||||||
|
slf: Weak<ControlCenterInner>,
|
||||||
|
windows: CopyHashMap<ToplevelIdentifier, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Matched {
|
||||||
|
fn request_frame(&self) {
|
||||||
|
if let Some(slf) = self.slf.upgrade() {
|
||||||
|
slf.window.request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_window_search_pane(self: &Rc<Self>) -> WindowSearchPane {
|
||||||
|
let mut pane = WindowSearchPane {
|
||||||
|
state: self.state.clone(),
|
||||||
|
criterion: Default::default(),
|
||||||
|
matched: Rc::new(Matched {
|
||||||
|
slf: Rc::downgrade(self),
|
||||||
|
windows: Default::default(),
|
||||||
|
}),
|
||||||
|
leaf: Default::default(),
|
||||||
|
};
|
||||||
|
pane.update_matcher();
|
||||||
|
pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowSearchPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Window Search");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
|
||||||
|
let mut clear = false;
|
||||||
|
if self.criterion.show(ui) {
|
||||||
|
clear = self.update_matcher();
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
let mut windows: Vec<_> = self.matched.windows.lock().keys().copied().collect();
|
||||||
|
windows.sort();
|
||||||
|
for id in windows {
|
||||||
|
let Some(window) = self.state.toplevels.get(&id).and_then(|v| v.upgrade()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
show_window_collapsible(behavior, ui, &window);
|
||||||
|
}
|
||||||
|
if clear {
|
||||||
|
self.matched.windows.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_matcher(&mut self) -> bool {
|
||||||
|
let mut clear = false;
|
||||||
|
let state = &self.state;
|
||||||
|
if let Some(new) = self.criterion.to_crit(state) {
|
||||||
|
clear = true;
|
||||||
|
let matched = self.matched.clone();
|
||||||
|
let leaf = state.tl_matcher_manager.leaf(&new, move |data| {
|
||||||
|
matched.windows.set(data, ());
|
||||||
|
matched.request_frame();
|
||||||
|
Box::new({
|
||||||
|
let matched = matched.clone();
|
||||||
|
move || {
|
||||||
|
matched.windows.remove(&data);
|
||||||
|
matched.request_frame();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
state.tl_matcher_manager.rematch_all(state);
|
||||||
|
if self.criterion.any(|c| matches!(c, WindowClit::Client(_))) {
|
||||||
|
state.cl_matcher_manager.rematch_all(state);
|
||||||
|
}
|
||||||
|
self.leaf = Some(leaf);
|
||||||
|
}
|
||||||
|
clear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WindowPane {
|
||||||
|
window: Rc<dyn ToplevelNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_window_pane(self: &Rc<Self>, window: &Rc<dyn ToplevelNode>) -> WindowPane {
|
||||||
|
WindowPane {
|
||||||
|
window: window.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Window");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
|
||||||
|
show_window(behavior, ui, &*self.window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_window_collapsible(
|
||||||
|
behavior: &mut CcBehavior,
|
||||||
|
ui: &mut Ui,
|
||||||
|
window: &Rc<dyn ToplevelNode>,
|
||||||
|
) {
|
||||||
|
let data = window.tl_data();
|
||||||
|
let mut layout_job = LayoutJob::default();
|
||||||
|
layout_job.append(
|
||||||
|
"Window",
|
||||||
|
0.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.inactive.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
layout_job.append(
|
||||||
|
&data.title.borrow(),
|
||||||
|
10.0,
|
||||||
|
TextFormat {
|
||||||
|
color: ui.style().visuals.widgets.active.text_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let closed = CollapsingHeader::new(layout_job)
|
||||||
|
.id_salt(("window", data.identifier.get()))
|
||||||
|
.show(ui, |ui| {
|
||||||
|
if icon_label(ICON_OPEN_IN_NEW)
|
||||||
|
.sense(Sense::CLICK)
|
||||||
|
.ui(ui)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
behavior.open = Some(PaneType::Window(behavior.cc.create_window_pane(window)));
|
||||||
|
}
|
||||||
|
show_window(behavior, ui, &**window)
|
||||||
|
})
|
||||||
|
.fully_closed();
|
||||||
|
if closed {
|
||||||
|
ensure_listener(ui, behavior, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_window(behavior: &mut CcBehavior<'_>, ui: &mut Ui, window: &dyn ToplevelNode) {
|
||||||
|
let data = window.tl_data();
|
||||||
|
ensure_listener(ui, behavior, data);
|
||||||
|
grid(ui, ("window", data.identifier.get()), |ui| {
|
||||||
|
label(ui, "ID", &*data.identifier.get().to_string());
|
||||||
|
label(ui, "Title", &*data.title.borrow());
|
||||||
|
if let Some(w) = data.workspace.get() {
|
||||||
|
label(ui, "Workspace", &w.name);
|
||||||
|
}
|
||||||
|
match &data.kind {
|
||||||
|
ToplevelType::Container => {
|
||||||
|
label(ui, "Type", "Container");
|
||||||
|
}
|
||||||
|
ToplevelType::Placeholder(_) => {
|
||||||
|
label(ui, "Type", "Placeholder");
|
||||||
|
}
|
||||||
|
ToplevelType::XdgToplevel(t) => {
|
||||||
|
label(ui, "Type", "xdg_toplevel");
|
||||||
|
let tag = &*t.tag.borrow();
|
||||||
|
if tag.is_not_empty() {
|
||||||
|
label(ui, "Tag", tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToplevelType::XWindow(t) => {
|
||||||
|
label(ui, "Type", "X Window");
|
||||||
|
if let Some(class) = &*t.info.class.borrow() {
|
||||||
|
label(ui, "Class", class);
|
||||||
|
}
|
||||||
|
if let Some(instance) = &*t.info.instance.borrow() {
|
||||||
|
label(ui, "Instance", instance);
|
||||||
|
}
|
||||||
|
if let Some(role) = &*t.info.role.borrow() {
|
||||||
|
label(ui, "Role", role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let app_id = &*data.app_id.borrow();
|
||||||
|
if app_id.is_not_empty() {
|
||||||
|
label(ui, "App ID", app_id);
|
||||||
|
}
|
||||||
|
read_only_bool(ui, "Floating", data.parent_is_float.get());
|
||||||
|
read_only_bool(ui, "Visible", data.visible.get());
|
||||||
|
read_only_bool(ui, "Urgent", data.wants_attention.get());
|
||||||
|
read_only_bool(ui, "Fullscreen", data.is_fullscreen.get());
|
||||||
|
if let Some(ct) = data.content_type.get() {
|
||||||
|
label(ui, "Content Type", ct.text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some(client) = &data.client {
|
||||||
|
show_client_collapsible(behavior, ui, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_listener(ui: &mut Ui, behavior: &CcBehavior<'_>, data: &ToplevelData) {
|
||||||
|
ui.memory_mut(|m| {
|
||||||
|
m.caches
|
||||||
|
.cache::<WindowPropertyListeners>()
|
||||||
|
.ensure(behavior.cc, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct WindowPropertyListeners {
|
||||||
|
generation: u64,
|
||||||
|
listeners: AHashMap<NodeId, WindowPropertyListener>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WindowPropertyListener {
|
||||||
|
_listener: EventListener<dyn LazyEventSourceListener>,
|
||||||
|
generation: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowPropertyListeners {
|
||||||
|
fn ensure(&mut self, cc: &Rc<ControlCenterInner>, data: &ToplevelData) {
|
||||||
|
let listener = self.listeners.entry(data.node_id).or_insert_with(|| {
|
||||||
|
let listener =
|
||||||
|
EventListener::new(Rc::downgrade(cc) as Weak<dyn LazyEventSourceListener>);
|
||||||
|
listener.attach(data.property_changed_source());
|
||||||
|
WindowPropertyListener {
|
||||||
|
_listener: listener,
|
||||||
|
generation: 0,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
listener.generation = self.generation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Sync for WindowPropertyListeners {}
|
||||||
|
unsafe impl Send for WindowPropertyListeners {}
|
||||||
|
|
||||||
|
impl CacheTrait for WindowPropertyListeners {
|
||||||
|
fn update(&mut self) {
|
||||||
|
self.listeners
|
||||||
|
.retain(|_, m| m.generation == self.generation);
|
||||||
|
self.generation += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.listeners.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_content_types(ui: &mut Ui, ct: &mut ContentType) -> bool {
|
||||||
|
let mut v = *ct;
|
||||||
|
let mut photo = (v & PHOTO_CONTENT).0 != 0;
|
||||||
|
let mut video = (v & VIDEO_CONTENT).0 != 0;
|
||||||
|
let mut game = (v & GAME_CONTENT).0 != 0;
|
||||||
|
ui.checkbox(&mut photo, "Photo");
|
||||||
|
ui.checkbox(&mut video, "Video");
|
||||||
|
ui.checkbox(&mut game, "Game");
|
||||||
|
v = NO_CONTENT_TYPE;
|
||||||
|
if photo {
|
||||||
|
v |= PHOTO_CONTENT;
|
||||||
|
}
|
||||||
|
if video {
|
||||||
|
v |= VIDEO_CONTENT;
|
||||||
|
}
|
||||||
|
if game {
|
||||||
|
v |= GAME_CONTENT;
|
||||||
|
}
|
||||||
|
mem::replace(ct, v) != v
|
||||||
|
}
|
||||||
91
src/control_center/cc_xwayland.rs
Normal file
91
src/control_center/cc_xwayland.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
compositor::DISPLAY,
|
||||||
|
control_center::{
|
||||||
|
CcBehavior, ControlCenterInner, bool, cc_clients::show_client_collapsible,
|
||||||
|
combo_box_ui, grid, label, read_only_bool, tip,
|
||||||
|
},
|
||||||
|
state::State,
|
||||||
|
utils::{errorfmt::ErrorFmt, oserror::OsError, static_text::StaticText},
|
||||||
|
},
|
||||||
|
egui::Ui,
|
||||||
|
linearize::Linearize,
|
||||||
|
std::rc::Rc,
|
||||||
|
uapi::c,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct XwaylandPane {
|
||||||
|
state: Rc<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlCenterInner {
|
||||||
|
pub fn create_xwayland_pane(self: &Rc<Self>) -> XwaylandPane {
|
||||||
|
XwaylandPane {
|
||||||
|
state: self.state.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Linearize)]
|
||||||
|
enum ScalingMode {
|
||||||
|
Default,
|
||||||
|
Downscaled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticText for ScalingMode {
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
ScalingMode::Default => "default",
|
||||||
|
ScalingMode::Downscaled => "downscaled",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XwaylandPane {
|
||||||
|
pub fn title(&self, res: &mut String) {
|
||||||
|
res.push_str("Xwayland");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
|
||||||
|
let s = &self.state;
|
||||||
|
grid(ui, "settings", |ui| {
|
||||||
|
bool(ui, "Enabled", s.xwayland.enabled.get(), |b| {
|
||||||
|
s.set_xwayland_enabled(b)
|
||||||
|
});
|
||||||
|
let mode = match self.state.xwayland.use_wire_scale.get() {
|
||||||
|
true => ScalingMode::Downscaled,
|
||||||
|
false => ScalingMode::Default,
|
||||||
|
};
|
||||||
|
combo_box_ui(
|
||||||
|
ui,
|
||||||
|
"Scaling Mode",
|
||||||
|
|ui| {
|
||||||
|
tip(ui, |ui| {
|
||||||
|
ui.label(r#"`downscaled` is known as "X applications scale themselves""#);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
mode,
|
||||||
|
|v| {
|
||||||
|
self.state
|
||||||
|
.set_xwayland_use_wire_scale(v == ScalingMode::Downscaled);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if let Some(display) = self.state.xwayland.display.get() {
|
||||||
|
label(ui, DISPLAY, &*display);
|
||||||
|
}
|
||||||
|
read_only_bool(ui, "Running", self.state.xwayland.running.get());
|
||||||
|
if let Some(client) = self.state.xwayland.client.get() {
|
||||||
|
label(ui, "PID", client.pid_info.pid.to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some(client) = self.state.xwayland.client.get()
|
||||||
|
&& ui.button("Kill").clicked()
|
||||||
|
&& let Err(e) = uapi::kill(client.pid_info.pid, c::SIGTERM)
|
||||||
|
{
|
||||||
|
log::error!("Could not kill Xwayland: {}", ErrorFmt(OsError::from(e)));
|
||||||
|
}
|
||||||
|
if let Some(client) = self.state.xwayland.client.get() {
|
||||||
|
show_client_collapsible(behavior, ui, &client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -234,7 +234,6 @@ impl ClMatcherManager {
|
||||||
self.root(ClmMatchTag::new(string))
|
self.root(ClmMatchTag::new(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn id(&self, id: ClientId) -> Rc<ClmUpstreamNode> {
|
pub fn id(&self, id: ClientId) -> Rc<ClmUpstreamNode> {
|
||||||
self.root(ClmMatchId(id))
|
self.root(ClmMatchId(id))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::HardwareCursorUpdate,
|
backend::HardwareCursorUpdate,
|
||||||
|
control_center::CCI_INPUT,
|
||||||
cursor::{Cursor, DEFAULT_CURSOR_SIZE, KnownCursor},
|
cursor::{Cursor, DEFAULT_CURSOR_SIZE, KnownCursor},
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::{AcquireSync, ReleaseSync},
|
gfx_api::{AcquireSync, ReleaseSync},
|
||||||
|
|
@ -183,6 +184,7 @@ impl CursorUserGroup {
|
||||||
self.remove_hardware_cursor();
|
self.remove_hardware_cursor();
|
||||||
self.state.cursor_user_group_hardware_cursor.take();
|
self.state.cursor_user_group_hardware_cursor.take();
|
||||||
}
|
}
|
||||||
|
self.state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hardware_cursor(&self) -> bool {
|
pub fn hardware_cursor(&self) -> bool {
|
||||||
|
|
@ -195,10 +197,10 @@ impl CursorUserGroup {
|
||||||
self.state.remove_cursor_size(old);
|
self.state.remove_cursor_size(old);
|
||||||
self.state.add_cursor_size(size);
|
self.state.add_cursor_size(size);
|
||||||
self.reload_known_cursor();
|
self.reload_known_cursor();
|
||||||
|
self.state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn cursor_size(&self) -> u32 {
|
pub fn cursor_size(&self) -> u32 {
|
||||||
self.size.get()
|
self.size.get()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
src/egui_adapter.rs
Normal file
3
src/egui_adapter.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod egui_oklch;
|
||||||
|
pub mod egui_platform;
|
||||||
|
mod egui_vulkan;
|
||||||
36
src/egui_adapter/egui_oklch.rs
Normal file
36
src/egui_adapter/egui_oklch.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
cmm::cmm_eotf::Eotf,
|
||||||
|
theme::{Color, Oklab, Oklch},
|
||||||
|
},
|
||||||
|
egui::{Color32, Rgba},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait Color32Ext {
|
||||||
|
fn to_oklab(self) -> Oklab;
|
||||||
|
fn to_oklch(self) -> Oklch;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color32Ext for Color32 {
|
||||||
|
fn to_oklab(self) -> Oklab {
|
||||||
|
let [r, g, b, a] = self.to_array();
|
||||||
|
Color::from_srgba_premultiplied(r, g, b, a).srgb_to_oklab()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_oklch(self) -> Oklch {
|
||||||
|
self.to_oklab().to_oklch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Color32> for Oklch {
|
||||||
|
fn into(self) -> Color32 {
|
||||||
|
self.to_oklab().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Color32> for Oklab {
|
||||||
|
fn into(self) -> Color32 {
|
||||||
|
let [r, g, b, a] = self.to_srgb().to_array(Eotf::Linear);
|
||||||
|
Rgba::from_rgba_premultiplied(r, g, b, a).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
1445
src/egui_adapter/egui_platform.rs
Normal file
1445
src/egui_adapter/egui_platform.rs
Normal file
File diff suppressed because it is too large
Load diff
2055
src/egui_adapter/egui_vulkan.rs
Normal file
2055
src/egui_adapter/egui_vulkan.rs
Normal file
File diff suppressed because it is too large
Load diff
BIN
src/egui_adapter/icons.ttf
Normal file
BIN
src/egui_adapter/icons.ttf
Normal file
Binary file not shown.
13
src/egui_adapter/shaders/shader.frag
Normal file
13
src/egui_adapter/shaders/shader.frag
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 color;
|
||||||
|
layout(location = 1) in vec2 pos;
|
||||||
|
|
||||||
|
layout(binding = 0, set = 0) uniform sampler2D tex;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 res;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 src = texture(tex, pos);
|
||||||
|
res = color * src;
|
||||||
|
}
|
||||||
19
src/egui_adapter/shaders/shader.vert
Normal file
19
src/egui_adapter/shaders/shader.vert
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 if_pos;
|
||||||
|
layout(location = 1) in vec2 it_pos;
|
||||||
|
layout(location = 2) in vec4 i_color;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_color;
|
||||||
|
layout(location = 1) out vec2 ot_pos;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
o_color = i_color;
|
||||||
|
o_color.rgb = mix(
|
||||||
|
o_color.rgb / vec3(12.92),
|
||||||
|
pow((o_color.rgb + vec3(0.055)) / vec3(1.055), vec3(2.4)),
|
||||||
|
greaterThan(o_color.rgb, vec3(0.04045))
|
||||||
|
);
|
||||||
|
ot_pos = it_pos;
|
||||||
|
gl_Position = vec4(if_pos.x, if_pos.y, 0.0, 1.0);
|
||||||
|
}
|
||||||
BIN
src/egui_adapter/shaders_bin/shader.frag.spv
Normal file
BIN
src/egui_adapter/shaders_bin/shader.frag.spv
Normal file
Binary file not shown.
BIN
src/egui_adapter/shaders_bin/shader.vert.spv
Normal file
BIN
src/egui_adapter/shaders_bin/shader.vert.spv
Normal file
Binary file not shown.
2
src/egui_adapter/shaders_hash.txt
Normal file
2
src/egui_adapter/shaders_hash.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
7eb8fae39ae513bc4f6973c12227aa4aa43734bdf34c90e1b3b69294ad98db87 src/egui_adapter/shaders/shader.frag
|
||||||
|
501f4d0c5c5f10a371659b89f12d87abb03e5b57a31dbae5f3c6ca5726e4db01 src/egui_adapter/shaders/shader.vert
|
||||||
|
|
@ -34,14 +34,12 @@ pub enum FontconfigError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[expect(dead_code)]
|
|
||||||
pub struct Font {
|
pub struct Font {
|
||||||
pub fullname: String,
|
pub fullname: String,
|
||||||
pub file: PathBuf,
|
pub file: PathBuf,
|
||||||
pub index: Option<i32>,
|
pub index: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn match_font(family: &str) -> Result<Font, FontconfigError> {
|
pub fn match_font(family: &str) -> Result<Font, FontconfigError> {
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static CONFIG: *mut FcConfig = FcConfigGetCurrent();
|
static CONFIG: *mut FcConfig = FcConfigGetCurrent();
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,7 @@ pub struct Globals {
|
||||||
removed: CopyHashMap<GlobalName, Rc<dyn Global>>,
|
removed: CopyHashMap<GlobalName, Rc<dyn Global>>,
|
||||||
pub outputs: CopyHashMap<GlobalName, Rc<WlOutputGlobal>>,
|
pub outputs: CopyHashMap<GlobalName, Rc<WlOutputGlobal>>,
|
||||||
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
|
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
|
||||||
singletons: StaticMap<Singleton, GlobalName>,
|
pub singletons: StaticMap<Singleton, GlobalName>,
|
||||||
exposed: StaticMap<Singleton, Cell<bool>>,
|
exposed: StaticMap<Singleton, Cell<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ pub mod jay_ei_session_builder;
|
||||||
pub mod jay_idle;
|
pub mod jay_idle;
|
||||||
pub mod jay_input;
|
pub mod jay_input;
|
||||||
pub mod jay_log_file;
|
pub mod jay_log_file;
|
||||||
|
pub mod jay_open_control_center_request;
|
||||||
pub mod jay_output;
|
pub mod jay_output;
|
||||||
pub mod jay_pointer;
|
pub mod jay_pointer;
|
||||||
pub mod jay_popup_ext_manager_v1;
|
pub mod jay_popup_ext_manager_v1;
|
||||||
|
|
|
||||||
|
|
@ -105,14 +105,13 @@ pub struct ReadOnlyHeadState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReadOnlyHeadState {
|
impl ReadOnlyHeadState {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn borrow(&self) -> Ref<'_, HeadState> {
|
pub fn borrow(&self) -> Ref<'_, HeadState> {
|
||||||
self.state.borrow()
|
self.state.borrow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeadState {
|
impl HeadState {
|
||||||
fn update_in_compositor_space(&mut self, wl_output: Option<GlobalName>) {
|
pub fn update_in_compositor_space(&mut self, wl_output: Option<GlobalName>) {
|
||||||
self.in_compositor_space = false;
|
self.in_compositor_space = false;
|
||||||
self.wl_output = None;
|
self.wl_output = None;
|
||||||
if !self.connector_enabled {
|
if !self.connector_enabled {
|
||||||
|
|
@ -131,7 +130,7 @@ impl HeadState {
|
||||||
self.wl_output = wl_output;
|
self.wl_output = wl_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_size(&mut self) {
|
pub fn update_size(&mut self) {
|
||||||
self.size =
|
self.size =
|
||||||
OutputNode::calculate_extents_(self.mode, self.transform, self.scale, self.position)
|
OutputNode::calculate_extents_(self.mode, self.transform, self.scale, self.position)
|
||||||
.size();
|
.size();
|
||||||
|
|
@ -213,7 +212,7 @@ pub enum HeadCommonError {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HeadManagers {
|
pub struct HeadManagers {
|
||||||
name: HeadName,
|
pub name: HeadName,
|
||||||
state: Rc<RefCell<HeadState>>,
|
state: Rc<RefCell<HeadState>>,
|
||||||
managers: CopyHashMap<(ClientId, JayHeadManagerSessionV1Id), Rc<Head>>,
|
managers: CopyHashMap<(ClientId, JayHeadManagerSessionV1Id), Rc<Head>>,
|
||||||
}
|
}
|
||||||
|
|
@ -235,7 +234,6 @@ impl HeadManagers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn state(&self) -> ReadOnlyHeadState {
|
pub fn state(&self) -> ReadOnlyHeadState {
|
||||||
ReadOnlyHeadState {
|
ReadOnlyHeadState {
|
||||||
state: self.state.clone(),
|
state: self.state.clone(),
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use {
|
||||||
jay_idle::JayIdle,
|
jay_idle::JayIdle,
|
||||||
jay_input::JayInput,
|
jay_input::JayInput,
|
||||||
jay_log_file::JayLogFile,
|
jay_log_file::JayLogFile,
|
||||||
|
jay_open_control_center_request::JayOpenControlCenterRequest,
|
||||||
jay_output::JayOutput,
|
jay_output::JayOutput,
|
||||||
jay_pointer::JayPointer,
|
jay_pointer::JayPointer,
|
||||||
jay_randr::JayRandr,
|
jay_randr::JayRandr,
|
||||||
|
|
@ -77,7 +78,7 @@ global_base!(JayCompositorGlobal, JayCompositor, JayCompositorError);
|
||||||
|
|
||||||
impl Global for JayCompositorGlobal {
|
impl Global for JayCompositorGlobal {
|
||||||
fn version(&self) -> u32 {
|
fn version(&self) -> u32 {
|
||||||
27
|
28
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_caps(&self) -> ClientCaps {
|
fn required_caps(&self) -> ClientCaps {
|
||||||
|
|
@ -541,6 +542,25 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_control_center(
|
||||||
|
&self,
|
||||||
|
req: OpenControlCenter,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let obj = Rc::new(JayOpenControlCenterRequest {
|
||||||
|
id: req.id,
|
||||||
|
client: self.client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version: self.version,
|
||||||
|
});
|
||||||
|
track!(self.client, obj);
|
||||||
|
self.client.add_client_obj(&obj)?;
|
||||||
|
if let Err(e) = self.client.state.open_control_center() {
|
||||||
|
obj.send_failed(e);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
|
||||||
|
|
@ -68,14 +68,14 @@ impl JayIdleRequestHandler for JayIdle {
|
||||||
fn set_interval(&self, req: SetInterval, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_interval(&self, req: SetInterval, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
let interval = Duration::from_secs(req.interval);
|
let interval = Duration::from_secs(req.interval);
|
||||||
let state = &self.client.state;
|
let state = &self.client.state;
|
||||||
state.idle.set_timeout(interval);
|
state.idle.set_timeout(state, interval);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_grace_period(&self, req: SetGracePeriod, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_grace_period(&self, req: SetGracePeriod, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
let period = Duration::from_secs(req.period);
|
let period = Duration::from_secs(req.period);
|
||||||
let state = &self.client.state;
|
let state = &self.client.state;
|
||||||
state.idle.set_grace_period(period);
|
state.idle.set_grace_period(state, period);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use {
|
||||||
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER, LIBINPUT_CONFIG_CLICK_METHOD_NONE,
|
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER, LIBINPUT_CONFIG_CLICK_METHOD_NONE,
|
||||||
},
|
},
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
state::{DeviceHandlerData, InputDeviceData},
|
state::{DeviceHandlerData, InputDeviceData, State},
|
||||||
utils::errorfmt::ErrorFmt,
|
utils::errorfmt::ErrorFmt,
|
||||||
wire::{JayInputId, jay_input::*},
|
wire::{JayInputId, jay_input::*},
|
||||||
},
|
},
|
||||||
|
|
@ -28,6 +28,7 @@ use {
|
||||||
pub struct JayInput {
|
pub struct JayInput {
|
||||||
pub id: JayInputId,
|
pub id: JayInputId,
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
|
pub state: Rc<State>,
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
@ -41,6 +42,7 @@ impl JayInput {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
|
state: client.state.clone(),
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +311,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive,
|
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive,
|
||||||
_ => return Err(JayInputError::UnknownAccelerationProfile(req.profile)),
|
_ => return Err(JayInputError::UnknownAccelerationProfile(req.profile)),
|
||||||
};
|
};
|
||||||
dev.set_accel_profile(profile);
|
dev.set_accel_profile(&self.state, profile);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -317,7 +319,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
fn set_accel_speed(&self, req: SetAccelSpeed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_accel_speed(&self, req: SetAccelSpeed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_accel_speed(req.speed);
|
dev.set_accel_speed(&self.state, req.speed);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -325,7 +327,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
fn set_tap_enabled(&self, req: SetTapEnabled, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_tap_enabled(&self, req: SetTapEnabled, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_tap_enabled(req.enabled != 0);
|
dev.set_tap_enabled(&self.state, req.enabled != 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +339,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_drag_enabled(req.enabled != 0);
|
dev.set_drag_enabled(&self.state, req.enabled != 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -349,7 +351,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_drag_lock_enabled(req.enabled != 0);
|
dev.set_drag_lock_enabled(&self.state, req.enabled != 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -357,7 +359,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
fn set_left_handed(&self, req: SetLeftHanded, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_left_handed(&self, req: SetLeftHanded, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_left_handed(req.enabled != 0);
|
dev.set_left_handed(&self.state, req.enabled != 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -369,7 +371,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_natural_scrolling_enabled(req.enabled != 0);
|
dev.set_natural_scrolling_enabled(&self.state, req.enabled != 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -381,7 +383,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_px_per_scroll_wheel(req.px);
|
dev.set_px_per_scroll_wheel(&self.state, req.px);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -393,7 +395,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_transform_matrix([[req.m11, req.m12], [req.m21, req.m22]]);
|
dev.set_transform_matrix(&self.state, [[req.m11, req.m12], [req.m21, req.m22]]);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -410,7 +412,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let seat = self.seat(req.seat)?;
|
let seat = self.seat(req.seat)?;
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_seat(Some(seat));
|
dev.set_seat(&self.state, Some(seat));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -418,7 +420,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
fn detach(&self, req: Detach, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn detach(&self, req: Detach, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_seat(None);
|
dev.set_seat(&self.state, None);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -459,7 +461,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
fn set_device_keymap(&self, req: SetDeviceKeymap, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_device_keymap(&self, req: SetDeviceKeymap, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
self.set_keymap_impl(&req.keymap, req.keymap_len, |map| {
|
self.set_keymap_impl(&req.keymap, req.keymap_len, |map| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_keymap(Some(map.clone()));
|
dev.set_keymap(&self.state, Some(map.clone()));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -490,11 +492,11 @@ impl JayInputRequestHandler for JayInput {
|
||||||
.find(|c| c.global.connector.name.to_ascii_lowercase() == namelc)
|
.find(|c| c.global.connector.name.to_ascii_lowercase() == namelc)
|
||||||
.cloned();
|
.cloned();
|
||||||
match c {
|
match c {
|
||||||
Some(c) => dev.set_output(Some(&c.global)),
|
Some(c) => dev.set_output(&self.state, Some(&c.global)),
|
||||||
_ => return Err(JayInputError::OutputNotConnected),
|
_ => return Err(JayInputError::OutputNotConnected),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => dev.set_output(None),
|
_ => dev.set_output(&self.state, None),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
@ -507,7 +509,10 @@ impl JayInputRequestHandler for JayInput {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_calibration_matrix([[req.m00, req.m01, req.m02], [req.m10, req.m11, req.m12]]);
|
dev.set_calibration_matrix(
|
||||||
|
&self.state,
|
||||||
|
[[req.m00, req.m01, req.m02], [req.m10, req.m11, req.m12]],
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -521,7 +526,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER => InputDeviceClickMethod::Clickfinger,
|
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER => InputDeviceClickMethod::Clickfinger,
|
||||||
_ => return Err(JayInputError::UnknownClickMethod(req.method)),
|
_ => return Err(JayInputError::UnknownClickMethod(req.method)),
|
||||||
};
|
};
|
||||||
dev.set_click_method(method);
|
dev.set_click_method(&self.state, method);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -533,7 +538,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_middle_button_emulation_enabled(req.enabled != 0);
|
dev.set_middle_button_emulation_enabled(&self.state, req.enabled != 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -594,7 +599,7 @@ impl JayInputRequestHandler for JayInput {
|
||||||
req.options,
|
req.options,
|
||||||
|map| {
|
|map| {
|
||||||
let dev = self.device(req.id)?;
|
let dev = self.device(req.id)?;
|
||||||
dev.set_keymap(Some(map.clone()));
|
dev.set_keymap(&self.state, Some(map.clone()));
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
53
src/ifs/jay_open_control_center_request.rs
Normal file
53
src/ifs/jay_open_control_center_request.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
utils::errorfmt::ErrorFmt,
|
||||||
|
wire::{JayOpenControlCenterRequestId, jay_open_control_center_request::*},
|
||||||
|
},
|
||||||
|
std::{error::Error, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct JayOpenControlCenterRequest {
|
||||||
|
pub id: JayOpenControlCenterRequestId,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayOpenControlCenterRequest {
|
||||||
|
pub fn send_failed(&self, err: impl Error) {
|
||||||
|
let msg = &ErrorFmt(err).to_string();
|
||||||
|
self.client.event(Failed {
|
||||||
|
self_id: self.id,
|
||||||
|
msg,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayOpenControlCenterRequestRequestHandler for JayOpenControlCenterRequest {
|
||||||
|
type Error = JayOpenControlCenterRequestError;
|
||||||
|
|
||||||
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = JayOpenControlCenterRequest;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for JayOpenControlCenterRequest {}
|
||||||
|
|
||||||
|
simple_add_obj!(JayOpenControlCenterRequest);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum JayOpenControlCenterRequestError {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
}
|
||||||
|
efrom!(JayOpenControlCenterRequestError, ClientError);
|
||||||
|
|
@ -350,7 +350,7 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
let Some(dev) = self.get_device(req.dev) else {
|
let Some(dev) = self.get_device(req.dev) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
dev.set_direct_scanout_enabled(req.enabled != 0);
|
dev.set_direct_scanout_enabled(&self.state, req.enabled != 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -456,7 +456,7 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
let Some(c) = self.get_output_node(req.output) else {
|
let Some(c) = self.get_output_node(req.output) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
c.schedule.set_cursor_hz(req.hz);
|
c.schedule.set_cursor_hz(&self.state, req.hz);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,7 +493,7 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
let Some(dev) = self.get_device(req.dev) else {
|
let Some(dev) = self.get_device(req.dev) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
dev.set_flip_margin(req.margin_ns);
|
dev.set_flip_margin(&self.state, req.margin_ns);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ use {
|
||||||
ButtonState, InputDeviceAccelProfile, InputDeviceClickMethod, Leds, TransformMatrix,
|
ButtonState, InputDeviceAccelProfile, InputDeviceClickMethod, Leds, TransformMatrix,
|
||||||
},
|
},
|
||||||
client::{Client, ClientError, ClientId},
|
client::{Client, ClientError, ClientId},
|
||||||
|
control_center::CCI_INPUT,
|
||||||
cursor_user::{CursorUser, CursorUserGroup, CursorUserOwner},
|
cursor_user::{CursorUser, CursorUserGroup, CursorUserOwner},
|
||||||
ei::ei_ifs::ei_seat::EiSeat,
|
ei::ei_ifs::ei_seat::EiSeat,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
|
|
@ -98,6 +99,7 @@ use {
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
rc_eq::{rc_eq, rc_weak_eq},
|
rc_eq::{rc_eq, rc_weak_eq},
|
||||||
smallmap::SmallMap,
|
smallmap::SmallMap,
|
||||||
|
static_text::StaticText,
|
||||||
},
|
},
|
||||||
wire::{
|
wire::{
|
||||||
ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId, WlSeatId,
|
ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId, WlSeatId,
|
||||||
|
|
@ -138,6 +140,9 @@ const MISSING_CAPABILITY: u32 = 0;
|
||||||
|
|
||||||
pub const BTN_LEFT: u32 = 0x110;
|
pub const BTN_LEFT: u32 = 0x110;
|
||||||
pub const BTN_RIGHT: u32 = 0x111;
|
pub const BTN_RIGHT: u32 = 0x111;
|
||||||
|
pub const BTN_MIDDLE: u32 = 0x112;
|
||||||
|
pub const BTN_SIDE: u32 = 0x113;
|
||||||
|
pub const BTN_EXTRA: u32 = 0x114;
|
||||||
|
|
||||||
pub const SEAT_NAME_SINCE: Version = Version(2);
|
pub const SEAT_NAME_SINCE: Version = Version(2);
|
||||||
|
|
||||||
|
|
@ -273,6 +278,15 @@ pub enum FallbackOutputMode {
|
||||||
Focus,
|
Focus,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StaticText for FallbackOutputMode {
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FallbackOutputMode::Cursor => "Cursor",
|
||||||
|
FallbackOutputMode::Focus => "Focus",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<ConfigFallbackOutputMode> for FallbackOutputMode {
|
impl TryFrom<ConfigFallbackOutputMode> for FallbackOutputMode {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
|
|
@ -765,6 +779,7 @@ impl WlSeatGlobal {
|
||||||
if let Some(grab) = self.input_method_grab.get() {
|
if let Some(grab) = self.input_method_grab.get() {
|
||||||
grab.on_repeat_info();
|
grab.on_repeat_info();
|
||||||
}
|
}
|
||||||
|
self.state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(self: &Rc<Self>) {
|
pub fn close(self: &Rc<Self>) {
|
||||||
|
|
@ -960,18 +975,18 @@ impl WlSeatGlobal {
|
||||||
|
|
||||||
pub fn focus_history_set_visible(&self, visible: bool) {
|
pub fn focus_history_set_visible(&self, visible: bool) {
|
||||||
self.focus_history_visible_only.set(visible);
|
self.focus_history_visible_only.set(visible);
|
||||||
|
self.state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn focus_history_visible(&self) -> bool {
|
pub fn focus_history_visible(&self) -> bool {
|
||||||
self.focus_history_visible_only.get()
|
self.focus_history_visible_only.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus_history_set_same_workspace(&self, same_workspace: bool) {
|
pub fn focus_history_set_same_workspace(&self, same_workspace: bool) {
|
||||||
self.focus_history_same_workspace.set(same_workspace);
|
self.focus_history_same_workspace.set(same_workspace);
|
||||||
|
self.state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn focus_history_same_workspace(&self) -> bool {
|
pub fn focus_history_same_workspace(&self) -> bool {
|
||||||
self.focus_history_same_workspace.get()
|
self.focus_history_same_workspace.get()
|
||||||
}
|
}
|
||||||
|
|
@ -1476,18 +1491,18 @@ impl WlSeatGlobal {
|
||||||
|
|
||||||
pub fn set_focus_follows_mouse(&self, focus_follows_mouse: bool) {
|
pub fn set_focus_follows_mouse(&self, focus_follows_mouse: bool) {
|
||||||
self.focus_follows_mouse.set(focus_follows_mouse);
|
self.focus_follows_mouse.set(focus_follows_mouse);
|
||||||
|
self.state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn focus_follows_mouse(&self) -> bool {
|
pub fn focus_follows_mouse(&self) -> bool {
|
||||||
self.focus_follows_mouse.get()
|
self.focus_follows_mouse.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_fallback_output_mode(&self, fallback_output_mode: FallbackOutputMode) {
|
pub fn set_fallback_output_mode(&self, fallback_output_mode: FallbackOutputMode) {
|
||||||
self.fallback_output_mode.set(fallback_output_mode);
|
self.fallback_output_mode.set(fallback_output_mode);
|
||||||
|
self.state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn fallback_output_mode(&self) -> FallbackOutputMode {
|
pub fn fallback_output_mode(&self) -> FallbackOutputMode {
|
||||||
self.fallback_output_mode.get()
|
self.fallback_output_mode.get()
|
||||||
}
|
}
|
||||||
|
|
@ -1607,9 +1622,9 @@ impl WlSeatGlobal {
|
||||||
|
|
||||||
pub fn set_pointer_revert_key(&self, key: KeySym) {
|
pub fn set_pointer_revert_key(&self, key: KeySym) {
|
||||||
self.revert_key.set(key);
|
self.revert_key.set(key);
|
||||||
|
self.state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn pointer_revert_key(&self) -> KeySym {
|
pub fn pointer_revert_key(&self) -> KeySym {
|
||||||
self.revert_key.get()
|
self.revert_key.get()
|
||||||
}
|
}
|
||||||
|
|
@ -1806,7 +1821,7 @@ pub fn collect_kb_foci(node: Rc<dyn Node>) -> SmallVec<[Rc<WlSeatGlobal>; 3]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceHandlerData {
|
impl DeviceHandlerData {
|
||||||
pub fn set_seat(&self, seat: Option<Rc<WlSeatGlobal>>) {
|
pub fn set_seat(&self, state: &State, seat: Option<Rc<WlSeatGlobal>>) {
|
||||||
if let Some(new) = &seat {
|
if let Some(new) = &seat {
|
||||||
if let Some(old) = self.seat.get()
|
if let Some(old) = self.seat.get()
|
||||||
&& old.id() == new.id()
|
&& old.id() == new.id()
|
||||||
|
|
@ -1845,6 +1860,7 @@ impl DeviceHandlerData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.attach_event_listeners();
|
self.attach_event_listeners();
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_physical_keyboard_state(&self) {
|
fn destroy_physical_keyboard_state(&self) {
|
||||||
|
|
@ -1866,13 +1882,14 @@ impl DeviceHandlerData {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_keymap(&self, keymap: Option<Rc<KbvmMap>>) {
|
pub fn set_keymap(&self, state: &State, keymap: Option<Rc<KbvmMap>>) {
|
||||||
self.destroy_physical_keyboard_state();
|
self.destroy_physical_keyboard_state();
|
||||||
self.keymap.set(keymap);
|
self.keymap.set(keymap);
|
||||||
self.attach_event_listeners();
|
self.attach_event_listeners();
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_output(&self, output: Option<&WlOutputGlobal>) {
|
pub fn set_output(&self, state: &State, output: Option<&WlOutputGlobal>) {
|
||||||
match output {
|
match output {
|
||||||
None => {
|
None => {
|
||||||
log::info!("Removing output mapping of {}", self.device.name());
|
log::info!("Removing output mapping of {}", self.device.name());
|
||||||
|
|
@ -1883,6 +1900,7 @@ impl DeviceHandlerData {
|
||||||
self.output.set(Some(o.opt.clone()));
|
self.output.set(Some(o.opt.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_rect(&self, state: &State) -> Rect {
|
pub fn get_rect(&self, state: &State) -> Rect {
|
||||||
|
|
@ -1894,52 +1912,64 @@ impl DeviceHandlerData {
|
||||||
state.root.extents.get()
|
state.root.extents.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_accel_profile(&self, v: InputDeviceAccelProfile) {
|
pub fn set_accel_profile(&self, state: &State, v: InputDeviceAccelProfile) {
|
||||||
self.device.set_accel_profile(v);
|
self.device.set_accel_profile(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_accel_speed(&self, v: f64) {
|
pub fn set_accel_speed(&self, state: &State, v: f64) {
|
||||||
self.device.set_accel_speed(v);
|
self.device.set_accel_speed(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_tap_enabled(&self, v: bool) {
|
pub fn set_tap_enabled(&self, state: &State, v: bool) {
|
||||||
self.device.set_tap_enabled(v);
|
self.device.set_tap_enabled(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_drag_enabled(&self, v: bool) {
|
pub fn set_drag_enabled(&self, state: &State, v: bool) {
|
||||||
self.device.set_drag_enabled(v);
|
self.device.set_drag_enabled(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_drag_lock_enabled(&self, v: bool) {
|
pub fn set_drag_lock_enabled(&self, state: &State, v: bool) {
|
||||||
self.device.set_drag_lock_enabled(v);
|
self.device.set_drag_lock_enabled(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_left_handed(&self, v: bool) {
|
pub fn set_left_handed(&self, state: &State, v: bool) {
|
||||||
self.device.set_left_handed(v);
|
self.device.set_left_handed(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_natural_scrolling_enabled(&self, v: bool) {
|
pub fn set_natural_scrolling_enabled(&self, state: &State, v: bool) {
|
||||||
self.device.set_natural_scrolling_enabled(v);
|
self.device.set_natural_scrolling_enabled(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_px_per_scroll_wheel(&self, v: f64) {
|
pub fn set_px_per_scroll_wheel(&self, state: &State, v: f64) {
|
||||||
self.px_per_scroll_wheel.set(v);
|
self.px_per_scroll_wheel.set(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_transform_matrix(&self, v: TransformMatrix) {
|
pub fn set_transform_matrix(&self, state: &State, v: TransformMatrix) {
|
||||||
self.device.set_transform_matrix(v);
|
self.device.set_transform_matrix(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_calibration_matrix(&self, v: [[f32; 3]; 2]) {
|
pub fn set_calibration_matrix(&self, state: &State, v: [[f32; 3]; 2]) {
|
||||||
self.device.set_calibration_matrix(v);
|
self.device.set_calibration_matrix(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_click_method(&self, v: InputDeviceClickMethod) {
|
pub fn set_click_method(&self, state: &State, v: InputDeviceClickMethod) {
|
||||||
self.device.set_click_method(v);
|
self.device.set_click_method(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_middle_button_emulation_enabled(&self, v: bool) {
|
pub fn set_middle_button_emulation_enabled(&self, state: &State, v: bool) {
|
||||||
self.device.set_middle_button_emulation_enabled(v);
|
self.device.set_middle_button_emulation_enabled(v);
|
||||||
|
state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::KeyState,
|
backend::KeyState,
|
||||||
|
control_center::CCI_INPUT,
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_seat::{
|
wl_seat::{
|
||||||
WlSeatGlobal,
|
WlSeatGlobal,
|
||||||
|
|
@ -89,6 +90,7 @@ impl WlSeatGlobal {
|
||||||
im.cancel_simple(self);
|
im.cancel_simple(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.state.trigger_cci(CCI_INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simple_im_enabled(&self) -> bool {
|
pub fn simple_im_enabled(&self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,12 @@ impl ZwpIdleInhibitorV1 {
|
||||||
|
|
||||||
pub fn activate(self: &Rc<Self>) {
|
pub fn activate(self: &Rc<Self>) {
|
||||||
let state = &self.client.state;
|
let state = &self.client.state;
|
||||||
state.idle.add_inhibitor(self);
|
state.idle.add_inhibitor(state, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deactivate(&self) {
|
pub fn deactivate(&self) {
|
||||||
let state = &self.client.state;
|
let state = &self.client.state;
|
||||||
state.idle.remove_inhibitor(self);
|
state.idle.remove_inhibitor(state, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ pub struct KbvmMap {
|
||||||
pub id: KbvmMapId,
|
pub id: KbvmMapId,
|
||||||
pub state_machine: StateMachine,
|
pub state_machine: StateMachine,
|
||||||
pub lookup_table: LookupTable,
|
pub lookup_table: LookupTable,
|
||||||
#[expect(dead_code)]
|
|
||||||
pub map_text: String,
|
pub map_text: String,
|
||||||
pub map: KeymapFd,
|
pub map: KeymapFd,
|
||||||
pub xwayland_map: KeymapFd,
|
pub xwayland_map: KeymapFd,
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,6 @@ impl Logger {
|
||||||
log::set_max_level(filter);
|
log::set_max_level(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn level(&self) -> LogLevel {
|
pub fn level(&self) -> LogLevel {
|
||||||
self.level.load(Relaxed)
|
self.level.load(Relaxed)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ mod clientmem;
|
||||||
mod cmm;
|
mod cmm;
|
||||||
mod compositor;
|
mod compositor;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod control_center;
|
||||||
mod copy_device;
|
mod copy_device;
|
||||||
mod cpu_worker;
|
mod cpu_worker;
|
||||||
mod criteria;
|
mod criteria;
|
||||||
|
|
@ -67,6 +68,7 @@ mod damage;
|
||||||
mod dbus;
|
mod dbus;
|
||||||
mod drm_feedback;
|
mod drm_feedback;
|
||||||
mod edid;
|
mod edid;
|
||||||
|
mod egui_adapter;
|
||||||
mod ei;
|
mod ei;
|
||||||
mod eventfd_cache;
|
mod eventfd_cache;
|
||||||
mod fixed;
|
mod fixed;
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::AsyncEngine,
|
async_engine::AsyncEngine,
|
||||||
backend::HardwareCursor,
|
backend::HardwareCursor,
|
||||||
|
control_center::CCI_OUTPUTS,
|
||||||
ifs::wl_output::PersistentOutputState,
|
ifs::wl_output::PersistentOutputState,
|
||||||
io_uring::{IoUring, IoUringError},
|
io_uring::{IoUring, IoUringError},
|
||||||
state::ConnectorData,
|
state::{ConnectorData, State},
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, cell_ext::CellExt, clonecell::CloneCell, errorfmt::ErrorFmt,
|
asyncevent::AsyncEvent, cell_ext::CellExt, clonecell::CloneCell, errorfmt::ErrorFmt,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
|
|
@ -51,8 +52,7 @@ pub struct OutputSchedule {
|
||||||
|
|
||||||
impl OutputSchedule {
|
impl OutputSchedule {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
ring: &Rc<IoUring>,
|
state: &State,
|
||||||
eng: &Rc<AsyncEngine>,
|
|
||||||
connector: &Rc<ConnectorData>,
|
connector: &Rc<ConnectorData>,
|
||||||
persistent: &Rc<PersistentOutputState>,
|
persistent: &Rc<PersistentOutputState>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
@ -60,8 +60,8 @@ impl OutputSchedule {
|
||||||
changed: Default::default(),
|
changed: Default::default(),
|
||||||
run: Default::default(),
|
run: Default::default(),
|
||||||
connector: connector.clone(),
|
connector: connector.clone(),
|
||||||
ring: ring.clone(),
|
ring: state.ring.clone(),
|
||||||
eng: eng.clone(),
|
eng: state.eng.clone(),
|
||||||
vrr_enabled: Default::default(),
|
vrr_enabled: Default::default(),
|
||||||
hardware_cursor_change: Cell::new(Change::None),
|
hardware_cursor_change: Cell::new(Change::None),
|
||||||
software_cursor_change: Cell::new(Change::None),
|
software_cursor_change: Cell::new(Change::None),
|
||||||
|
|
@ -72,7 +72,7 @@ impl OutputSchedule {
|
||||||
iteration: Default::default(),
|
iteration: Default::default(),
|
||||||
};
|
};
|
||||||
if let Some(hz) = persistent.vrr_cursor_hz.get() {
|
if let Some(hz) = persistent.vrr_cursor_hz.get() {
|
||||||
slf.set_cursor_hz(hz);
|
slf.set_cursor_hz(state, hz);
|
||||||
}
|
}
|
||||||
slf
|
slf
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +118,7 @@ impl OutputSchedule {
|
||||||
self.trigger();
|
self.trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor_hz(&self, hz: f64) {
|
pub fn set_cursor_hz(&self, state: &State, hz: f64) {
|
||||||
let (hz, delta) = match map_cursor_hz(hz) {
|
let (hz, delta) = match map_cursor_hz(hz) {
|
||||||
None => {
|
None => {
|
||||||
log::warn!("Ignoring cursor frequency {hz}");
|
log::warn!("Ignoring cursor frequency {hz}");
|
||||||
|
|
@ -128,6 +128,7 @@ impl OutputSchedule {
|
||||||
};
|
};
|
||||||
self.persistent.vrr_cursor_hz.set(hz);
|
self.persistent.vrr_cursor_hz.set(hz);
|
||||||
self.connector.head_managers.handle_cursor_hz_change(hz);
|
self.connector.head_managers.handle_cursor_hz_change(hz);
|
||||||
|
state.trigger_cci(CCI_OUTPUTS);
|
||||||
self.cursor_delta_nsec.set(delta);
|
self.cursor_delta_nsec.set(delta);
|
||||||
self.trigger();
|
self.trigger();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,15 @@ pub struct AcceptorMetadata {
|
||||||
pub tag: Option<String>,
|
pub tag: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AcceptorMetadata {
|
||||||
|
pub fn secure() -> Self {
|
||||||
|
Self {
|
||||||
|
secure: true,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SecurityContextAcceptors {
|
impl SecurityContextAcceptors {
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
for acceptor in self.acceptors.lock().drain_values() {
|
for acceptor in self.acceptors.lock().drain_values() {
|
||||||
|
|
|
||||||
95
src/state.rs
95
src/state.rs
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
acceptor::Acceptor,
|
acceptor::Acceptor,
|
||||||
|
allocator::BufferObject,
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
backend::{
|
backend::{
|
||||||
Backend, BackendConnectorState, BackendConnectorStateSerials, BackendDrmDevice,
|
Backend, BackendConnectorState, BackendConnectorStateSerials, BackendDrmDevice,
|
||||||
|
|
@ -15,6 +16,10 @@ use {
|
||||||
cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager},
|
cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager},
|
||||||
compositor::{LIBEI_SOCKET, LogLevel},
|
compositor::{LIBEI_SOCKET, LogLevel},
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
|
control_center::{
|
||||||
|
CCI_COLOR_MANAGEMENT, CCI_COMPOSITOR, CCI_GPUS, CCI_IDLE, CCI_LOOK_AND_FEEL,
|
||||||
|
CCI_OUTPUTS, CCI_XWAYLAND, ControlCenters,
|
||||||
|
},
|
||||||
copy_device::CopyDeviceRegistry,
|
copy_device::CopyDeviceRegistry,
|
||||||
cpu_worker::CpuWorker,
|
cpu_worker::CpuWorker,
|
||||||
criteria::{clm::ClMatcherManager, tlm::TlMatcherManager},
|
criteria::{clm::ClMatcherManager, tlm::TlMatcherManager},
|
||||||
|
|
@ -23,6 +28,7 @@ use {
|
||||||
damage::DamageVisualizer,
|
damage::DamageVisualizer,
|
||||||
dbus::Dbus,
|
dbus::Dbus,
|
||||||
drm_feedback::{DrmFeedback, DrmFeedbackIds},
|
drm_feedback::{DrmFeedback, DrmFeedbackIds},
|
||||||
|
egui_adapter::egui_platform::EggState,
|
||||||
ei::{
|
ei::{
|
||||||
ei_acceptor::EiAcceptor,
|
ei_acceptor::EiAcceptor,
|
||||||
ei_client::{EiClient, EiClients},
|
ei_client::{EiClient, EiClients},
|
||||||
|
|
@ -115,6 +121,7 @@ use {
|
||||||
hash_map_ext::HashMapExt,
|
hash_map_ext::HashMapExt,
|
||||||
linkedlist::LinkedList,
|
linkedlist::LinkedList,
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
|
object_drop_queue::ObjectDropQueue,
|
||||||
queue::AsyncQueue,
|
queue::AsyncQueue,
|
||||||
refcounted::RefCounted,
|
refcounted::RefCounted,
|
||||||
run_toplevel::RunToplevel,
|
run_toplevel::RunToplevel,
|
||||||
|
|
@ -224,7 +231,7 @@ pub struct State {
|
||||||
pub activation_tokens: CopyHashMap<ActivationToken, ()>,
|
pub activation_tokens: CopyHashMap<ActivationToken, ()>,
|
||||||
pub toplevel_lists:
|
pub toplevel_lists:
|
||||||
CopyHashMap<(ClientId, ExtForeignToplevelListV1Id), Rc<ExtForeignToplevelListV1>>,
|
CopyHashMap<(ClientId, ExtForeignToplevelListV1Id), Rc<ExtForeignToplevelListV1>>,
|
||||||
pub dma_buf_ids: DmaBufIds,
|
pub dma_buf_ids: Rc<DmaBufIds>,
|
||||||
pub drm_feedback_ids: DrmFeedbackIds,
|
pub drm_feedback_ids: DrmFeedbackIds,
|
||||||
pub direct_scanout_enabled: Cell<bool>,
|
pub direct_scanout_enabled: Cell<bool>,
|
||||||
pub persistent_output_states: CopyHashMap<Rc<OutputId>, Rc<PersistentOutputState>>,
|
pub persistent_output_states: CopyHashMap<Rc<OutputId>, Rc<PersistentOutputState>>,
|
||||||
|
|
@ -292,6 +299,9 @@ pub struct State {
|
||||||
pub supports_presentation_feedback: Cell<bool>,
|
pub supports_presentation_feedback: Cell<bool>,
|
||||||
pub eventfd_cache: Rc<EventfdCache>,
|
pub eventfd_cache: Rc<EventfdCache>,
|
||||||
pub lazy_event_sources: Rc<LazyEventSources>,
|
pub lazy_event_sources: Rc<LazyEventSources>,
|
||||||
|
pub bo_drop_queue: Rc<ObjectDropQueue<Rc<dyn BufferObject>>>,
|
||||||
|
pub egg_state: EggState,
|
||||||
|
pub control_centers: ControlCenters,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -340,37 +350,39 @@ pub struct IdleState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdleState {
|
impl IdleState {
|
||||||
pub fn set_timeout(&self, timeout: Duration) {
|
pub fn set_timeout(&self, state: &State, timeout: Duration) {
|
||||||
self.timeout.set(timeout);
|
self.timeout.set(timeout);
|
||||||
self.timeout_changed();
|
self.timeout_changed(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_grace_period(&self, grace_period: Duration) {
|
pub fn set_grace_period(&self, state: &State, grace_period: Duration) {
|
||||||
self.grace_period.set(grace_period);
|
self.grace_period.set(grace_period);
|
||||||
self.timeout_changed();
|
self.timeout_changed(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn timeout_changed(&self) {
|
fn timeout_changed(&self, state: &State) {
|
||||||
self.timeout_changed.set(true);
|
self.timeout_changed.set(true);
|
||||||
self.change.trigger();
|
self.change.trigger();
|
||||||
|
state.trigger_cci(CCI_IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_inhibitor(&self, inhibitor: &Rc<ZwpIdleInhibitorV1>) {
|
pub fn add_inhibitor(&self, state: &State, inhibitor: &Rc<ZwpIdleInhibitorV1>) {
|
||||||
self.inhibitors.set(inhibitor.inhibit_id, inhibitor.clone());
|
self.inhibitors.set(inhibitor.inhibit_id, inhibitor.clone());
|
||||||
self.inhibitors_changed();
|
self.inhibitors_changed(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_inhibitor(&self, inhibitor: &ZwpIdleInhibitorV1) {
|
pub fn remove_inhibitor(&self, state: &State, inhibitor: &ZwpIdleInhibitorV1) {
|
||||||
self.inhibitors.remove(&inhibitor.inhibit_id);
|
self.inhibitors.remove(&inhibitor.inhibit_id);
|
||||||
self.inhibitors_changed();
|
self.inhibitors_changed(state);
|
||||||
if self.inhibitors.is_empty() {
|
if self.inhibitors.is_empty() {
|
||||||
self.resume_inhibited_notifications();
|
self.resume_inhibited_notifications();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inhibitors_changed(&self) {
|
fn inhibitors_changed(&self, state: &State) {
|
||||||
self.inhibitors_changed.set(true);
|
self.inhibitors_changed.set(true);
|
||||||
self.change.trigger();
|
self.change.trigger();
|
||||||
|
state.trigger_cci(CCI_IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resume_inhibited_notifications(&self) {
|
fn resume_inhibited_notifications(&self) {
|
||||||
|
|
@ -482,30 +494,39 @@ impl ConnectorData {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*self.state.borrow_mut() = s.clone();
|
*self.state.borrow_mut() = s.clone();
|
||||||
if old.enabled != s.enabled {
|
macro_rules! b {
|
||||||
|
($expr:expr) => {{
|
||||||
|
let e = $expr;
|
||||||
|
if e {
|
||||||
|
state.trigger_cci(CCI_OUTPUTS);
|
||||||
|
}
|
||||||
|
e
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
if b!(old.enabled != s.enabled) {
|
||||||
self.head_managers.handle_enabled_change(s.enabled);
|
self.head_managers.handle_enabled_change(s.enabled);
|
||||||
}
|
}
|
||||||
if old.active != s.active {
|
if b!(old.active != s.active) {
|
||||||
self.head_managers.handle_active_change(s.active);
|
self.head_managers.handle_active_change(s.active);
|
||||||
}
|
}
|
||||||
if old.non_desktop_override != s.non_desktop_override {
|
if b!(old.non_desktop_override != s.non_desktop_override) {
|
||||||
self.head_managers
|
self.head_managers
|
||||||
.handle_non_desktop_override_changed(s.non_desktop_override);
|
.handle_non_desktop_override_changed(s.non_desktop_override);
|
||||||
}
|
}
|
||||||
if old.vrr != s.vrr {
|
if b!(old.vrr != s.vrr) {
|
||||||
self.head_managers.handle_vrr_change(s.vrr);
|
self.head_managers.handle_vrr_change(s.vrr);
|
||||||
}
|
}
|
||||||
if old.tearing != s.tearing {
|
if b!(old.tearing != s.tearing) {
|
||||||
self.head_managers.handle_tearing_enabled_change(s.tearing);
|
self.head_managers.handle_tearing_enabled_change(s.tearing);
|
||||||
}
|
}
|
||||||
if old.format != s.format {
|
if b!(old.format != s.format) {
|
||||||
self.head_managers.handle_format_change(s.format);
|
self.head_managers.handle_format_change(s.format);
|
||||||
}
|
}
|
||||||
if (old.color_space, old.eotf) != (s.color_space, s.eotf) {
|
if b!((old.color_space, old.eotf) != (s.color_space, s.eotf)) {
|
||||||
self.head_managers
|
self.head_managers
|
||||||
.handle_colors_change(s.color_space, s.eotf);
|
.handle_colors_change(s.color_space, s.eotf);
|
||||||
}
|
}
|
||||||
if old.mode != s.mode {
|
if b!(old.mode != s.mode) {
|
||||||
self.head_managers.handle_mode_change(s.mode);
|
self.head_managers.handle_mode_change(s.mode);
|
||||||
for head in self.wlr_output_heads.lock().values() {
|
for head in self.wlr_output_heads.lock().values() {
|
||||||
head.handle_mode_change(s.mode);
|
head.handle_mode_change(s.mode);
|
||||||
|
|
@ -528,12 +549,14 @@ impl DrmDevData {
|
||||||
self.dev.clone().make_render_device();
|
self.dev.clone().make_render_device();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_direct_scanout_enabled(&self, enabled: bool) {
|
pub fn set_direct_scanout_enabled(&self, state: &State, enabled: bool) {
|
||||||
self.dev.set_direct_scanout_enabled(enabled);
|
self.dev.set_direct_scanout_enabled(enabled);
|
||||||
|
state.trigger_cci(CCI_GPUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_flip_margin(&self, margin: u64) {
|
pub fn set_flip_margin(&self, state: &State, margin: u64) {
|
||||||
self.dev.set_flip_margin(margin);
|
self.dev.set_flip_margin(margin);
|
||||||
|
state.trigger_cci(CCI_GPUS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -642,6 +665,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_render_ctx(&self, ctx: Option<Rc<dyn GfxContext>>) {
|
pub fn set_render_ctx(&self, ctx: Option<Rc<dyn GfxContext>>) {
|
||||||
|
self.egg_state.clear();
|
||||||
self.explicit_sync_supported.set(false);
|
self.explicit_sync_supported.set(false);
|
||||||
self.render_ctx.set(ctx.clone());
|
self.render_ctx.set(ctx.clone());
|
||||||
self.render_ctx_version.fetch_add(1);
|
self.render_ctx_version.fetch_add(1);
|
||||||
|
|
@ -756,6 +780,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expose_new_singletons();
|
self.expose_new_singletons();
|
||||||
|
self.trigger_cci(CCI_COLOR_MANAGEMENT | CCI_GPUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reload_cursors(&self) {
|
fn reload_cursors(&self) {
|
||||||
|
|
@ -1000,6 +1025,7 @@ impl State {
|
||||||
} else {
|
} else {
|
||||||
self.stop_xwayland();
|
self.stop_xwayland();
|
||||||
}
|
}
|
||||||
|
self.trigger_cci(CCI_XWAYLAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_xwayland_use_wire_scale(&self, use_wire_scale: bool) {
|
pub fn set_xwayland_use_wire_scale(&self, use_wire_scale: bool) {
|
||||||
|
|
@ -1007,6 +1033,7 @@ impl State {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.update_xwayland_wire_scale();
|
self.update_xwayland_wire_scale();
|
||||||
|
self.trigger_cci(CCI_XWAYLAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_serial(&self, client: Option<&Client>) -> u64 {
|
pub fn next_serial(&self, client: Option<&Client>) -> u64 {
|
||||||
|
|
@ -1154,6 +1181,9 @@ impl State {
|
||||||
self.wait_for_syncobj.clear();
|
self.wait_for_syncobj.clear();
|
||||||
self.xdg_surface_configure_events.clear();
|
self.xdg_surface_configure_events.clear();
|
||||||
self.lazy_event_sources.clear();
|
self.lazy_event_sources.clear();
|
||||||
|
self.bo_drop_queue.kill();
|
||||||
|
self.egg_state.clear();
|
||||||
|
self.control_centers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_toplevel_id(&self, id: ToplevelIdentifier) {
|
pub fn remove_toplevel_id(&self, id: ToplevelIdentifier) {
|
||||||
|
|
@ -1703,11 +1733,13 @@ impl State {
|
||||||
pub fn set_color_management_enabled(&self, enabled: bool) {
|
pub fn set_color_management_enabled(&self, enabled: bool) {
|
||||||
self.color_management_enabled.set(enabled);
|
self.color_management_enabled.set(enabled);
|
||||||
self.expose_new_singletons();
|
self.expose_new_singletons();
|
||||||
|
self.trigger_cci(CCI_COLOR_MANAGEMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_primary_selection_enabled(&self, enabled: bool) {
|
pub fn set_primary_selection_enabled(&self, enabled: bool) {
|
||||||
self.enable_primary_selection.set(enabled);
|
self.enable_primary_selection.set(enabled);
|
||||||
self.expose_new_singletons();
|
self.expose_new_singletons();
|
||||||
|
self.trigger_cci(CCI_LOOK_AND_FEEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_explicit_sync_enabled(&self, enabled: bool) {
|
pub fn set_explicit_sync_enabled(&self, enabled: bool) {
|
||||||
|
|
@ -1718,6 +1750,7 @@ impl State {
|
||||||
pub fn set_log_level(&self, level: LogLevel) {
|
pub fn set_log_level(&self, level: LogLevel) {
|
||||||
if let Some(logger) = &self.logger {
|
if let Some(logger) = &self.logger {
|
||||||
logger.set_level(level);
|
logger.set_level(level);
|
||||||
|
self.trigger_cci(CCI_COMPOSITOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1742,6 +1775,7 @@ impl State {
|
||||||
self.root.clone().node_visit(&mut V);
|
self.root.clone().node_visit(&mut V);
|
||||||
self.damage(self.root.extents.get());
|
self.damage(self.root.extents.get());
|
||||||
self.icons.clear();
|
self.icons.clear();
|
||||||
|
self.trigger_cci(CCI_LOOK_AND_FEEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_colors(&self) {
|
pub fn reset_colors(&self) {
|
||||||
|
|
@ -1776,6 +1810,7 @@ impl State {
|
||||||
pub fn set_ei_socket_enabled(self: &Rc<Self>, enabled: bool) {
|
pub fn set_ei_socket_enabled(self: &Rc<Self>, enabled: bool) {
|
||||||
self.enable_ei_acceptor.set(enabled);
|
self.enable_ei_acceptor.set(enabled);
|
||||||
self.update_ei_acceptor();
|
self.update_ei_acceptor();
|
||||||
|
self.trigger_cci(CCI_COMPOSITOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
|
pub fn set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
|
||||||
|
|
@ -1783,6 +1818,7 @@ impl State {
|
||||||
for output in self.root.outputs.lock().values() {
|
for output in self.root.outputs.lock().values() {
|
||||||
output.handle_workspace_display_order_update();
|
output.handle_workspace_display_order_update();
|
||||||
}
|
}
|
||||||
|
self.trigger_cci(CCI_COMPOSITOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spaces_changed(&self) {
|
fn spaces_changed(&self) {
|
||||||
|
|
@ -1804,6 +1840,7 @@ impl State {
|
||||||
self.root.clone().node_visit(&mut V);
|
self.root.clone().node_visit(&mut V);
|
||||||
self.damage(self.root.extents.get());
|
self.damage(self.root.extents.get());
|
||||||
self.icons.update_sizes(self);
|
self.icons.update_sizes(self);
|
||||||
|
self.trigger_cci(CCI_LOOK_AND_FEEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_show_bar(&self, show: bool) {
|
pub fn set_show_bar(&self, show: bool) {
|
||||||
|
|
@ -1818,15 +1855,18 @@ impl State {
|
||||||
|
|
||||||
pub fn set_ui_drag_enabled(&self, enabled: bool) {
|
pub fn set_ui_drag_enabled(&self, enabled: bool) {
|
||||||
self.ui_drag_enabled.set(enabled);
|
self.ui_drag_enabled.set(enabled);
|
||||||
|
self.trigger_cci(CCI_LOOK_AND_FEEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ui_drag_threshold(&self, threshold: i32) {
|
pub fn set_ui_drag_threshold(&self, threshold: i32) {
|
||||||
self.ui_drag_threshold_squared
|
self.ui_drag_threshold_squared
|
||||||
.set(threshold.saturating_mul(threshold));
|
.set(threshold.saturating_mul(threshold));
|
||||||
|
self.trigger_cci(CCI_LOOK_AND_FEEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_show_pin_icon(&self, show: bool) {
|
pub fn set_show_pin_icon(&self, show: bool) {
|
||||||
self.show_pin_icon.set(show);
|
self.show_pin_icon.set(show);
|
||||||
|
self.trigger_cci(CCI_LOOK_AND_FEEL);
|
||||||
for stacked in self.root.stacked.iter() {
|
for stacked in self.root.stacked.iter() {
|
||||||
if let Some(float) = stacked.deref().clone().node_into_float() {
|
if let Some(float) = stacked.deref().clone().node_into_float() {
|
||||||
float.schedule_render_titles();
|
float.schedule_render_titles();
|
||||||
|
|
@ -1836,6 +1876,7 @@ impl State {
|
||||||
|
|
||||||
pub fn set_float_above_fullscreen(&self, v: bool) {
|
pub fn set_float_above_fullscreen(&self, v: bool) {
|
||||||
self.float_above_fullscreen.set(v);
|
self.float_above_fullscreen.set(v);
|
||||||
|
self.trigger_cci(CCI_LOOK_AND_FEEL);
|
||||||
for seat in self.globals.seats.lock().values() {
|
for seat in self.globals.seats.lock().values() {
|
||||||
seat.emulate_cursor_moved();
|
seat.emulate_cursor_moved();
|
||||||
seat.trigger_tree_changed(false);
|
seat.trigger_tree_changed(false);
|
||||||
|
|
@ -1849,6 +1890,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fonts_changed(&self) {
|
fn fonts_changed(&self) {
|
||||||
|
self.trigger_cci(CCI_LOOK_AND_FEEL);
|
||||||
struct V;
|
struct V;
|
||||||
impl NodeVisitorBase for V {
|
impl NodeVisitorBase for V {
|
||||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||||
|
|
@ -1872,6 +1914,7 @@ impl State {
|
||||||
theme.font.set(self.theme.default_font.clone());
|
theme.font.set(self.theme.default_font.clone());
|
||||||
theme.bar_font.set(None);
|
theme.bar_font.set(None);
|
||||||
theme.title_font.set(None);
|
theme.title_font.set(None);
|
||||||
|
self.egg_state.reset_fonts();
|
||||||
self.fonts_changed();
|
self.fonts_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1892,6 +1935,16 @@ impl State {
|
||||||
self.fonts_changed();
|
self.fonts_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_egui_fonts(&self, proportional: Option<Vec<&str>>, monospace: Option<Vec<&str>>) {
|
||||||
|
if let Some(fonts) = &proportional {
|
||||||
|
self.egg_state.set_proportional_fonts(fonts);
|
||||||
|
}
|
||||||
|
if let Some(fonts) = &monospace {
|
||||||
|
self.egg_state.set_monospace_fonts(fonts);
|
||||||
|
}
|
||||||
|
self.fonts_changed();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_bar_position(&self, p: BarPosition) {
|
pub fn set_bar_position(&self, p: BarPosition) {
|
||||||
self.theme.bar_position.set(p);
|
self.theme.bar_position.set(p);
|
||||||
self.spaces_changed();
|
self.spaces_changed();
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use {
|
||||||
BackendConnectorState, BackendConnectorStateSerial, Connector, ConnectorEvent,
|
BackendConnectorState, BackendConnectorStateSerial, Connector, ConnectorEvent,
|
||||||
ConnectorId, MonitorInfo,
|
ConnectorId, MonitorInfo,
|
||||||
},
|
},
|
||||||
|
control_center::CCI_OUTPUTS,
|
||||||
format::XRGB8888,
|
format::XRGB8888,
|
||||||
globals::GlobalName,
|
globals::GlobalName,
|
||||||
ifs::{
|
ifs::{
|
||||||
|
|
@ -108,6 +109,7 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
||||||
for mgr in state.head_managers.lock().values() {
|
for mgr in state.head_managers.lock().values() {
|
||||||
mgr.announce(&data);
|
mgr.announce(&data);
|
||||||
}
|
}
|
||||||
|
state.trigger_cci(CCI_OUTPUTS);
|
||||||
if state.connectors.set(id, data).is_some() {
|
if state.connectors.set(id, data).is_some() {
|
||||||
panic!("Connector id has been reused");
|
panic!("Connector id has been reused");
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +149,7 @@ impl ConnectorHandler {
|
||||||
self.data.handler.set(None);
|
self.data.handler.set(None);
|
||||||
self.state.connectors.remove(&self.id);
|
self.state.connectors.remove(&self.id);
|
||||||
self.data.head_managers.handle_removed();
|
self.data.head_managers.handle_removed();
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_connected(&self, info: MonitorInfo) {
|
async fn handle_connected(&self, info: MonitorInfo) {
|
||||||
|
|
@ -162,6 +165,7 @@ impl ConnectorHandler {
|
||||||
}
|
}
|
||||||
self.data.connected.set(false);
|
self.data.connected.set(false);
|
||||||
self.data.head_managers.handle_output_disconnected();
|
self.data.head_managers.handle_output_disconnected();
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
for head in self.data.wlr_output_heads.lock().drain_values() {
|
for head in self.data.wlr_output_heads.lock().drain_values() {
|
||||||
head.handle_disconnected();
|
head.handle_disconnected();
|
||||||
}
|
}
|
||||||
|
|
@ -213,12 +217,7 @@ impl ConnectorHandler {
|
||||||
info.primaries,
|
info.primaries,
|
||||||
info.luminance,
|
info.luminance,
|
||||||
));
|
));
|
||||||
let schedule = Rc::new(OutputSchedule::new(
|
let schedule = Rc::new(OutputSchedule::new(&self.state, &self.data, &desired_state));
|
||||||
&self.state.ring,
|
|
||||||
&self.state.eng,
|
|
||||||
&self.data,
|
|
||||||
&desired_state,
|
|
||||||
));
|
|
||||||
let _schedule = self
|
let _schedule = self
|
||||||
.state
|
.state
|
||||||
.eng
|
.eng
|
||||||
|
|
@ -341,6 +340,7 @@ impl ConnectorHandler {
|
||||||
self.data
|
self.data
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_output_connected(&output_data);
|
.handle_output_connected(&output_data);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
self.state.wlr_output_managers.announce_head(&output_data);
|
self.state.wlr_output_managers.announce_head(&output_data);
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
while let Some(event) = self.data.connector.event() {
|
while let Some(event) = self.data.connector.event() {
|
||||||
|
|
@ -353,6 +353,7 @@ impl ConnectorHandler {
|
||||||
}
|
}
|
||||||
ConnectorEvent::FormatsChanged(formats) => {
|
ConnectorEvent::FormatsChanged(formats) => {
|
||||||
self.data.head_managers.handle_formats_change(&formats);
|
self.data.head_managers.handle_formats_change(&formats);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
on.global.formats.set(formats);
|
on.global.formats.set(formats);
|
||||||
}
|
}
|
||||||
ConnectorEvent::State(state) => {
|
ConnectorEvent::State(state) => {
|
||||||
|
|
@ -466,6 +467,7 @@ impl ConnectorHandler {
|
||||||
self.data
|
self.data
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_output_connected(&output_data);
|
.handle_output_connected(&output_data);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
self.state.wlr_output_managers.announce_head(&output_data);
|
self.state.wlr_output_managers.announce_head(&output_data);
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
while let Some(event) = self.data.connector.event() {
|
while let Some(event) = self.data.connector.event() {
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ impl DeviceHandler {
|
||||||
}
|
}
|
||||||
for seat in self.state.globals.seats.lock().values() {
|
for seat in self.state.globals.seats.lock().values() {
|
||||||
if seat.seat_name() == DEFAULT_SEAT_NAME {
|
if seat.seat_name() == DEFAULT_SEAT_NAME {
|
||||||
self.data.set_seat(Some(seat.clone()));
|
self.data.set_seat(&self.state, Some(seat.clone()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,6 +102,6 @@ impl DeviceHandler {
|
||||||
.input_device_handlers
|
.input_device_handlers
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.remove(&self.dev.id());
|
.remove(&self.dev.id());
|
||||||
self.data.set_seat(None);
|
self.data.set_seat(&self.state, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ impl ToolClient {
|
||||||
self_id: s.registry,
|
self_id: s.registry,
|
||||||
name: s.jay_compositor.0,
|
name: s.jay_compositor.0,
|
||||||
interface: JayCompositor.name(),
|
interface: JayCompositor.name(),
|
||||||
version: s.jay_compositor.1.min(27),
|
version: s.jay_compositor.1.min(28),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
});
|
});
|
||||||
self.jay_compositor.set(Some(id));
|
self.jay_compositor.set(Some(id));
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use {
|
||||||
},
|
},
|
||||||
client::ClientId,
|
client::ClientId,
|
||||||
cmm::cmm_description::ColorDescription,
|
cmm::cmm_description::ColorDescription,
|
||||||
|
control_center::CCI_OUTPUTS,
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
|
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
|
||||||
|
|
@ -243,6 +244,7 @@ impl OutputNode {
|
||||||
.connector
|
.connector
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_tearing_active_change(tearing);
|
.handle_tearing_active_change(tearing);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -501,6 +503,7 @@ impl OutputNode {
|
||||||
.connector
|
.connector
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_scale_change(scale);
|
.handle_scale_change(scale);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
for head in self.global.connector.wlr_output_heads.lock().values() {
|
for head in self.global.connector.wlr_output_heads.lock().values() {
|
||||||
head.handle_new_scale(scale);
|
head.handle_new_scale(scale);
|
||||||
}
|
}
|
||||||
|
|
@ -873,6 +876,7 @@ impl OutputNode {
|
||||||
.connector
|
.connector
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_transform_change(transform);
|
.handle_transform_change(transform);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
for head in self.global.connector.wlr_output_heads.lock().values() {
|
for head in self.global.connector.wlr_output_heads.lock().values() {
|
||||||
head.hande_transform_change(transform);
|
head.hande_transform_change(transform);
|
||||||
}
|
}
|
||||||
|
|
@ -935,6 +939,7 @@ impl OutputNode {
|
||||||
.connector
|
.connector
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_position_size_change(self);
|
.handle_position_size_change(self);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_state(self: &Rc<Self>, old: BackendConnectorState, state: BackendConnectorState) {
|
pub fn update_state(self: &Rc<Self>, old: BackendConnectorState, state: BackendConnectorState) {
|
||||||
|
|
@ -989,6 +994,7 @@ impl OutputNode {
|
||||||
.connector
|
.connector
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_brightness_change(brightness);
|
.handle_brightness_change(brightness);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1004,6 +1010,7 @@ impl OutputNode {
|
||||||
.connector
|
.connector
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_use_native_gamut_change(use_native_gamut);
|
.handle_use_native_gamut_change(use_native_gamut);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1015,6 +1022,7 @@ impl OutputNode {
|
||||||
.connector
|
.connector
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_blend_space_change(blend_space);
|
.handle_blend_space_change(blend_space);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn find_stacked_at(
|
fn find_stacked_at(
|
||||||
|
|
@ -1480,6 +1488,7 @@ impl OutputNode {
|
||||||
.connector
|
.connector
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_vrr_mode_change(mode);
|
.handle_vrr_mode_change(mode);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
for head in self.global.connector.wlr_output_heads.lock().values() {
|
for head in self.global.connector.wlr_output_heads.lock().values() {
|
||||||
head.handle_vrr_mode_change(mode);
|
head.handle_vrr_mode_change(mode);
|
||||||
}
|
}
|
||||||
|
|
@ -1494,6 +1503,7 @@ impl OutputNode {
|
||||||
.connector
|
.connector
|
||||||
.head_managers
|
.head_managers
|
||||||
.handle_tearing_mode_change(mode);
|
.handle_tearing_mode_change(mode);
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1543,6 +1553,7 @@ impl OutputNode {
|
||||||
|
|
||||||
pub fn set_flip_margin(&self, margin_ns: u64) {
|
pub fn set_flip_margin(&self, margin_ns: u64) {
|
||||||
self.flip_margin_ns.set(Some(margin_ns));
|
self.flip_margin_ns.set(Some(margin_ns));
|
||||||
|
self.state.trigger_cci(CCI_OUTPUTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -936,7 +936,6 @@ impl ToplevelData {
|
||||||
parent.node_is_workspace()
|
parent.node_is_workspace()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn property_changed_source(&self) -> &Rc<LazyEventSource> {
|
pub fn property_changed_source(&self) -> &Rc<LazyEventSource> {
|
||||||
self.property_changed_source
|
self.property_changed_source
|
||||||
.get_or_init(|| self.state.lazy_event_sources.create_source())
|
.get_or_init(|| self.state.lazy_event_sources.create_source())
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ pub mod nice;
|
||||||
pub mod nonblock;
|
pub mod nonblock;
|
||||||
pub mod num_cpus;
|
pub mod num_cpus;
|
||||||
pub mod numcell;
|
pub mod numcell;
|
||||||
|
pub mod object_drop_queue;
|
||||||
pub mod on_change;
|
pub mod on_change;
|
||||||
pub mod on_drop_event;
|
pub mod on_drop_event;
|
||||||
pub mod once;
|
pub mod once;
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,14 @@ impl<T> NumCell<T> {
|
||||||
{
|
{
|
||||||
!self.is_zero()
|
!self.is_zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn take(&self) -> T
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
self.t.replace(T::default())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BitOr<Output = T> + Copy> BitOr<T> for &'_ NumCell<T> {
|
impl<T: BitOr<Output = T> + Copy> BitOr<T> for &'_ NumCell<T> {
|
||||||
|
|
|
||||||
83
src/utils/object_drop_queue.rs
Normal file
83
src/utils/object_drop_queue.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
io_uring::{IoUring, PendingPoll, PollCallback},
|
||||||
|
utils::{errorfmt::ErrorFmt, oserror::OsError, stack::Stack},
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
uapi::{OwnedFd, c::c_short},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ObjectDropQueue<T> {
|
||||||
|
ring: Rc<IoUring>,
|
||||||
|
killed: Cell<bool>,
|
||||||
|
pending: RefCell<Vec<Option<(T, PendingPoll)>>>,
|
||||||
|
stack: Stack<Rc<Pollable<T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pollable<T> {
|
||||||
|
queue: Rc<ObjectDropQueue<T>>,
|
||||||
|
idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ObjectDropQueue<T> {
|
||||||
|
pub fn new(ring: &Rc<IoUring>) -> Self {
|
||||||
|
Self {
|
||||||
|
ring: ring.clone(),
|
||||||
|
killed: Default::default(),
|
||||||
|
pending: Default::default(),
|
||||||
|
stack: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(self: &Rc<Self>, fd: &Rc<OwnedFd>, t: T)
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
|
if self.killed.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let pending = &mut *self.pending.borrow_mut();
|
||||||
|
let pollable = match self.stack.pop() {
|
||||||
|
Some(p) => p,
|
||||||
|
None => {
|
||||||
|
let pollable = Rc::new(Pollable {
|
||||||
|
queue: self.clone(),
|
||||||
|
idx: pending.len(),
|
||||||
|
});
|
||||||
|
pending.push(None);
|
||||||
|
pollable
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let idx = pollable.idx;
|
||||||
|
match self.ring.readable_external(fd, pollable) {
|
||||||
|
Ok(p) => {
|
||||||
|
pending[idx] = Some((t, p));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not register object: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kill(&self) {
|
||||||
|
self.killed.set(true);
|
||||||
|
self.pending.take();
|
||||||
|
self.stack.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PollCallback for Pollable<T> {
|
||||||
|
fn completed(self: Rc<Self>, res: Result<c_short, OsError>) {
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::error!("Could not wait for fd to become readable: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
let q = &self.queue;
|
||||||
|
if !q.killed.get() {
|
||||||
|
q.pending.borrow_mut()[self.idx] = None;
|
||||||
|
q.stack.push(self.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ use {
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct Opaque {
|
pub struct Opaque {
|
||||||
lo: u64,
|
lo: u64,
|
||||||
hi: u64,
|
hi: u64,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ pub fn pipe() -> Result<Pipe<OwnedFd, OwnedFd>, OsError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, R> Pipe<L, R> {
|
impl<L, R> Pipe<L, R> {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn map_read<Lprime>(self, map: impl FnOnce(L) -> Lprime) -> Pipe<Lprime, R> {
|
pub fn map_read<Lprime>(self, map: impl FnOnce(L) -> Lprime) -> Pipe<Lprime, R> {
|
||||||
Pipe {
|
Pipe {
|
||||||
read: map(self.read),
|
read: map(self.read),
|
||||||
|
|
@ -22,7 +21,6 @@ impl<L, R> Pipe<L, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn map_write<Rprime>(self, map: impl FnOnce(R) -> Rprime) -> Pipe<L, Rprime> {
|
pub fn map_write<Rprime>(self, map: impl FnOnce(R) -> Rprime) -> Pipe<L, Rprime> {
|
||||||
Pipe {
|
Pipe {
|
||||||
read: self.read,
|
read: self.read,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, Ord, PartialOrd)]
|
||||||
pub struct ToplevelIdentifier(Opaque);
|
pub struct ToplevelIdentifier(Opaque);
|
||||||
|
|
||||||
unsafe impl UnsafeCellCloneSafe for ToplevelIdentifier {}
|
unsafe impl UnsafeCellCloneSafe for ToplevelIdentifier {}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
format::Format,
|
||||||
|
gfx_api::SyncFile,
|
||||||
utils::{compat::IoctlNumber, oserror::OsError},
|
utils::{compat::IoctlNumber, oserror::OsError},
|
||||||
video::{LINEAR_MODIFIER, Modifier},
|
video::{
|
||||||
|
LINEAR_MODIFIER, Modifier,
|
||||||
|
drm::{DrmError, syncobj::merge_sync_files},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
arrayvec::ArrayVec,
|
arrayvec::ArrayVec,
|
||||||
std::{cell::OnceCell, rc::Rc, sync::OnceLock},
|
std::{cell::OnceCell, rc::Rc, sync::OnceLock},
|
||||||
|
|
@ -113,6 +117,22 @@ impl DmaBuf {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn export_sync_file(&self, flags: u32) -> Result<Option<SyncFile>, DrmError> {
|
||||||
|
let mut sf = PlaneVec::new();
|
||||||
|
for plane in &self.planes {
|
||||||
|
sf.push(
|
||||||
|
dma_buf_export_sync_file(&plane.fd, flags)
|
||||||
|
.map(Rc::new)
|
||||||
|
.map(SyncFile)
|
||||||
|
.map_err(DrmError::ExportSyncFile)?,
|
||||||
|
);
|
||||||
|
if self.is_one_file() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
merge_sync_files(sf.iter())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const DMA_BUF_BASE: u64 = b'b' as _;
|
const DMA_BUF_BASE: u64 = b'b' as _;
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,6 @@ impl UsrJayCompositor {
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn get_sync_file_surface(&self, surface: &UsrWlSurface) -> Rc<UsrJaySyncFileSurface> {
|
pub fn get_sync_file_surface(&self, surface: &UsrWlSurface) -> Rc<UsrJaySyncFileSurface> {
|
||||||
let obj = Rc::new(UsrJaySyncFileSurface {
|
let obj = Rc::new(UsrJaySyncFileSurface {
|
||||||
id: self.con.id(),
|
id: self.con.id(),
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ pub struct UsrJaySyncFileSurface {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrJaySyncFileSurface {
|
impl UsrJaySyncFileSurface {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn set_acquire(&self, sf: Option<&FdSync>) {
|
pub fn set_acquire(&self, sf: Option<&FdSync>) {
|
||||||
match sf.and_then(|s| s.get_sync_file()) {
|
match sf.and_then(|s| s.get_sync_file()) {
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -33,7 +32,6 @@ impl UsrJaySyncFileSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn get_release(&self) -> Rc<UsrJaySyncFileRelease> {
|
pub fn get_release(&self) -> Rc<UsrJaySyncFileRelease> {
|
||||||
let obj = Rc::new(UsrJaySyncFileRelease {
|
let obj = Rc::new(UsrJaySyncFileRelease {
|
||||||
id: self.con.id(),
|
id: self.con.id(),
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ pub struct UsrWlDataDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrWlDataDevice {
|
impl UsrWlDataDevice {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn set_selection(&self, serial: u32, source: &UsrWlDataSource) {
|
pub fn set_selection(&self, serial: u32, source: &UsrWlDataSource) {
|
||||||
self.con.request(SetSelection {
|
self.con.request(SetSelection {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ pub struct UsrWlDataDeviceManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrWlDataDeviceManager {
|
impl UsrWlDataDeviceManager {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn create_data_source(&self) -> Rc<UsrWlDataSource> {
|
pub fn create_data_source(&self) -> Rc<UsrWlDataSource> {
|
||||||
let obj = Rc::new(UsrWlDataSource {
|
let obj = Rc::new(UsrWlDataSource {
|
||||||
id: self.con.id(),
|
id: self.con.id(),
|
||||||
|
|
@ -37,7 +36,6 @@ impl UsrWlDataDeviceManager {
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn get_data_device(&self, seat: &UsrWlSeat) -> Rc<UsrWlDataDevice> {
|
pub fn get_data_device(&self, seat: &UsrWlSeat) -> Rc<UsrWlDataDevice> {
|
||||||
let obj = Rc::new(UsrWlDataDevice {
|
let obj = Rc::new(UsrWlDataDevice {
|
||||||
id: self.con.id(),
|
id: self.con.id(),
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ pub struct UsrWlDataOffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrWlDataOffer {
|
impl UsrWlDataOffer {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn receive(&self, mime_type: &str, fd: &Rc<OwnedFd>) {
|
pub fn receive(&self, mime_type: &str, fd: &Rc<OwnedFd>) {
|
||||||
self.con.request(Receive {
|
self.con.request(Receive {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ pub trait UsrWlDataSourceOwner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrWlDataSource {
|
impl UsrWlDataSource {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn offer(&self, mime_type: &str) {
|
pub fn offer(&self, mime_type: &str) {
|
||||||
self.con.request(Offer {
|
self.con.request(Offer {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ pub trait UsrWlPointerOwner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrWlPointer {
|
impl UsrWlPointer {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn set_cursor(&self, serial: u32, cursor: Option<&UsrWlSurface>, hot_x: i32, hot_y: i32) {
|
pub fn set_cursor(&self, serial: u32, cursor: Option<&UsrWlSurface>, hot_x: i32, hot_y: i32) {
|
||||||
self.con.request(SetCursor {
|
self.con.request(SetCursor {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@ impl UsrWlSeat {
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn get_keyboard(&self) -> Rc<UsrWlKeyboard> {
|
pub fn get_keyboard(&self) -> Rc<UsrWlKeyboard> {
|
||||||
let kb = Rc::new(UsrWlKeyboard {
|
let kb = Rc::new(UsrWlKeyboard {
|
||||||
id: self.con.id(),
|
id: self.con.id(),
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ pub struct UsrWpCursorShapeDeviceV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrWpCursorShapeDeviceV1 {
|
impl UsrWpCursorShapeDeviceV1 {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn set_shape(&self, serial: u32, cursor: KnownCursor) {
|
pub fn set_shape(&self, serial: u32, cursor: KnownCursor) {
|
||||||
self.con.request(SetShape {
|
self.con.request(SetShape {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ pub struct UsrWpCursorShapeManagerV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrWpCursorShapeManagerV1 {
|
impl UsrWpCursorShapeManagerV1 {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn get_pointer(&self, pointer: &UsrWlPointer) -> Rc<UsrWpCursorShapeDeviceV1> {
|
pub fn get_pointer(&self, pointer: &UsrWlPointer) -> Rc<UsrWpCursorShapeDeviceV1> {
|
||||||
let obj = Rc::new(UsrWpCursorShapeDeviceV1 {
|
let obj = Rc::new(UsrWpCursorShapeDeviceV1 {
|
||||||
id: self.con.id(),
|
id: self.con.id(),
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ pub trait UsrXdgSurfaceOwner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrXdgSurface {
|
impl UsrXdgSurface {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn get_toplevel(&self) -> Rc<UsrXdgToplevel> {
|
pub fn get_toplevel(&self) -> Rc<UsrXdgToplevel> {
|
||||||
let obj = Rc::new(UsrXdgToplevel {
|
let obj = Rc::new(UsrXdgToplevel {
|
||||||
id: self.con.id(),
|
id: self.con.id(),
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ pub struct UsrXdgToplevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrXdgToplevel {
|
impl UsrXdgToplevel {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn set_title(&self, title: &str) {
|
pub fn set_title(&self, title: &str) {
|
||||||
self.con.request(SetTitle {
|
self.con.request(SetTitle {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
@ -24,7 +23,6 @@ impl UsrXdgToplevel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn set_fullscreen(&self, fullscreen: bool) {
|
pub fn set_fullscreen(&self, fullscreen: bool) {
|
||||||
match fullscreen {
|
match fullscreen {
|
||||||
true => {
|
true => {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ pub struct UsrXdgWmBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrXdgWmBase {
|
impl UsrXdgWmBase {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn get_xdg_surface(&self, surface: &UsrWlSurface) -> Rc<UsrXdgSurface> {
|
pub fn get_xdg_surface(&self, surface: &UsrWlSurface) -> Rc<UsrXdgSurface> {
|
||||||
let obj = Rc::new(UsrXdgSurface {
|
let obj = Rc::new(UsrXdgSurface {
|
||||||
id: self.con.id(),
|
id: self.con.id(),
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ pub struct UsrZwpLinuxDmabufV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrZwpLinuxDmabufV1 {
|
impl UsrZwpLinuxDmabufV1 {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn create_buffer(&self, buffer: &DmaBuf) -> Rc<UsrWlBuffer> {
|
pub fn create_buffer(&self, buffer: &DmaBuf) -> Rc<UsrWlBuffer> {
|
||||||
let params = Rc::new(UsrZwpLinuxBufferParamsV1 {
|
let params = Rc::new(UsrZwpLinuxBufferParamsV1 {
|
||||||
id: self.con.id(),
|
id: self.con.id(),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{ClientCaps, ClientError},
|
client::{ClientCaps, ClientError},
|
||||||
compositor::DISPLAY,
|
compositor::DISPLAY,
|
||||||
|
control_center::CCI_XWAYLAND,
|
||||||
forker::{ForkerError, ForkerProxy},
|
forker::{ForkerError, ForkerProxy},
|
||||||
ifs::{
|
ifs::{
|
||||||
ipc::{DataOfferId, DataSourceId, IpcLocation, x_data_offer::XDataOffer},
|
ipc::{DataOfferId, DataSourceId, IpcLocation, x_data_offer::XDataOffer},
|
||||||
|
|
@ -117,9 +118,11 @@ pub async fn manage(state: Rc<State>) {
|
||||||
let display = Rc::new(format!(":{}", xsocket.id));
|
let display = Rc::new(format!(":{}", xsocket.id));
|
||||||
forker.setenv(DISPLAY.as_bytes(), display.as_bytes());
|
forker.setenv(DISPLAY.as_bytes(), display.as_bytes());
|
||||||
state.xwayland.display.set(Some(display.clone()));
|
state.xwayland.display.set(Some(display.clone()));
|
||||||
|
state.trigger_cci(CCI_XWAYLAND);
|
||||||
let _unsetenv = on_drop(|| {
|
let _unsetenv = on_drop(|| {
|
||||||
forker.unsetenv(DISPLAY.as_bytes());
|
forker.unsetenv(DISPLAY.as_bytes());
|
||||||
state.xwayland.display.take();
|
state.xwayland.display.take();
|
||||||
|
state.trigger_cci(CCI_XWAYLAND);
|
||||||
});
|
});
|
||||||
log::info!("Allocated display :{} for Xwayland", xsocket.id);
|
log::info!("Allocated display :{} for Xwayland", xsocket.id);
|
||||||
log::info!("Waiting for connection attempt");
|
log::info!("Waiting for connection attempt");
|
||||||
|
|
@ -212,9 +215,11 @@ async fn run(
|
||||||
state.xwayland.queue.clear();
|
state.xwayland.queue.clear();
|
||||||
state.xwayland.pidfd.set(Some(pidfd.clone()));
|
state.xwayland.pidfd.set(Some(pidfd.clone()));
|
||||||
state.xwayland.client.set(Some(client.clone()));
|
state.xwayland.client.set(Some(client.clone()));
|
||||||
|
state.trigger_cci(CCI_XWAYLAND);
|
||||||
let _remove_pidfd = on_drop(|| {
|
let _remove_pidfd = on_drop(|| {
|
||||||
state.xwayland.pidfd.take();
|
state.xwayland.pidfd.take();
|
||||||
state.xwayland.client.take();
|
state.xwayland.client.take();
|
||||||
|
state.trigger_cci(CCI_XWAYLAND);
|
||||||
});
|
});
|
||||||
{
|
{
|
||||||
let shared = Rc::new(XwmShared::default());
|
let shared = Rc::new(XwmShared::default());
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ pub enum SimpleCommand {
|
||||||
ToggleSimpleImEnabled,
|
ToggleSimpleImEnabled,
|
||||||
ReloadSimpleIm,
|
ReloadSimpleIm,
|
||||||
EnableUnicodeInput,
|
EnableUnicodeInput,
|
||||||
|
OpenControlCenter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -211,6 +212,12 @@ pub struct Theme {
|
||||||
pub bar_separator_width: Option<i32>,
|
pub bar_separator_width: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Egui {
|
||||||
|
pub proportional_fonts: Option<Vec<String>>,
|
||||||
|
pub monospace_fonts: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
pub format: MessageFormat,
|
pub format: MessageFormat,
|
||||||
|
|
@ -510,6 +517,7 @@ pub struct Config {
|
||||||
pub auto_reload: Option<bool>,
|
pub auto_reload: Option<bool>,
|
||||||
pub log_level: Option<LogLevel>,
|
pub log_level: Option<LogLevel>,
|
||||||
pub theme: Theme,
|
pub theme: Theme,
|
||||||
|
pub egui: Egui,
|
||||||
pub gfx_api: Option<GfxApi>,
|
pub gfx_api: Option<GfxApi>,
|
||||||
pub direct_scanout_enabled: Option<bool>,
|
pub direct_scanout_enabled: Option<bool>,
|
||||||
pub drm_devices: Vec<ConfigDrmDevice>,
|
pub drm_devices: Vec<ConfigDrmDevice>,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ mod connector_match;
|
||||||
mod content_type;
|
mod content_type;
|
||||||
mod drm_device;
|
mod drm_device;
|
||||||
mod drm_device_match;
|
mod drm_device_match;
|
||||||
|
mod egui;
|
||||||
mod env;
|
mod env;
|
||||||
pub mod exec;
|
pub mod exec;
|
||||||
mod fallback_output_mode;
|
mod fallback_output_mode;
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ impl ActionParser<'_> {
|
||||||
"toggle-simple-im-enabled" => ToggleSimpleImEnabled,
|
"toggle-simple-im-enabled" => ToggleSimpleImEnabled,
|
||||||
"reload-simple-im" => ReloadSimpleIm,
|
"reload-simple-im" => ReloadSimpleIm,
|
||||||
"enable-unicode-input" => EnableUnicodeInput,
|
"enable-unicode-input" => EnableUnicodeInput,
|
||||||
|
"open-control-center" => OpenControlCenter,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(
|
return Err(
|
||||||
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
config::{
|
config::{
|
||||||
Action, Config, Libei, Theme, UiDrag,
|
Action, Config, Egui, Libei, Theme, UiDrag,
|
||||||
context::Context,
|
context::Context,
|
||||||
extractor::{Extractor, ExtractorError, arr, bol, int, opt, recover, str, val},
|
extractor::{Extractor, ExtractorError, arr, bol, int, opt, recover, str, val},
|
||||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
|
|
@ -13,6 +13,7 @@ use {
|
||||||
connector::ConnectorsParser,
|
connector::ConnectorsParser,
|
||||||
drm_device::DrmDevicesParser,
|
drm_device::DrmDevicesParser,
|
||||||
drm_device_match::DrmDeviceMatchParser,
|
drm_device_match::DrmDeviceMatchParser,
|
||||||
|
egui::EguiParser,
|
||||||
env::EnvParser,
|
env::EnvParser,
|
||||||
fallback_output_mode::FallbackOutputModeParser,
|
fallback_output_mode::FallbackOutputModeParser,
|
||||||
float::FloatParser,
|
float::FloatParser,
|
||||||
|
|
@ -150,6 +151,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
simple_im_val,
|
simple_im_val,
|
||||||
show_titles,
|
show_titles,
|
||||||
fallback_output_mode_val,
|
fallback_output_mode_val,
|
||||||
|
egui_val,
|
||||||
),
|
),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
|
|
@ -208,6 +210,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
opt(val("simple-im")),
|
opt(val("simple-im")),
|
||||||
recover(opt(bol("show-titles"))),
|
recover(opt(bol("show-titles"))),
|
||||||
opt(val("fallback-output-mode")),
|
opt(val("fallback-output-mode")),
|
||||||
|
opt(val("egui")),
|
||||||
),
|
),
|
||||||
))?;
|
))?;
|
||||||
let mut keymap = None;
|
let mut keymap = None;
|
||||||
|
|
@ -313,6 +316,15 @@ impl Parser for ConfigParser<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut egui = Egui::default();
|
||||||
|
if let Some(value) = egui_val {
|
||||||
|
match value.parse(&mut EguiParser(self.0)) {
|
||||||
|
Ok(v) => egui = v,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not parse the egui settings: {}", self.0.error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut gfx_api = None;
|
let mut gfx_api = None;
|
||||||
if let Some(value) = gfx_api_val {
|
if let Some(value) = gfx_api_val {
|
||||||
match value.parse(&mut GfxApiParser) {
|
match value.parse(&mut GfxApiParser) {
|
||||||
|
|
@ -556,6 +568,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
auto_reload: auto_reload.despan(),
|
auto_reload: auto_reload.despan(),
|
||||||
log_level,
|
log_level,
|
||||||
theme,
|
theme,
|
||||||
|
egui,
|
||||||
gfx_api,
|
gfx_api,
|
||||||
drm_devices,
|
drm_devices,
|
||||||
direct_scanout_enabled: direct_scanout.despan(),
|
direct_scanout_enabled: direct_scanout.despan(),
|
||||||
|
|
|
||||||
63
toml-config/src/config/parsers/egui.rs
Normal file
63
toml-config/src/config/parsers/egui.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
config::{
|
||||||
|
Egui,
|
||||||
|
context::Context,
|
||||||
|
extractor::{Extractor, ExtractorError, arr, opt},
|
||||||
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
|
},
|
||||||
|
toml::{
|
||||||
|
toml_span::{Span, Spanned},
|
||||||
|
toml_value::Value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexmap::IndexMap,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EguiParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum EguiParserError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Expected(#[from] UnexpectedDataType),
|
||||||
|
#[error(transparent)]
|
||||||
|
Extractor(#[from] ExtractorError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser for EguiParser<'_> {
|
||||||
|
type Value = Egui;
|
||||||
|
type Error = EguiParserError;
|
||||||
|
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||||
|
|
||||||
|
fn parse_table(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||||
|
) -> ParseResult<Self> {
|
||||||
|
let mut ext = Extractor::new(self.0, span, table);
|
||||||
|
let (proportional_fonts_arr, monospace_fonts_arr) =
|
||||||
|
ext.extract((opt(arr("proportional-fonts")), opt(arr("monospace-fonts"))))?;
|
||||||
|
let mut proportional_fonts = None;
|
||||||
|
let mut monospace_fonts = None;
|
||||||
|
for (out, f) in [
|
||||||
|
(&mut proportional_fonts, proportional_fonts_arr),
|
||||||
|
(&mut monospace_fonts, monospace_fonts_arr),
|
||||||
|
] {
|
||||||
|
if let Some(f) = f {
|
||||||
|
let fonts = out.insert(vec![]);
|
||||||
|
for f in f.value {
|
||||||
|
let Value::String(s) = &f.value else {
|
||||||
|
log::error!("Expected a string: {}", self.0.error3(f.span));
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
fonts.push(s.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Egui {
|
||||||
|
proportional_fonts,
|
||||||
|
monospace_fonts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ alt-m = "toggle-mono"
|
||||||
alt-u = "toggle-fullscreen"
|
alt-u = "toggle-fullscreen"
|
||||||
|
|
||||||
alt-f = "focus-parent"
|
alt-f = "focus-parent"
|
||||||
|
alt-c = "open-control-center"
|
||||||
alt-shift-c = "close"
|
alt-shift-c = "close"
|
||||||
alt-shift-f = "toggle-floating"
|
alt-shift-f = "toggle-floating"
|
||||||
Super_L = { type = "exec", exec = "alacritty" }
|
Super_L = { type = "exec", exec = "alacritty" }
|
||||||
|
|
|
||||||
|
|
@ -36,16 +36,17 @@ use {
|
||||||
is_reload,
|
is_reload,
|
||||||
keyboard::Keymap,
|
keyboard::Keymap,
|
||||||
logging::set_log_level,
|
logging::set_log_level,
|
||||||
on_devices_enumerated, on_idle, on_unload, quit, reload, set_color_management_enabled,
|
on_devices_enumerated, on_idle, on_unload, open_control_center, quit, reload,
|
||||||
set_default_workspace_capture, set_explicit_sync_enabled, set_float_above_fullscreen,
|
set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled,
|
||||||
set_idle, set_idle_grace_period, set_middle_click_paste_enabled, set_show_bar,
|
set_float_above_fullscreen, set_idle, set_idle_grace_period,
|
||||||
set_show_float_pin_icon, set_show_titles, set_ui_drag_enabled, set_ui_drag_threshold,
|
set_middle_click_paste_enabled, set_show_bar, set_show_float_pin_icon, set_show_titles,
|
||||||
|
set_ui_drag_enabled, set_ui_drag_threshold,
|
||||||
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||||
switch_to_vt,
|
switch_to_vt,
|
||||||
tasks::{self, JoinHandle},
|
tasks::{self, JoinHandle},
|
||||||
theme::{
|
theme::{
|
||||||
reset_colors, reset_font, reset_sizes, set_bar_font, set_bar_position, set_font,
|
reset_colors, reset_font, reset_sizes, set_bar_font, set_bar_position,
|
||||||
set_title_font,
|
set_egui_monospace_fonts, set_egui_proportional_fonts, set_font, set_title_font,
|
||||||
},
|
},
|
||||||
toggle_float_above_fullscreen, toggle_show_bar, toggle_show_titles,
|
toggle_float_above_fullscreen, toggle_show_bar, toggle_show_titles,
|
||||||
video::{
|
video::{
|
||||||
|
|
@ -245,6 +246,7 @@ impl Action {
|
||||||
let persistent = state.persistent.clone();
|
let persistent = state.persistent.clone();
|
||||||
b.new(move || persistent.seat.enable_unicode_input())
|
b.new(move || persistent.seat.enable_unicode_input())
|
||||||
}
|
}
|
||||||
|
SimpleCommand::OpenControlCenter => b.new(open_control_center),
|
||||||
},
|
},
|
||||||
Action::Multi { actions } => {
|
Action::Multi { actions } => {
|
||||||
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
||||||
|
|
@ -1633,6 +1635,12 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
|
||||||
if let Some(v) = config.fallback_output_mode {
|
if let Some(v) = config.fallback_output_mode {
|
||||||
persistent.seat.set_fallback_output_mode(v);
|
persistent.seat.set_fallback_output_mode(v);
|
||||||
}
|
}
|
||||||
|
if let Some(f) = &config.egui.proportional_fonts {
|
||||||
|
set_egui_proportional_fonts(f.iter().map(|s| &**s));
|
||||||
|
}
|
||||||
|
if let Some(f) = &config.egui.monospace_fonts {
|
||||||
|
set_egui_monospace_fonts(f.iter().map(|s| &**s));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_command(exec: &Exec) -> Command {
|
fn create_command(exec: &Exec) -> Command {
|
||||||
|
|
|
||||||
|
|
@ -1081,6 +1081,10 @@
|
||||||
"fallback-output-mode": {
|
"fallback-output-mode": {
|
||||||
"description": "Sets the fallback output mode.\n\nThe default is `cursor`.\n\n- Example:\n\n ```toml\n fallback-output-mode = \"focus\"\n ```\n",
|
"description": "Sets the fallback output mode.\n\nThe default is `cursor`.\n\n- Example:\n\n ```toml\n fallback-output-mode = \"focus\"\n ```\n",
|
||||||
"$ref": "#/$defs/FallbackOutputMode"
|
"$ref": "#/$defs/FallbackOutputMode"
|
||||||
|
},
|
||||||
|
"egui": {
|
||||||
|
"description": "Sets the egui settings of the compositor.\n",
|
||||||
|
"$ref": "#/$defs/Egui"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
@ -1237,6 +1241,29 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Egui": {
|
||||||
|
"description": "The egui settings.\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"proportional-fonts": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "The list of proportional fonts.\n\nThe default is `[\"sans-serif\", \"Noto Sans\", \"Noto Color Emoji\"]`.\n",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"monospace-fonts": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "The list of monospace fonts.\n\nThe default is `[\"monospace\", \"Noto Sans Mono\", \"Noto Color Emoji\"]`.\n",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
},
|
||||||
"Eotf": {
|
"Eotf": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The EOTF of an output.\n",
|
"description": "The EOTF of an output.\n",
|
||||||
|
|
@ -1901,7 +1928,8 @@
|
||||||
"disable-simple-im",
|
"disable-simple-im",
|
||||||
"toggle-simple-im-enabled",
|
"toggle-simple-im-enabled",
|
||||||
"reload-simple-im",
|
"reload-simple-im",
|
||||||
"enable-unicode-input"
|
"enable-unicode-input",
|
||||||
|
"open-control-center"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"SimpleIm": {
|
"SimpleIm": {
|
||||||
|
|
|
||||||
|
|
@ -2248,6 +2248,12 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a [FallbackOutputMode](#types-FallbackOutputMode).
|
The value of this field should be a [FallbackOutputMode](#types-FallbackOutputMode).
|
||||||
|
|
||||||
|
- `egui` (optional):
|
||||||
|
|
||||||
|
Sets the egui settings of the compositor.
|
||||||
|
|
||||||
|
The value of this field should be a [Egui](#types-Egui).
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Connector"></a>
|
<a name="types-Connector"></a>
|
||||||
### `Connector`
|
### `Connector`
|
||||||
|
|
@ -2588,6 +2594,32 @@ The table has the following fields:
|
||||||
The numbers should be integers.
|
The numbers should be integers.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="types-Egui"></a>
|
||||||
|
### `Egui`
|
||||||
|
|
||||||
|
The egui settings.
|
||||||
|
|
||||||
|
Values of this type should be tables.
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `proportional-fonts` (optional):
|
||||||
|
|
||||||
|
The list of proportional fonts.
|
||||||
|
|
||||||
|
The default is `["sans-serif", "Noto Sans", "Noto Color Emoji"]`.
|
||||||
|
|
||||||
|
The value of this field should be an array of strings.
|
||||||
|
|
||||||
|
- `monospace-fonts` (optional):
|
||||||
|
|
||||||
|
The list of monospace fonts.
|
||||||
|
|
||||||
|
The default is `["monospace", "Noto Sans Mono", "Noto Color Emoji"]`.
|
||||||
|
|
||||||
|
The value of this field should be an array of strings.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Eotf"></a>
|
<a name="types-Eotf"></a>
|
||||||
### `Eotf`
|
### `Eotf`
|
||||||
|
|
||||||
|
|
@ -4395,6 +4427,10 @@ The string should have one of the following values:
|
||||||
|
|
||||||
This has no effect if the simple IM is not currently active.
|
This has no effect if the simple IM is not currently active.
|
||||||
|
|
||||||
|
- `open-control-center`:
|
||||||
|
|
||||||
|
Opens the control center.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="types-SimpleIm"></a>
|
<a name="types-SimpleIm"></a>
|
||||||
|
|
|
||||||
|
|
@ -1089,6 +1089,8 @@ SimpleActionName:
|
||||||
Enables Unicode input in the simple, XCompose based input method.
|
Enables Unicode input in the simple, XCompose based input method.
|
||||||
|
|
||||||
This has no effect if the simple IM is not currently active.
|
This has no effect if the simple IM is not currently active.
|
||||||
|
- value: open-control-center
|
||||||
|
description: Opens the control center.
|
||||||
|
|
||||||
|
|
||||||
Color:
|
Color:
|
||||||
|
|
@ -3004,6 +3006,11 @@ Config:
|
||||||
```toml
|
```toml
|
||||||
fallback-output-mode = "focus"
|
fallback-output-mode = "focus"
|
||||||
```
|
```
|
||||||
|
egui:
|
||||||
|
ref: Egui
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
Sets the egui settings of the compositor.
|
||||||
|
|
||||||
|
|
||||||
Idle:
|
Idle:
|
||||||
|
|
@ -4426,3 +4433,28 @@ FallbackOutputMode:
|
||||||
description: Use the output the cursor is on.
|
description: Use the output the cursor is on.
|
||||||
- value: focus
|
- value: focus
|
||||||
description: Use the output the focus is on (highlighted window).
|
description: Use the output the focus is on (highlighted window).
|
||||||
|
|
||||||
|
|
||||||
|
Egui:
|
||||||
|
kind: table
|
||||||
|
description: |
|
||||||
|
The egui settings.
|
||||||
|
fields:
|
||||||
|
proportional-fonts:
|
||||||
|
kind: array
|
||||||
|
items:
|
||||||
|
kind: string
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
The list of proportional fonts.
|
||||||
|
|
||||||
|
The default is `["sans-serif", "Noto Sans", "Noto Color Emoji"]`.
|
||||||
|
monospace-fonts:
|
||||||
|
kind: array
|
||||||
|
items:
|
||||||
|
kind: string
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
The list of monospace fonts.
|
||||||
|
|
||||||
|
The default is `["monospace", "Noto Sans Mono", "Noto Color Emoji"]`.
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,10 @@ request get_pid (since = 27) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request open_control_center (since = 28) {
|
||||||
|
id: id(jay_open_control_center_request),
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event client_id {
|
event client_id {
|
||||||
|
|
|
||||||
7
wire/jay_open_control_center_request.txt
Normal file
7
wire/jay_open_control_center_request.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
request destroy {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
event failed {
|
||||||
|
msg: str,
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue