diff --git a/.gitignore b/.gitignore index 9b4aeb32..d1b99f13 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ kime_engine.h kime_engine.hpp massif.out* flamegraph.svg +kime-crash.log result .DS_Store diff --git a/Cargo.lock b/Cargo.lock index 9aff2165..a6794e3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "thiserror 1.0.69", ] @@ -83,15 +83,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anes" version = "0.1.6" @@ -151,15 +142,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" -[[package]] -name = "ash" -version = "0.38.0+1.3.281" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" -dependencies = [ - "libloading", -] - [[package]] name = "async-broadcast" version = "0.7.2" @@ -180,7 +162,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -191,7 +173,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -208,22 +190,20 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bindgen" -version = "0.69.5" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags 2.10.0", "cexpr", "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", + "itertools 0.13.0", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash 2.1.1", "shlex", - "syn 2.0.114", + "syn", ] [[package]] @@ -252,15 +232,6 @@ name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" -dependencies = [ - "serde_core", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block2" @@ -303,7 +274,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -354,7 +325,7 @@ dependencies = [ "calloop 0.13.0", "rustix 0.38.44", "wayland-backend", - "wayland-client 0.31.12", + "wayland-client", ] [[package]] @@ -366,7 +337,7 @@ dependencies = [ "calloop 0.14.3", "rustix 1.1.3", "wayland-backend", - "wayland-client 0.31.12", + "wayland-client", ] [[package]] @@ -377,18 +348,18 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cbindgen" -version = "0.26.0" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49" +checksum = "befbfd072a8e81c02f8c507aefce431fe5e7d051f83d48a23ffc9b9fe5a11799" dependencies = [ - "heck 0.4.1", - "indexmap 1.9.3", + "heck", + "indexmap", "log", "proc-macro2", "quote", "serde", "serde_json", - "syn 1.0.109", + "syn", "tempfile", "toml", ] @@ -514,11 +485,10 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ - "termcolor", "unicode-width", ] @@ -702,7 +672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73736a89c4aff73035ba2ed2e565061954da00d4970fc9ac25dcc85a2a20d790" dependencies = [ "dispatch2", - "nix 0.30.1", + "nix", "windows-sys 0.61.2", ] @@ -732,7 +702,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -743,7 +713,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -787,7 +757,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -822,9 +792,9 @@ checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "ecolor" -version = "0.31.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc4feb366740ded31a004a0e4452fbf84e80ef432ecf8314c485210229672fd1" +checksum = "71ddb8ac7643d1dba1bb02110e804406dd459a838efcb14011ced10556711a8e" dependencies = [ "bytemuck", "emath", @@ -832,9 +802,9 @@ dependencies = [ [[package]] name = "eframe" -version = "0.31.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0dfe0859f3fb1bc6424c57d41e10e9093fe938f426b691e42272c2f336d915c" +checksum = "457481173e6db5ca9fa2be93a58df8f4c7be639587aeb4853b526c6cf87db4e6" dependencies = [ "ahash", "bytemuck", @@ -861,16 +831,15 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "web-time", - "winapi", - "windows-sys 0.59.0", + "windows-sys 0.61.2", "winit", ] [[package]] name = "egui" -version = "0.31.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd34cec49ab55d85ebf70139cb1ccd29c977ef6b6ba4fe85489d6877ee9ef3" +checksum = "6a9b567d356674e9a5121ed3fedfb0a7c31e059fe71f6972b691bcd0bfc284e3" dependencies = [ "ahash", "bitflags 2.10.0", @@ -879,13 +848,15 @@ dependencies = [ "log", "nohash-hasher", "profiling", + "smallvec", + "unicode-segmentation", ] [[package]] name = "egui-wgpu" -version = "0.31.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d319dfef570f699b6e9114e235e862a2ddcf75f0d1a061de9e1328d92146d820" +checksum = "5e4d209971c84b2352a06174abdba701af1e552ce56b144d96f2bd50a3c91236" dependencies = [ "ahash", "bytemuck", @@ -894,7 +865,7 @@ dependencies = [ "epaint", "log", "profiling", - "thiserror 1.0.69", + "thiserror 2.0.17", "type-map", "web-time", "wgpu", @@ -903,15 +874,17 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.31.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9dfbb78fe4eb9c3a39ad528b90ee5915c252e77bbab9d4ebc576541ab67e13" +checksum = "ec6687e5bb551702f4ad10ac428bab12acf9d53047ebb1082d4a0ed8c6251a29" dependencies = [ - "ahash", "arboard", "bytemuck", "egui", "log", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-ui-kit", "profiling", "raw-window-handle", "smithay-clipboard", @@ -922,16 +895,15 @@ dependencies = [ [[package]] name = "egui_glow" -version = "0.31.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910906e3f042ea6d2378ec12a6fd07698e14ddae68aed2d819ffe944a73aab9e" +checksum = "6420863ea1d90e750f75075231a260030ad8a9f30a7cef82cdc966492dc4c4eb" dependencies = [ - "ahash", "bytemuck", "egui", "glow", "log", - "memoffset 0.9.1", + "memoffset", "profiling", "wasm-bindgen", "web-sys", @@ -946,9 +918,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "emath" -version = "0.31.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e4cadcff7a5353ba72b7fea76bf2122b5ebdbc68e8155aa56dfdea90083fe1b" +checksum = "491bdf728bf25ddd9ad60d4cf1c48588fa82c013a2440b91aa7fc43e34a07c32" dependencies = [ "bytemuck", ] @@ -985,7 +957,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -1006,7 +978,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -1028,14 +1000,14 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] name = "epaint" -version = "0.31.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fcc0f5a7c613afd2dee5e4b30c3e6acafb8ad6f0edb06068811f708a67c562" +checksum = "009d0dd3c2163823a0abdb899451ecbc78798dec545ee91b43aff1fa790bab62" dependencies = [ "ab_glyph", "ahash", @@ -1051,9 +1023,9 @@ dependencies = [ [[package]] name = "epaint_default_fonts" -version = "0.31.1" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7e7a64c02cf7a5b51e745a9e45f60660a286f151c238b9d397b3e923f5082f" +checksum = "5c4fbe202b6578d3d56428fa185cdf114a05e49da05f477b3c7f0fbb221f1862" [[package]] name = "equivalent" @@ -1121,7 +1093,7 @@ checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -1157,9 +1129,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "fontconfig-parser" @@ -1178,7 +1150,7 @@ checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", - "memmap2 0.9.9", + "memmap2", "slotmap", "tinyvec", "ttf-parser", @@ -1202,7 +1174,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -1253,7 +1225,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -1293,10 +1265,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -1306,9 +1276,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -1322,6 +1294,102 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glam" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da" + +[[package]] +name = "glam" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b" + +[[package]] +name = "glam" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16" + +[[package]] +name = "glam" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776" + +[[package]] +name = "glam" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34" + +[[package]] +name = "glam" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca" + +[[package]] +name = "glam" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f" + +[[package]] +name = "glam" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815" + +[[package]] +name = "glam" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" + +[[package]] +name = "glam" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c" + +[[package]] +name = "glam" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" + +[[package]] +name = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" + +[[package]] +name = "glam" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" + +[[package]] +name = "glam" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" + +[[package]] +name = "glam" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" + +[[package]] +name = "glam" +version = "0.30.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" + [[package]] name = "glob" version = "0.3.3" @@ -1360,7 +1428,7 @@ dependencies = [ "objc2-foundation 0.3.2", "once_cell", "raw-window-handle", - "wayland-sys 0.31.8", + "wayland-sys", "windows-sys 0.52.0", "x11-dl", ] @@ -1406,45 +1474,6 @@ dependencies = [ "gl_generator", ] -[[package]] -name = "gpu-alloc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" -dependencies = [ - "bitflags 2.10.0", - "gpu-alloc-types", -] - -[[package]] -name = "gpu-alloc-types" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" -dependencies = [ - "bitflags 2.10.0", -] - -[[package]] -name = "gpu-descriptor" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" -dependencies = [ - "bitflags 2.10.0", - "gpu-descriptor-types", - "hashbrown 0.15.5", -] - -[[package]] -name = "gpu-descriptor-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" -dependencies = [ - "bitflags 2.10.0", -] - [[package]] name = "half" version = "2.7.1" @@ -1453,36 +1482,19 @@ checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "num-traits", "zerocopy", ] [[package]] name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.15.5" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "foldhash", ] -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1631,31 +1643,21 @@ dependencies = [ [[package]] name = "imageproc" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2393fb7808960751a52e8a154f67e7dd3f8a2ef9bd80d1553078a7b4e8ed3f0d" +checksum = "3880a67ffee492100224b2ff31f4220ade971fc327c3c96faa63095a3825ea4d" dependencies = [ "ab_glyph", "approx", - "getrandom 0.2.17", + "getrandom 0.3.4", "image", - "itertools 0.12.1", + "itertools 0.14.0", "nalgebra", "num", "rand", "rand_distr", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.13.0" @@ -1663,23 +1665,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown", ] [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -1732,17 +1734,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "khronos-egl" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" -dependencies = [ - "libc", - "libloading", - "pkg-config", -] - [[package]] name = "khronos_api" version = "3.1.0" @@ -1758,7 +1749,7 @@ dependencies = [ "kime-run-dir", "kime-version", "log", - "nix 0.30.1", + "nix", "pico-args", ] @@ -1779,7 +1770,7 @@ dependencies = [ "owo-colors", "pad", "serde_yaml", - "strum 0.26.3", + "strum", "xdg", ] @@ -1791,7 +1782,7 @@ dependencies = [ "enum-map", "enumset", "serde", - "strum 0.27.2", + "strum", ] [[package]] @@ -1848,7 +1839,7 @@ name = "kime-engine-candidate" version = "0.1.0" dependencies = [ "kime-engine-dict", - "nix 0.30.1", + "nix", ] [[package]] @@ -1898,8 +1889,8 @@ dependencies = [ name = "kime-engine-dict" version = "0.1.0" dependencies = [ - "itertools 0.13.0", - "quick-xml 0.27.1", + "itertools 0.14.0", + "quick-xml 0.39.0", "serde", "serde_json", "unicode-properties", @@ -1949,10 +1940,10 @@ dependencies = [ "mio", "pico-args", "timerfd-mio", - "wayland-client 0.29.5", - "wayland-protocols 0.29.5", + "wayland-client", + "wayland-protocols", + "wayland-protocols-misc", "xkbcommon", - "zwp-virtual-keyboard", ] [[package]] @@ -1984,18 +1975,6 @@ dependencies = [ "zbus", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.180" @@ -2071,15 +2050,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "maplit" version = "1.0.2" @@ -2102,15 +2072,6 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" -[[package]] -name = "memmap2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" -dependencies = [ - "libc", -] - [[package]] name = "memmap2" version = "0.9.9" @@ -2120,15 +2081,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -2138,21 +2090,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "metal" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" -dependencies = [ - "bitflags 2.10.0", - "block", - "core-graphics-types", - "foreign-types", - "log", - "objc", - "paste", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2193,33 +2130,52 @@ dependencies = [ [[package]] name = "naga" -version = "24.0.0" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" +checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" dependencies = [ "arrayvec", "bit-set", "bitflags 2.10.0", + "cfg-if", "cfg_aliases", "codespan-reporting", + "half", + "hashbrown", "hexf-parse", - "indexmap 2.13.0", + "indexmap", + "libm", "log", + "num-traits", + "once_cell", "rustc-hash 1.1.0", - "spirv", - "strum 0.26.3", - "termcolor", "thiserror 2.0.17", - "unicode-xid", + "unicode-ident", ] [[package]] name = "nalgebra" -version = "0.32.6" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" +checksum = "c4d5b3eff5cd580f93da45e64715e8c20a3996342f1e466599cf7a267a0c2f5f" dependencies = [ "approx", + "glam 0.14.0", + "glam 0.15.2", + "glam 0.16.0", + "glam 0.17.3", + "glam 0.18.0", + "glam 0.19.0", + "glam 0.20.5", + "glam 0.21.3", + "glam 0.22.0", + "glam 0.23.0", + "glam 0.24.2", + "glam 0.25.0", + "glam 0.27.0", + "glam 0.28.0", + "glam 0.29.3", + "glam 0.30.10", "matrixmultiply", "num-complex", "num-rational", @@ -2237,7 +2193,7 @@ dependencies = [ "bitflags 2.10.0", "jni-sys", "log", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "raw-window-handle", "thiserror 1.0.69", @@ -2249,15 +2205,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "ndk-sys" -version = "0.5.0+25.2.9519653" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" -dependencies = [ - "jni-sys", -] - [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -2267,18 +2214,6 @@ dependencies = [ "jni-sys", ] -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - [[package]] name = "nix" version = "0.30.1" @@ -2313,7 +2248,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ - "num-bigint", "num-complex", "num-integer", "num-iter", @@ -2354,7 +2288,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -2417,7 +2351,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -2429,15 +2363,6 @@ dependencies = [ "libc", ] -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - [[package]] name = "objc-sys" version = "0.3.5" @@ -2731,15 +2656,6 @@ dependencies = [ "libredox", ] -[[package]] -name = "ordered-float" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" -dependencies = [ - "num-traits", -] - [[package]] name = "ordered-stream" version = "0.2.0" @@ -2854,7 +2770,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -2930,6 +2846,21 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "portable-atomic" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -3013,20 +2944,20 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.27.1" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc053f057dd768a56f62cd7e434c42c831d296968997e9ac1f76ea7c2d14c41" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ - "encoding_rs", "memchr", ] [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "f2e3bf4aa9d243beeb01a7b3bc30b77cfe2c44e24ec02d751a7104a53c2c49a1" dependencies = [ + "encoding_rs", "memchr", ] @@ -3047,20 +2978,19 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" -version = "0.8.5" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", @@ -3068,18 +2998,18 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.3.4", ] [[package]] name = "rand_distr" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ "num-traits", "rand", @@ -3292,7 +3222,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -3316,7 +3246,16 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", ] [[package]] @@ -3325,7 +3264,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.13.0", + "indexmap", "itoa", "ryu", "serde", @@ -3350,9 +3289,9 @@ dependencies = [ [[package]] name = "simba" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95" dependencies = [ "approx", "num-complex", @@ -3411,16 +3350,16 @@ dependencies = [ "cursor-icon", "libc", "log", - "memmap2 0.9.9", + "memmap2", "rustix 0.38.44", "thiserror 1.0.69", "wayland-backend", - "wayland-client 0.31.12", + "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.10", + "wayland-protocols", "wayland-protocols-wlr", - "wayland-scanner 0.31.8", + "wayland-scanner", "xkeysym", ] @@ -3436,18 +3375,18 @@ dependencies = [ "cursor-icon", "libc", "log", - "memmap2 0.9.9", + "memmap2", "rustix 1.1.3", "thiserror 2.0.17", "wayland-backend", - "wayland-client 0.31.12", + "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.10", + "wayland-protocols", "wayland-protocols-experimental", "wayland-protocols-misc", "wayland-protocols-wlr", - "wayland-scanner 0.31.8", + "wayland-scanner", "xkeysym", ] @@ -3481,15 +3420,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "spirv" -version = "0.3.0+sdk-1.3.268.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" -dependencies = [ - "bitflags 2.10.0", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3502,35 +3432,13 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - [[package]] name = "strum" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.114", + "strum_macros", ] [[package]] @@ -3539,21 +3447,10 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ + "heck", "proc-macro2", "quote", - "unicode-ident", + "syn", ] [[package]] @@ -3575,7 +3472,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -3626,7 +3523,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -3637,7 +3534,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -3767,16 +3664,22 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] name = "toml" -version = "0.5.11" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ - "serde", + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] @@ -3794,7 +3697,7 @@ version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.13.0", + "indexmap", "toml_datetime", "toml_parser", "winnow", @@ -3809,6 +3712,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + [[package]] name = "tracing" version = "0.1.44" @@ -3829,7 +3738,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -3871,7 +3780,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ - "memoffset 0.9.1", + "memoffset", "tempfile", "winapi", ] @@ -3900,12 +3809,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -4018,7 +3921,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn", "wasm-bindgen-shared", ] @@ -4042,22 +3945,7 @@ dependencies = [ "rustix 1.1.3", "scoped-tls", "smallvec", - "wayland-sys 0.31.8", -] - -[[package]] -name = "wayland-client" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" -dependencies = [ - "bitflags 1.3.2", - "downcast-rs", - "libc", - "nix 0.24.3", - "wayland-commons", - "wayland-scanner 0.29.5", - "wayland-sys 0.29.5", + "wayland-sys", ] [[package]] @@ -4069,19 +3957,7 @@ dependencies = [ "bitflags 2.10.0", "rustix 1.1.3", "wayland-backend", - "wayland-scanner 0.31.8", -] - -[[package]] -name = "wayland-commons" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" -dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys 0.29.5", + "wayland-scanner", ] [[package]] @@ -4102,22 +3978,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5864c4b5b6064b06b1e8b74ead4a98a6c45a285fe7a0e784d24735f011fdb078" dependencies = [ "rustix 1.1.3", - "wayland-client 0.31.12", + "wayland-client", "xcursor", ] -[[package]] -name = "wayland-protocols" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" -dependencies = [ - "bitflags 1.3.2", - "wayland-client 0.29.5", - "wayland-commons", - "wayland-scanner 0.29.5", -] - [[package]] name = "wayland-protocols" version = "0.32.10" @@ -4126,8 +3990,8 @@ checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" dependencies = [ "bitflags 2.10.0", "wayland-backend", - "wayland-client 0.31.12", - "wayland-scanner 0.31.8", + "wayland-client", + "wayland-scanner", ] [[package]] @@ -4138,9 +4002,9 @@ checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ "bitflags 2.10.0", "wayland-backend", - "wayland-client 0.31.12", - "wayland-protocols 0.32.10", - "wayland-scanner 0.31.8", + "wayland-client", + "wayland-protocols", + "wayland-scanner", ] [[package]] @@ -4151,9 +4015,9 @@ checksum = "791c58fdeec5406aa37169dd815327d1e47f334219b523444bc26d70ceb4c34e" dependencies = [ "bitflags 2.10.0", "wayland-backend", - "wayland-client 0.31.12", - "wayland-protocols 0.32.10", - "wayland-scanner 0.31.8", + "wayland-client", + "wayland-protocols", + "wayland-scanner", ] [[package]] @@ -4164,9 +4028,9 @@ checksum = "aa98634619300a535a9a97f338aed9a5ff1e01a461943e8346ff4ae26007306b" dependencies = [ "bitflags 2.10.0", "wayland-backend", - "wayland-client 0.31.12", - "wayland-protocols 0.32.10", - "wayland-scanner 0.31.8", + "wayland-client", + "wayland-protocols", + "wayland-scanner", ] [[package]] @@ -4177,20 +4041,9 @@ checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" dependencies = [ "bitflags 2.10.0", "wayland-backend", - "wayland-client 0.31.12", - "wayland-protocols 0.32.10", - "wayland-scanner 0.31.8", -] - -[[package]] -name = "wayland-scanner" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" -dependencies = [ - "proc-macro2", - "quote", - "xml-rs", + "wayland-client", + "wayland-protocols", + "wayland-scanner", ] [[package]] @@ -4204,15 +4057,6 @@ dependencies = [ "quote", ] -[[package]] -name = "wayland-sys" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" -dependencies = [ - "pkg-config", -] - [[package]] name = "wayland-sys" version = "0.31.8" @@ -4269,24 +4113,22 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "24.0.5" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0b3436f0729f6cdf2e6e9201f3d39dc95813fad61d826c1ed07918b4539353" +checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" dependencies = [ "arrayvec", "bitflags 2.10.0", + "cfg-if", "cfg_aliases", "document-features", - "js-sys", + "hashbrown", "log", - "parking_lot", + "portable-atomic", "profiling", "raw-window-handle", "smallvec", "static_assertions", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", "wgpu-core", "wgpu-hal", "wgpu-types", @@ -4294,79 +4136,74 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "24.0.5" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f0aa306497a238d169b9dc70659105b4a096859a34894544ca81719242e1499" +checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" dependencies = [ "arrayvec", + "bit-set", "bit-vec", "bitflags 2.10.0", + "bytemuck", "cfg_aliases", "document-features", - "indexmap 2.13.0", + "hashbrown", + "indexmap", "log", "naga", "once_cell", "parking_lot", + "portable-atomic", "profiling", "raw-window-handle", "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.17", + "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-types", ] +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "27.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +dependencies = [ + "wgpu-hal", +] + [[package]] name = "wgpu-hal" -version = "24.0.4" +version = "27.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f112f464674ca69f3533248508ee30cb84c67cf06c25ff6800685f5e0294e259" +checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" dependencies = [ - "android_system_properties", - "arrayvec", - "ash", "bitflags 2.10.0", - "bytemuck", + "cfg-if", "cfg_aliases", - "core-graphics-types", - "glow", - "glutin_wgl_sys", - "gpu-alloc", - "gpu-descriptor", - "js-sys", - "khronos-egl", - "libc", "libloading", "log", - "metal", "naga", - "ndk-sys 0.5.0+25.2.9519653", - "objc", - "once_cell", - "ordered-float", - "parking_lot", - "profiling", + "portable-atomic", + "portable-atomic-util", "raw-window-handle", "renderdoc-sys", - "rustc-hash 1.1.0", - "smallvec", "thiserror 2.0.17", - "wasm-bindgen", - "web-sys", "wgpu-types", - "windows", ] [[package]] name = "wgpu-types" -version = "24.0.0" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" +checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" dependencies = [ "bitflags 2.10.0", + "bytemuck", "js-sys", "log", + "thiserror 2.0.17", "web-sys", ] @@ -4411,76 +4248,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.45.0" @@ -4733,7 +4506,7 @@ dependencies = [ "dpi", "js-sys", "libc", - "memmap2 0.9.9", + "memmap2", "ndk", "objc2 0.5.2", "objc2-app-kit 0.2.2", @@ -4752,8 +4525,8 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "wayland-backend", - "wayland-client 0.31.12", - "wayland-protocols 0.32.10", + "wayland-client", + "wayland-protocols", "wayland-protocols-plasma", "web-sys", "web-time", @@ -4824,9 +4597,9 @@ checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" [[package]] name = "xdg" -version = "2.5.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" +checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5" [[package]] name = "xim" @@ -4835,7 +4608,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb59e6a1a1e0a88e15f870621fa98e8a840f736db8d852ff4e254d76dc75fe26" dependencies = [ "ahash", - "hashbrown 0.16.1", + "hashbrown", "log", "x11rb", "xim-ctext", @@ -4862,12 +4635,12 @@ dependencies = [ [[package]] name = "xkbcommon" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" dependencies = [ "libc", - "memmap2 0.8.0", + "memmap2", "xkeysym", ] @@ -4921,7 +4694,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", "synstructure", ] @@ -4964,7 +4737,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn", "zbus_names", "zvariant", "zvariant_utils", @@ -4998,7 +4771,7 @@ checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -5018,7 +4791,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", "synstructure", ] @@ -5052,7 +4825,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn", ] [[package]] @@ -5099,7 +4872,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn", "zvariant_utils", ] @@ -5112,17 +4885,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.114", + "syn", "winnow", ] - -[[package]] -name = "zwp-virtual-keyboard" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b449405e5c49d10ab71187d3338366625340263d3fb061944ae3c94a639a9a" -dependencies = [ - "wayland-client 0.29.5", - "wayland-commons", - "wayland-scanner 0.29.5", -] diff --git a/default.nix b/default.nix index c7ace09f..c71a6e8b 100644 --- a/default.nix +++ b/default.nix @@ -1,5 +1,6 @@ { pkgs ? import { }, + rustToolchain ? pkgs.rustc, debug ? false, }: let @@ -13,7 +14,7 @@ llvmPackages_18.stdenv.mkDerivation { name = "kime"; inherit src; buildInputs = deps.kimeBuildInputs; - nativeBuildInputs = deps.kimeNativeBuildInputs ++ [ rustPlatform.cargoSetupHook ]; + nativeBuildInputs = deps.kimeNativeBuildInputs ++ [ rustToolchain rustPlatform.cargoSetupHook ]; version = kimeVersion; cargoDeps = rustPlatform.fetchCargoTarball { inherit src; diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index cdf592ff..dcf39944 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,14 +14,23 @@ * Add Opensuse Build Service repository and modify README * fix(xim): handle None from from_hardware_code without panic [#721](https://github.com/Riey/kime/721) * Update dependencies: + - wayland-client 0.29 → 0.31, wayland-protocols 0.29 → 0.32 + - wayland-protocols-misc 0.3 (신규), xdg 2.5 → 3.0, quick-xml 0.27 → 0.39 + - xkbcommon 0.7 → 0.9, ksni 0.2 → 0.3.3 + tokio - bitflags 2.10, nix 0.30, strum 0.27 - - x11rb 0.13, xim 0.5, image 0.25, imageproc 0.25 - - mio 1.0 with timerfd-mio - - egui/eframe 0.33 + - x11rb 0.13, xim 0.5, image 0.25, imageproc 0.26 + - mio 1.0 with timerfd-mio, egui/eframe 0.33, itertools 0.14 + - bindgen 0.72.1, cbindgen 0.29.2 - Replace rusttype with ab_glyph - Replace ansi_term with owo-colors - Replace daemonize with nix daemon - Replace unic with unicode-properties +* Remove unused dependencies: + - Remove zwp-virtual-keyboard (merged into wayland-protocols-misc) +* Fix kime-wayland crash on KDE Plasma 6.5.5 by handling KeyState::Repeated +* Fix indicator tray icon not showing/updating on KDE with tokio async I/O (ksni 0.3.3) +* Rewrite kime-wayland for wayland-rs 0.31 API (Dispatch trait pattern) + ## 3.1.1 diff --git a/flake.lock b/flake.lock index 8e48b5cc..759ff5dc 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1747505133, - "narHash": "sha256-ONJblEhnYJGctOd2z2AmHBp//OKajQL+yUDoFAWQcvo=", + "lastModified": 1769049916, + "narHash": "sha256-RcsY0BtjUhRuA1SRcqMrVpWC8cQkfCzhVcYdiBzDxNw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c5cac24ea8edf485ff43144c8494c5309b613546", + "rev": "47f3deb4bc905ce0de12ba7bd6ad002c132acd02", "type": "github" }, "original": { @@ -33,10 +33,45 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1744536153, + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1769050281, + "narHash": "sha256-1H8DN4UZgEUqPUA5ecHOufLZMscJ4IlcGaEftaPtpBY=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "6deef0585c52d9e70f96b6121207e1496d4b0c49", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" } }, "systems": { diff --git a/flake.nix b/flake.nix index 3b6c4c82..5f31c5d2 100644 --- a/flake.nix +++ b/flake.nix @@ -3,18 +3,23 @@ inputs = { nixpkgs.url = github:NixOS/nixpkgs; + rust-overlay.url = github:oxalica/rust-overlay; flake-utils.url = github:numtide/flake-utils; }; - outputs = { self, nixpkgs, flake-utils }: + outputs = { self, nixpkgs, rust-overlay, flake-utils }: flake-utils.lib.eachDefaultSystem (system: - let pkgs = import nixpkgs { - inherit system; - }; in + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + rustToolchain = pkgs.rust-bin.stable.latest.default; + in { - devShells.default = import ./shell.nix { inherit pkgs; }; - packages.default = import ./default.nix { inherit pkgs; }; + devShells.default = import ./shell.nix { inherit pkgs rustToolchain; }; + packages.default = import ./default.nix { inherit pkgs rustToolchain; }; } ); } diff --git a/nix/deps.nix b/nix/deps.nix index ef8dcac0..fc3b8f5e 100644 --- a/nix/deps.nix +++ b/nix/deps.nix @@ -22,7 +22,6 @@ llvmPackages_18.clang llvmPackages_18.libclang.lib llvmPackages_18.bintools - rustc cargo cmake extra-cmake-modules ]; diff --git a/shell.nix b/shell.nix index cdd7877a..b549c8f2 100644 --- a/shell.nix +++ b/shell.nix @@ -1,5 +1,6 @@ { pkgs ? import { }, + rustToolchain ? pkgs.rustc, }: let deps = import ./nix/deps.nix { inherit pkgs; }; @@ -11,11 +12,11 @@ pkgs.mkShell { dontUseCmakeConfigure = true; dontWrapQtApps = true; buildInputs = deps.kimeBuildInputs; - nativeBuildInputs = deps.kimeNativeBuildInputs ++ (with pkgs; [ - rustfmt + nativeBuildInputs = deps.kimeNativeBuildInputs ++ [ + rustToolchain pkgs.gedit - llvmPackages_18.lldb - ]); + pkgs.llvmPackages_18.lldb + ]; CMAKE_EXPORT_COMPILE_COMMANDS = 1; LIBCLANG_PATH = "${pkgs.llvmPackages_18.libclang.lib}/lib"; LD_LIBRARY_PATH = "./target/debug:${pkgs.wayland}/lib:${pkgs.libGL}/lib:${pkgs.libxkbcommon}/lib"; diff --git a/src/engine/backends/hangul/Cargo.toml b/src/engine/backends/hangul/Cargo.toml index 8b8e857f..820ec0cf 100644 --- a/src/engine/backends/hangul/Cargo.toml +++ b/src/engine/backends/hangul/Cargo.toml @@ -14,4 +14,4 @@ serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" [target.'cfg(unix)'.dependencies] -xdg = "2.5" +xdg = "3.0" diff --git a/src/engine/capi/Cargo.toml b/src/engine/capi/Cargo.toml index 44bd1846..ce0649c4 100644 --- a/src/engine/capi/Cargo.toml +++ b/src/engine/capi/Cargo.toml @@ -14,5 +14,5 @@ crate-type = ["cdylib"] kime-engine-core = { path = "../core" } [build-dependencies] -bindgen = { version = "0.69.4", default-features = false } -cbindgen = { version = "0.26.0", default-features = false } +bindgen = { version = "0.72.1", default-features = false } +cbindgen = { version = "0.29.2", default-features = false } diff --git a/src/engine/core/Cargo.toml b/src/engine/core/Cargo.toml index a19a901c..76eb2eaf 100644 --- a/src/engine/core/Cargo.toml +++ b/src/engine/core/Cargo.toml @@ -18,7 +18,7 @@ parking_lot = "0.12" fontdb = { version = "0.23", features = ["fontconfig"] } [target.'cfg(unix)'.dependencies] -xdg = "2.5" +xdg = "3.0" kime-run-dir = { path = "../../tools/run_dir" } [dev-dependencies] diff --git a/src/engine/core/src/config.rs b/src/engine/core/src/config.rs index 01a1bdff..5d927efe 100644 --- a/src/engine/core/src/config.rs +++ b/src/engine/core/src/config.rs @@ -40,11 +40,7 @@ impl Config { #[cfg(unix)] let translation_layer: Option> = engine .translation_layer - .and_then(|f| { - xdg::BaseDirectories::with_prefix("kime") - .ok() - .and_then(|d| d.find_config_file(f)) - }) + .and_then(|f| xdg::BaseDirectories::with_prefix("kime").find_config_file(f)) .as_ref() .and_then(|f| fs::read_to_string(f.as_path()).ok()) .as_ref() @@ -112,16 +108,16 @@ impl Config { #[cfg(unix)] pub fn load_raw_config_from_config_dir() -> RawConfig { - let dir = xdg::BaseDirectories::with_prefix("kime").ok(); + let dir = xdg::BaseDirectories::with_prefix("kime"); - dir.and_then(|dir| dir.find_config_file("config.yaml")) + dir.find_config_file("config.yaml") .and_then(|config| serde_yaml::from_reader(std::fs::File::open(config).ok()?).ok()) .unwrap_or_default() } #[cfg(unix)] pub fn load_engine_config_from_config_dir() -> Option { - let dir = xdg::BaseDirectories::with_prefix("kime").ok()?; + let dir = xdg::BaseDirectories::with_prefix("kime"); let config: RawConfig = dir .find_config_file("config.yaml") .and_then(|config| serde_yaml::from_reader(std::fs::File::open(config).ok()?).ok()) diff --git a/src/engine/core/src/os.rs b/src/engine/core/src/os.rs index 78801de0..3ee74893 100644 --- a/src/engine/core/src/os.rs +++ b/src/engine/core/src/os.rs @@ -60,6 +60,7 @@ mod unix { } } +#[cfg(not(unix))] mod fallback { use crate::InputCategory; use std::io; diff --git a/src/engine/dict/Cargo.toml b/src/engine/dict/Cargo.toml index 25030a26..bf185ebc 100644 --- a/src/engine/dict/Cargo.toml +++ b/src/engine/dict/Cargo.toml @@ -8,6 +8,6 @@ license = "GPL-3.0-or-later" [build-dependencies] serde = {version = "1.0.118", features = ["derive"]} serde_json = "1.0" -itertools = "0.13.0" -quick-xml = { version = "0.27.1", features = ["encoding"] } +itertools = "0.14.0" +quick-xml = { version = "0.39.0", features = ["encoding"] } unicode-properties = { version = "0.1", features = ["emoji"] } diff --git a/src/engine/dict/build.rs b/src/engine/dict/build.rs index ac665a52..79d56045 100644 --- a/src/engine/dict/build.rs +++ b/src/engine/dict/build.rs @@ -133,7 +133,7 @@ fn load_unicode_annotations() -> quick_xml::Result> { Event::Start(start) if start.name().0 == b"annotation" => { let cp = start.attributes().next().unwrap()?; debug_assert_eq!(cp.key.0, b"cp"); - let cp = cp.decode_and_unescape_value(&reader)?; + let cp = cp.decode_and_unescape_value(reader.decoder())?; if current_entry.cp != cp { if !current_entry.cp.is_empty() { out.push(mem::take(&mut current_entry)); diff --git a/src/frontends/wayland/Cargo.toml b/src/frontends/wayland/Cargo.toml index af909b25..8260acb9 100644 --- a/src/frontends/wayland/Cargo.toml +++ b/src/frontends/wayland/Cargo.toml @@ -9,13 +9,13 @@ license = "GPL-3.0-or-later" kime-engine-core = { path = "../../engine/core" } kime-version = { path = "../../tools/version" } -wayland-client = "0.29" -wayland-protocols = { version = "0.29", features = [ +wayland-client = "0.31" +wayland-protocols = { version = "0.32", features = [ "client", - "unstable_protocols", + "unstable", ] } -zwp-virtual-keyboard = "0.2.7" -xkbcommon = { version = "0.7.0", features = ["wayland"] } +wayland-protocols-misc = { version = "0.3", features = ["client"] } +xkbcommon = { version = "0.9.0", features = ["wayland"] } libc = "0.2" log = "0.4" diff --git a/src/frontends/wayland/src/input_method_v1.rs b/src/frontends/wayland/src/input_method_v1.rs deleted file mode 100644 index 6461a333..00000000 --- a/src/frontends/wayland/src/input_method_v1.rs +++ /dev/null @@ -1,509 +0,0 @@ -use std::error::Error; -use std::os::fd::{FromRawFd, OwnedFd}; -use std::time::{Duration, Instant}; - -use kime_engine_core::{ - load_engine_config_from_config_dir, Config, InputEngine, InputResult, Key, KeyCode, - ModifierState, -}; -use wayland_client::{ - event_enum, - protocol::wl_keyboard::{Event as KeyEvent, KeyState, WlKeyboard, REQ_RELEASE_SINCE}, - DispatchData, Display, EventQueue, Filter, GlobalManager, Main, -}; - -use wayland_protocols::unstable::input_method::v1::client::{ - zwp_input_method_context_v1::{Event as ImCtxEvent, ZwpInputMethodContextV1}, - zwp_input_method_v1::{Event as ImEvent, ZwpInputMethodV1}, -}; - -use mio::{unix::SourceFd, Events as MioEvents, Interest, Poll, Token}; -use timerfd_mio::TimerFd; -use wayland_client::protocol::wl_keyboard::KeymapFormat; -use xkbcommon::xkb::{ - Context, Keycode, Keymap, CONTEXT_NO_FLAGS, KEYMAP_COMPILE_NO_FLAGS, KEYMAP_FORMAT_TEXT_V1, -}; - -use crate::{PressState, RepeatInfo}; - -event_enum! { - Events | - Im => ZwpInputMethodV1, - ImCtx => ZwpInputMethodContextV1, - Key => WlKeyboard -} - -const ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_NONE: u32 = 1; -const ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE: u32 = 5; - -struct KimeContext { - config: Config, - engine: InputEngine, - mod_state: ModifierState, - im_ctx: Option>, - keyboard: Option>, - numlock: bool, - engine_ready: bool, - keymap: Option, - grab_activate: bool, //? - serial: u32, - timer: TimerFd, - /// `None` if `KimeContext` have never received a `RepeatInfo` or repeat is disabled (i.e. rate - /// is zero). `Some(..)` if `RepeatInfo` is known and kime-wayland started tracking the press - /// state of keys. - repeat_state: Option<(RepeatInfo, PressState)>, - last_preedit_len: usize, -} - -impl Drop for KimeContext { - fn drop(&mut self) { - if let Some(im_ctx) = &mut self.im_ctx { - im_ctx.destroy(); - } - } -} - -impl KimeContext { - pub fn new(timer: TimerFd) -> Self { - let config = load_engine_config_from_config_dir().unwrap_or_default(); - Self { - engine: InputEngine::new(&config), - config, - mod_state: ModifierState::empty(), - serial: 0, - numlock: false, - engine_ready: true, - keymap: None, - grab_activate: false, - im_ctx: None, - keyboard: None, - timer, - // Clients with older protocols might not provide repeat info. - // Therefore a default value is required. - repeat_state: Some(( - RepeatInfo { - rate: 20, - delay: 400, - }, - PressState::NotPressing, - )), - last_preedit_len: 0, - } - } - - pub fn new_data<'a>(data: &'a mut DispatchData) -> &'a mut Self { - data.get::().unwrap() - } - - fn process_input_result(&mut self, ret: InputResult) -> bool { - if ret.contains(InputResult::NOT_READY) { - self.engine_ready = false; - } - - if ret.contains(InputResult::LANGUAGE_CHANGED) { - self.engine.update_layout_state().ok(); - } - - if ret.contains(InputResult::HAS_COMMIT) { - self.commit_string(self.engine.commit_str().into()); - self.engine.clear_commit(); - } - - if ret.contains(InputResult::HAS_PREEDIT) { - let preedit = self.engine.preedit_str().into(); - self.preedit(preedit); - } else { - self.clear_preedit(); - } - - !ret.contains(InputResult::CONSUMED) - } - - fn commit_string(&mut self, s: String) { - if !s.is_empty() { - if let Some(im_ctx) = &mut self.im_ctx { - im_ctx.commit_string(self.serial, s); - } - } - } - - fn clear_preedit(&mut self) { - if let Some(im_ctx) = &mut self.im_ctx { - if self.last_preedit_len > 0 { - im_ctx.preedit_cursor(0); - im_ctx.preedit_styling(0, 0, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_NONE); - im_ctx.preedit_string(self.serial, String::new(), String::new()); - self.last_preedit_len = 0; - } - } - } - - fn preedit(&mut self, s: String) { - if let Some(im_ctx) = &mut self.im_ctx { - self.last_preedit_len = s.len(); - im_ctx.preedit_cursor(s.len() as _); - im_ctx.preedit_styling(0, s.len() as _, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE); - im_ctx.preedit_string(self.serial, s.clone(), s); - } - } - - fn key(&mut self, time: u32, key: u32, state: KeyState) { - if let Some(im_ctx) = &mut self.im_ctx { - im_ctx.key(self.serial, time, key, state as _); - } - } - - pub fn handle_im_ctx_ev(&mut self, ev: ImCtxEvent) { - match ev { - ImCtxEvent::CommitState { serial } => { - self.serial = serial; - } - _ => {} - } - } - - pub fn handle_key_ev(&mut self, ev: KeyEvent) { - match ev { - KeyEvent::Keymap { format, fd, size } => { - if let KeymapFormat::XkbV1 = format { - unsafe { - self.keymap = Keymap::new_from_fd( - &Context::new(CONTEXT_NO_FLAGS), - OwnedFd::from_raw_fd(fd), - size as usize, - KEYMAP_FORMAT_TEXT_V1, - KEYMAP_COMPILE_NO_FLAGS, - ) - .unwrap_or(None); - } - } else { - unsafe { - libc::close(fd); - } - } - } - KeyEvent::Key { - time, key, state, .. - } => { - if state == KeyState::Pressed { - if self.grab_activate { - let hwcode = (key + 8) as u16; - if let Some(code) = KeyCode::from_hardware_code(hwcode, self.numlock) { - let ret = self - .engine - .press_key(Key::new(code, self.mod_state), &self.config); - - let bypassed = self.process_input_result(ret); - - if bypassed { - self.key(time, key, state); - } else { - // If the key was not bypassed by IME, key repeat should be handled by the - // IME. Start waiting for the key hold timer event. - match self.repeat_state { - Some((info, ref mut press_state)) - if !press_state.is_pressing(key) => - { - let duration = Duration::from_millis(info.delay as u64); - if let Err(e) = self.timer.set_timeout_oneshot(duration) { - log::warn!("failed to set repeat timer: {}", e); - } - *press_state = PressState::Pressing { - pressed_at: Instant::now(), - is_repeating: false, - key, - wayland_time: time, - } - } - _ => {} - } - } - } else { - self.key(time, key, state); - return; - } - } else { - self.key(time, key, state); - } - } else { - if let Some((.., ref mut press_state)) = self.repeat_state { - if press_state.is_pressing(key) { - let _ = self.timer.set_timeout_oneshot(Duration::ZERO); - *press_state = PressState::NotPressing; - } - } - - self.key(time, key, state); - } - } - KeyEvent::Modifiers { - mods_depressed, - mods_latched, - mods_locked, - group, - .. - } => { - self.mod_state = ModifierState::empty(); - if mods_depressed & 0x1 != 0 { - self.mod_state |= ModifierState::SHIFT; - } - if mods_depressed & 0x4 != 0 { - self.mod_state |= ModifierState::CONTROL; - } - if mods_depressed & 0x8 != 0 { - self.mod_state |= ModifierState::ALT; - } - if mods_depressed & 0x40 != 0 { - self.mod_state |= ModifierState::SUPER; - } - - self.numlock = mods_depressed & 0x10 != 0; - - if let Some(im_ctx) = &mut self.im_ctx { - im_ctx.modifiers( - self.serial, - mods_depressed, - mods_latched, - mods_locked, - group, - ); - } - } - KeyEvent::RepeatInfo { rate, delay } => { - self.repeat_state = if rate == 0 { - None - } else { - let info = RepeatInfo { rate, delay }; - let press_state = self.repeat_state.map(|pair| pair.1); - Some((info, press_state.unwrap_or(PressState::NotPressing))) - } - } - _ => {} - } - } - - pub fn handle_timer_ev(&mut self) -> std::io::Result<()> { - // Read timer, this MUST be called or timer will be broken - let overrun_count = self.timer.read()?; - if overrun_count == 0 { - // Non-blocking read returned no expirations, skip processing - return Ok(()); - } - if overrun_count != 1 { - log::warn!("Some timer events were not properly handled!"); - } - - if let Some(( - info, - PressState::Pressing { - pressed_at, - ref mut is_repeating, - key, - wayland_time, - }, - )) = self.repeat_state - { - if !*is_repeating { - if self - .keymap - .as_ref() - .map_or_else(|| true, |x| x.key_repeats(Keycode::new(key + 8))) - { - // Start repeat - log::trace!("Start repeating {}", key); - let interval = Duration::from_secs_f64(1.0 / info.rate as f64); - self.timer.set_timeout_interval(interval, interval)?; - *is_repeating = true; - } - } - - let ev = KeyEvent::Key { - serial: self.serial, // Is this fine? - time: wayland_time + pressed_at.elapsed().as_millis() as u32, - key, - state: KeyState::Pressed, - }; - self.handle_key_ev(ev); - } else { - log::warn!("Received timer event when it has never received RepeatInfo."); - } - Ok(()) - } - - pub fn activate(&mut self, im_ctx: Main, keyboard: Main) { - self.engine.update_layout_state().ok(); - if !self.engine_ready { - if self.engine.check_ready() { - let ret = self.engine.end_ready(); - self.process_input_result(ret); - self.engine_ready = true; - } - } - self.grab_activate = true; - - let filter = Filter::new(|ev, _filter, mut data| { - let ctx = KimeContext::new_data(&mut data); - - match ev { - Events::ImCtx { event, .. } => { - ctx.handle_im_ctx_ev(event); - } - Events::Key { event, .. } => { - ctx.handle_key_ev(event); - } - _ => {} - } - }); - - im_ctx.assign(filter.clone()); - keyboard.assign(filter); - - self.im_ctx = Some(im_ctx); - self.keyboard = Some(keyboard); - } - - pub fn deactivate(&mut self) { - // Focus lost, reset states - if self.engine_ready { - self.engine.reset(); - } - self.grab_activate = false; - - // Input deactivated, stop repeating - let _ = self.timer.set_timeout_oneshot(Duration::ZERO); - if let Some((_, ref mut press_state)) = self.repeat_state { - *press_state = PressState::NotPressing - } - - if let Some(im_ctx) = &mut self.im_ctx { - im_ctx.destroy(); - } - self.im_ctx = None; - - if let Some(keyboard) = &mut self.keyboard { - if keyboard.as_ref().version() >= REQ_RELEASE_SINCE { - keyboard.release(); - } - } - self.keyboard = None; - } -} - -pub fn run( - display: &Display, - event_queue: &mut EventQueue, - globals: &GlobalManager, -) -> Result<(), Box> { - let im_filter = Filter::new(|ev, _filter, mut data| { - let ctx = KimeContext::new_data(&mut data); - match ev { - Events::Im { event, .. } => match event { - ImEvent::Activate { id: im_ctx } => { - let keyboard = im_ctx.grab_keyboard(); - ctx.activate(im_ctx, keyboard); - } - ImEvent::Deactivate { .. } => { - ctx.deactivate(); - } - _ => {} - }, - _ => {} - } - }); - - let im = globals.instantiate_exact::(1)?; - im.assign(im_filter); - - let mut timer = TimerFd::new().expect("Initialize timer"); - - let mut poll = Poll::new().expect("Initialize epoll()"); - let registry = poll.registry(); - - const POLL_WAYLAND: Token = Token(0); - registry - .register( - &mut SourceFd(&display.get_connection_fd()), - POLL_WAYLAND, - Interest::READABLE | Interest::WRITABLE, - ) - .expect("Register wayland socket to the epoll()"); - - const POLL_TIMER: Token = Token(1); - registry - .register(&mut timer, POLL_TIMER, Interest::READABLE) - .expect("Register timer to the epoll()"); - - // Initialize kime context - let mut kime_ctx = KimeContext::new(timer); - event_queue - .sync_roundtrip(&mut kime_ctx, |_, _, _| ()) - .unwrap(); - - log::info!("Server init success!"); - - // Non-blocking event loop - // - // Reference: - // https://docs.rs/wayland-client/0.28.3/wayland_client/struct.EventQueue.html - let mut events = MioEvents::with_capacity(1024); - let stop_reason = 'main: loop { - use std::io::ErrorKind; - - // Sleep until next event - if let Err(e) = poll.poll(&mut events, None) { - // Should retry on EINTR - // - // Reference: - // https://www.gnu.org/software/libc/manual/html_node/Interrupted-Primitives.html - if e.kind() == ErrorKind::Interrupted { - continue; - } - break Err(e); - } - - for event in &events { - match event.token() { - POLL_WAYLAND => {} - POLL_TIMER => { - if let Err(e) = kime_ctx.handle_timer_ev() { - break 'main Err(e); - } - } - _ => unreachable!(), - } - } - - // Perform read() only when it's ready, returns None when there're already pending events - if let Some(guard) = event_queue.prepare_read() { - if let Err(e) = guard.read_events() { - // EWOULDBLOCK here means there's no new messages to read - if e.kind() != ErrorKind::WouldBlock { - break Err(e); - } - } - } - - if let Err(e) = event_queue.dispatch_pending(&mut kime_ctx, |_, _, _| {}) { - break Err(e); - } - - // Flush pending writes - if let Err(e) = display.flush() { - // EWOULDBLOCK here means there're so many to write, retry later - if e.kind() != ErrorKind::WouldBlock { - break Err(e); - } - } - }; - - match stop_reason { - Ok(()) => { - log::info!("Server finished gracefully"); - Ok(()) - } - Err(e) => { - log::error!("Server aborted due to IO Error: {}", e); - Err(Box::from(e)) - } - } -} diff --git a/src/frontends/wayland/src/input_method_v2.rs b/src/frontends/wayland/src/input_method_v2.rs deleted file mode 100644 index 5d56e76f..00000000 --- a/src/frontends/wayland/src/input_method_v2.rs +++ /dev/null @@ -1,488 +0,0 @@ -use std::error::Error; -use std::time::{Duration, Instant}; - -use kime_engine_core::{ - load_engine_config_from_config_dir, Config, InputEngine, InputResult, Key, KeyCode, - ModifierState, -}; -use wayland_client::{ - event_enum, - protocol::{wl_keyboard::KeyState, wl_seat::WlSeat}, - DispatchData, Display, EventQueue, Filter, GlobalManager, Main, -}; - -use wayland_protocols::misc::zwp_input_method_v2::client::{ - zwp_input_method_keyboard_grab_v2::{Event as KeyEvent, ZwpInputMethodKeyboardGrabV2}, - zwp_input_method_manager_v2::ZwpInputMethodManagerV2, - zwp_input_method_v2::{Event as ImEvent, ZwpInputMethodV2}, -}; -use zwp_virtual_keyboard::virtual_keyboard_unstable_v1::{ - zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1, - zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1, -}; - -use mio::{unix::SourceFd, Events as MioEvents, Interest, Poll, Token}; -use timerfd_mio::TimerFd; - -use crate::{PressState, RepeatInfo}; - -event_enum! { - Events | - Key => ZwpInputMethodKeyboardGrabV2, - Im => ZwpInputMethodV2 -} - -struct InputMethodState { - activate: bool, - deactivate: bool, -} - -impl Default for InputMethodState { - fn default() -> Self { - Self { - activate: false, - deactivate: false, - } - } -} - -struct KimeContext { - config: Config, - engine: InputEngine, - mod_state: ModifierState, - current_state: InputMethodState, - pending_state: InputMethodState, - vk: Main, - im: Main, - grab: Main, - numlock: bool, - engine_ready: bool, - keymap_init: bool, - grab_activate: bool, - serial: u32, - // Have to concern Multi seats? - - // Key repeat contexts - timer: TimerFd, - /// `None` if `KimeContext` have never received a `RepeatInfo` or repeat is disabled (i.e. rate - /// is zero). `Some(..)` if `RepeatInfo` is known and kime-wayland started tracking the press - /// state of keys. - repeat_state: Option<(RepeatInfo, PressState)>, - last_preedit_len: usize, -} - -impl Drop for KimeContext { - fn drop(&mut self) { - self.grab.release(); - self.vk.destroy(); - self.im.destroy(); - } -} - -impl KimeContext { - pub fn new( - vk: Main, - im: Main, - grab: Main, - timer: TimerFd, - ) -> Self { - let config = load_engine_config_from_config_dir().unwrap_or_default(); - Self { - engine: InputEngine::new(&config), - config, - mod_state: ModifierState::empty(), - current_state: InputMethodState::default(), - pending_state: InputMethodState::default(), - serial: 0, - numlock: false, - engine_ready: true, - keymap_init: false, - grab_activate: false, - vk, - im, - grab, - timer, - repeat_state: None, - last_preedit_len: 0, - } - } - - pub fn new_data<'a>(data: &'a mut DispatchData) -> &'a mut Self { - data.get::().unwrap() - } - - fn process_input_result(&mut self, ret: InputResult) -> bool { - if ret.contains(InputResult::NOT_READY) { - self.engine_ready = false; - } - - if ret.contains(InputResult::LANGUAGE_CHANGED) { - self.engine.update_layout_state().ok(); - } - - if ret.contains(InputResult::HAS_PREEDIT) { - let preedit = self.engine.preedit_str().into(); - self.preedit(preedit); - } else { - self.clear_preedit(); - } - - if ret.contains(InputResult::HAS_COMMIT) { - self.commit_string(self.engine.commit_str().into()); - self.engine.clear_commit(); - } - - self.commit(); - - !ret.contains(InputResult::CONSUMED) - } - - fn commit(&mut self) { - self.im.commit(self.serial); - } - - fn commit_string(&mut self, s: String) { - if !s.is_empty() { - self.im.commit_string(s); - } - } - - fn clear_preedit(&mut self) { - if self.last_preedit_len > 0 { - self.im.set_preedit_string(String::new(), -1, -1); - self.last_preedit_len = 0; - } - } - - fn preedit(&mut self, s: String) { - self.last_preedit_len = s.len(); - let len = s.len(); - self.im.set_preedit_string(s, 0, len as _); - } - - pub fn handle_im_ev(&mut self, ev: ImEvent) { - match ev { - ImEvent::Activate => { - self.pending_state.activate = true; - } - ImEvent::Deactivate => { - self.pending_state.deactivate = true; - } - ImEvent::Unavailable => { - log::error!("Receive Unavailable event is another server already running?"); - panic!("Unavailable") - } - ImEvent::Done => { - self.serial += 1; - if !self.current_state.activate && self.pending_state.activate { - self.engine.update_layout_state().ok(); - if !self.engine_ready { - if self.engine.check_ready() { - let ret = self.engine.end_ready(); - self.process_input_result(ret); - self.engine_ready = true; - } - } - self.grab_activate = true; - } else if !self.current_state.deactivate && self.pending_state.deactivate { - // Focus lost, reset states - if self.engine_ready { - self.engine.reset(); - } - self.grab_activate = false; - - // Input deactivated, stop repeating - let _ = self.timer.set_timeout_oneshot(Duration::ZERO); - if let Some((_, ref mut press_state)) = self.repeat_state { - *press_state = PressState::NotPressing - } - } - self.current_state = std::mem::take(&mut self.pending_state); - } - _ => {} - } - } - pub fn handle_key_ev(&mut self, ev: KeyEvent) { - match ev { - KeyEvent::Keymap { fd, format, size } => { - if !self.keymap_init { - self.vk.keymap(format as _, fd, size); - self.keymap_init = true; - } - unsafe { - libc::close(fd); - } - } - KeyEvent::Key { - state, key, time, .. - } => { - // NOTE: Never read `serial` of KeyEvent. You should rely on serial of KimeContext - if state == KeyState::Pressed { - if self.grab_activate { - let hwcode = (key + 8) as u16; - if let Some(code) = KeyCode::from_hardware_code(hwcode, self.numlock) { - let ret = self - .engine - .press_key(Key::new(code, self.mod_state), &self.config); - - let bypassed = self.process_input_result(ret); - - if bypassed { - // Bypassed key's repeat will be handled by the clients. - // - // Reference: - // https://github.com/swaywm/sway/pull/4932#issuecomment-774113129 - self.vk.key(time, key, state as _); - } else { - // If the key was not bypassed by IME, key repeat should be handled by the - // IME. Start waiting for the key hold timer event. - match self.repeat_state { - Some((info, ref mut press_state)) - if !press_state.is_pressing(key) => - { - let duration = Duration::from_millis(info.delay as u64); - if let Err(e) = self.timer.set_timeout_oneshot(duration) { - log::warn!("failed to set repeat timer: {}", e); - } - *press_state = PressState::Pressing { - pressed_at: Instant::now(), - is_repeating: false, - key, - wayland_time: time, - }; - } - _ => {} - } - } - } else { - log::warn!("unknown hardware keycode: {}", key); - self.vk.key(time, key, state as _); - return; - } - } else { - // not activated so just skip - self.vk.key(time, key, state as _); - } - } else { - // If user released the last pressed key, clear the timer and state - if let Some((.., ref mut press_state)) = self.repeat_state { - if press_state.is_pressing(key) { - let _ = self.timer.set_timeout_oneshot(Duration::ZERO); - *press_state = PressState::NotPressing; - } - } - - self.vk.key(time, key, state as _); - } - } - KeyEvent::Modifiers { - mods_depressed, - mods_latched, - mods_locked, - group, - .. - } => { - self.mod_state = ModifierState::empty(); - if mods_depressed & 0x1 != 0 { - self.mod_state |= ModifierState::SHIFT; - } - if mods_depressed & 0x4 != 0 { - self.mod_state |= ModifierState::CONTROL; - } - if mods_depressed & 0x8 != 0 { - self.mod_state |= ModifierState::ALT; - } - if mods_depressed & 0x40 != 0 { - self.mod_state |= ModifierState::SUPER; - } - - self.numlock = mods_depressed & 0x10 != 0; - - self.vk - .modifiers(mods_depressed, mods_latched, mods_locked, group); - } - KeyEvent::RepeatInfo { rate, delay } => { - self.repeat_state = if rate == 0 { - // Zero rate means disabled repeat - // - // Reference: - // https://github.com/swaywm/wlroots/blob/3d46d3f7/protocol/input-method-unstable-v2.xml#L444-L455 - None - } else { - let info = RepeatInfo { rate, delay }; - let press_state = self.repeat_state.map(|pair| pair.1); - Some((info, press_state.unwrap_or(PressState::NotPressing))) - }; - } - _ => {} - } - } - - pub fn handle_timer_ev(&mut self) -> std::io::Result<()> { - // Read timer, this MUST be called or timer will be broken - let overrun_count = self.timer.read()?; - if overrun_count == 0 { - // Non-blocking read returned no expirations, skip processing - return Ok(()); - } - if overrun_count != 1 { - log::warn!("Some timer events were not properly handled!"); - } - - if let Some(( - info, - PressState::Pressing { - pressed_at, - ref mut is_repeating, - key, - wayland_time, - }, - )) = self.repeat_state - { - if !*is_repeating { - // Start repeat - log::trace!("Start repeating {}", key); - let interval = Duration::from_secs_f64(1.0 / info.rate as f64); - self.timer.set_timeout_interval(interval, interval)?; - *is_repeating = true; - } - - // Emit key repeat event - let ev = KeyEvent::Key { - serial: self.serial, - time: wayland_time + pressed_at.elapsed().as_millis() as u32, - key, - state: KeyState::Pressed, - }; - self.handle_key_ev(ev); - } else { - log::warn!("Received timer event when it has never received RepeatInfo."); - } - - Ok(()) - } -} - -pub fn run( - display: &Display, - event_queue: &mut EventQueue, - globals: &GlobalManager, -) -> Result<(), Box> { - let im_manager = globals.instantiate_exact::(1)?; - let vk_manager = globals.instantiate_exact::(1)?; - let seat = globals.instantiate_exact::(1).expect("Load Seat"); - - let filter = Filter::new(|ev, _filter, mut data| { - let ctx = KimeContext::new_data(&mut data); - - match ev { - Events::Key { event, .. } => { - ctx.handle_key_ev(event); - } - Events::Im { event, .. } => { - ctx.handle_im_ev(event); - } - } - }); - - let vk = vk_manager.create_virtual_keyboard(&seat); - let im = im_manager.get_input_method(&seat); - let grab = im.grab_keyboard(); - grab.assign(filter.clone()); - im.assign(filter); - - // Initialize timer - let mut timer = TimerFd::new().expect("Initialize timer"); - - // Initialize epoll() object - let mut poll = Poll::new().expect("Initialize epoll()"); - let registry = poll.registry(); - - const POLL_WAYLAND: Token = Token(0); - registry - .register( - &mut SourceFd(&display.get_connection_fd()), - POLL_WAYLAND, - Interest::READABLE | Interest::WRITABLE, - ) - .expect("Register wayland socket to the epoll()"); - - const POLL_TIMER: Token = Token(1); - registry - .register(&mut timer, POLL_TIMER, Interest::READABLE) - .expect("Register timer to the epoll()"); - - // Initialize kime context - let mut kime_ctx = KimeContext::new(vk, im, grab, timer); - event_queue - .sync_roundtrip(&mut kime_ctx, |_, _, _| ()) - .unwrap(); - - log::info!("Server init success!"); - - // Non-blocking event loop - // - // Reference: - // https://docs.rs/wayland-client/0.28.3/wayland_client/struct.EventQueue.html - let mut events = MioEvents::with_capacity(1024); - let stop_reason = 'main: loop { - use std::io::ErrorKind; - - // Sleep until next event - if let Err(e) = poll.poll(&mut events, None) { - // Should retry on EINTR - // - // Reference: - // https://www.gnu.org/software/libc/manual/html_node/Interrupted-Primitives.html - if e.kind() == ErrorKind::Interrupted { - continue; - } - break Err(e); - } - - for event in &events { - match event.token() { - POLL_WAYLAND => {} - POLL_TIMER => { - if let Err(e) = kime_ctx.handle_timer_ev() { - break 'main Err(e); - } - } - _ => unreachable!(), - } - } - - // Perform read() only when it's ready, returns None when there're already pending events - if let Some(guard) = event_queue.prepare_read() { - if let Err(e) = guard.read_events() { - // EWOULDBLOCK here means there's no new messages to read - if e.kind() != ErrorKind::WouldBlock { - break Err(e); - } - } - } - - if let Err(e) = event_queue.dispatch_pending(&mut kime_ctx, |_, _, _| {}) { - break Err(e); - } - - // Flush pending writes - if let Err(e) = display.flush() { - // EWOULDBLOCK here means there're so many to write, retry later - if e.kind() != ErrorKind::WouldBlock { - break Err(e); - } - } - }; - - match stop_reason { - Ok(()) => { - log::info!("Server finished gracefully"); - Ok(()) - } - Err(e) => { - log::error!("Server aborted due to IO Error: {}", e); - Err(Box::from(e)) - } - } -} diff --git a/src/frontends/wayland/src/lib.rs b/src/frontends/wayland/src/lib.rs index 77444a30..99f5d879 100644 --- a/src/frontends/wayland/src/lib.rs +++ b/src/frontends/wayland/src/lib.rs @@ -1,14 +1,13 @@ use std::time::Instant; -pub mod input_method_v1; -pub mod input_method_v2; +pub mod state; #[derive(Clone, Copy)] pub struct RepeatInfo { /// The rate of repeating keys in characters per second - rate: i32, + pub rate: i32, /// Delay in milliseconds since key down until repeating starts - delay: i32, + pub delay: i32, } #[derive(Clone, Copy)] diff --git a/src/frontends/wayland/src/main.rs b/src/frontends/wayland/src/main.rs index a589b15e..8428896f 100644 --- a/src/frontends/wayland/src/main.rs +++ b/src/frontends/wayland/src/main.rs @@ -1,19 +1,58 @@ -use wayland_client::{Display, GlobalManager}; +use wayland_client::Connection; + +use kime_wayland::state::AppState; fn main() { kime_version::cli_boilerplate!((),); - let display = Display::connect_to_env().expect("Failed to connect wayland display"); - let mut event_queue = display.create_event_queue(); - let attached_display = display.attach(event_queue.token()); - let globals = GlobalManager::new(&attached_display); + let conn = Connection::connect_to_env().expect("Failed to connect wayland display"); + let display = conn.display(); + + let mut event_queue = conn.new_event_queue::(); + let qh = event_queue.handle(); + + // Get registry to bind globals + display.get_registry(&qh, ()); + + // Create initial state + let mut state = AppState::new(&conn); - event_queue.sync_roundtrip(&mut (), |_, _, _| ()).unwrap(); + // Initial roundtrip to get globals + event_queue.roundtrip(&mut state).unwrap(); - let result = kime_wayland::input_method_v2::run(&display, &mut event_queue, &globals); + log::debug!( + "Globals: v2={}, vk={}, seat={}, v1={}", + state.has_input_method_v2(), + state.has_vk_manager(), + state.has_seat(), + state.has_input_method_v1() + ); - if let Err(e) = result { - log::warn!("input_method_v2 failed: {}, trying v1", e); - kime_wayland::input_method_v1::run(&display, &mut event_queue, &globals).unwrap(); + // Try v2 first, fall back to v1 + if state.has_input_method_v2() { + log::info!("Using input_method_v2 protocol"); + if let Err(e) = state.setup_input_method_v2(&qh) { + log::warn!("input_method_v2 setup failed: {}, trying v1", e); + if state.has_input_method_v1() { + state.setup_input_method_v1(&qh).unwrap(); + } else { + log::error!("No input method protocol available"); + std::process::exit(1); + } + } + } else if state.has_input_method_v1() { + log::info!("Using input_method_v1 protocol"); + state.setup_input_method_v1(&qh).unwrap(); + } else { + log::error!("No input method protocol available (no zwp_input_method_manager_v2, zwp_virtual_keyboard_manager_v1, or zwp_input_method_v1 found)"); + std::process::exit(1); } + + // Roundtrip after setup + event_queue.roundtrip(&mut state).unwrap(); + + log::info!("Server init success!"); + + // Event loop + state.run_event_loop(&conn, event_queue); } diff --git a/src/frontends/wayland/src/state.rs b/src/frontends/wayland/src/state.rs new file mode 100644 index 00000000..1fd436e7 --- /dev/null +++ b/src/frontends/wayland/src/state.rs @@ -0,0 +1,948 @@ +use std::error::Error; +use std::os::fd::{AsFd, AsRawFd}; +use std::time::{Duration, Instant}; + +use kime_engine_core::{ + load_engine_config_from_config_dir, Config, InputEngine, InputResult, Key, KeyCode, + ModifierState, +}; + +use mio::{unix::SourceFd, Events as MioEvents, Interest, Poll, Token}; +use timerfd_mio::TimerFd; + +use wayland_client::{ + delegate_noop, event_created_child, + protocol::{ + wl_keyboard::{self, KeyState, KeymapFormat, WlKeyboard}, + wl_registry::{self, WlRegistry}, + wl_seat::{self, WlSeat}, + }, + Connection, Dispatch, EventQueue, Proxy, QueueHandle, WEnum, +}; + +use wayland_protocols_misc::zwp_input_method_v2::client::{ + zwp_input_method_keyboard_grab_v2::{self, ZwpInputMethodKeyboardGrabV2}, + zwp_input_method_manager_v2::ZwpInputMethodManagerV2, + zwp_input_method_v2::{self, ZwpInputMethodV2}, +}; + +use wayland_protocols::wp::input_method::zv1::client::{ + zwp_input_method_context_v1::{self, ZwpInputMethodContextV1}, + zwp_input_method_v1::{self, ZwpInputMethodV1}, +}; + +use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{ + zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1, + zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1, +}; + +use crate::{PressState, RepeatInfo}; + +use xkbcommon::xkb::{ + Context as XkbContext, Keycode, Keymap, CONTEXT_NO_FLAGS, KEYMAP_COMPILE_NO_FLAGS, + KEYMAP_FORMAT_TEXT_V1, +}; + +const ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE: u32 = 5; + +/// Registry state for binding globals +struct Globals { + seat: Option, + im_manager_v2: Option, + vk_manager: Option, + im_v1: Option, +} + +/// Input method v2 state +struct InputMethodV2State { + im: ZwpInputMethodV2, + #[allow(dead_code)] + grab: ZwpInputMethodKeyboardGrabV2, + vk: ZwpVirtualKeyboardV1, + pending_activate: bool, + pending_deactivate: bool, + current_activate: bool, + current_deactivate: bool, + grab_activate: bool, + keymap_init: bool, +} + +/// Input method v1 state +struct InputMethodV1State { + im_ctx: Option, + keyboard: Option, + keymap: Option, + grab_activate: bool, +} + +/// Main application state +pub struct AppState { + config: Config, + engine: InputEngine, + mod_state: ModifierState, + numlock: bool, + engine_ready: bool, + serial: u32, + timer: TimerFd, + repeat_state: Option<(RepeatInfo, PressState)>, + last_preedit_len: usize, + should_exit: bool, + + // Wayland state + globals: Globals, + im_v2: Option, + im_v1: Option, +} + +impl AppState { + pub fn new(_conn: &Connection) -> Self { + let config = load_engine_config_from_config_dir().unwrap_or_default(); + let timer = TimerFd::new().expect("Initialize timer"); + + Self { + engine: InputEngine::new(&config), + config, + mod_state: ModifierState::empty(), + numlock: false, + engine_ready: true, + serial: 0, + timer, + // For v1, provide default repeat info since older protocols might not provide it + repeat_state: Some(( + RepeatInfo { + rate: 20, + delay: 400, + }, + PressState::NotPressing, + )), + last_preedit_len: 0, + should_exit: false, + globals: Globals { + seat: None, + im_manager_v2: None, + vk_manager: None, + im_v1: None, + }, + im_v2: None, + im_v1: None, + } + } + + pub fn has_input_method_v2(&self) -> bool { + self.globals.im_manager_v2.is_some() + && self.globals.vk_manager.is_some() + && self.globals.seat.is_some() + } + + pub fn has_input_method_v1(&self) -> bool { + self.globals.im_v1.is_some() + } + + pub fn has_vk_manager(&self) -> bool { + self.globals.vk_manager.is_some() + } + + pub fn has_seat(&self) -> bool { + self.globals.seat.is_some() + } + + pub fn setup_input_method_v2(&mut self, qh: &QueueHandle) -> Result<(), Box> { + let seat = self.globals.seat.as_ref().ok_or("No seat")?; + let im_manager = self + .globals + .im_manager_v2 + .as_ref() + .ok_or("No input method manager v2")?; + let vk_manager = self + .globals + .vk_manager + .as_ref() + .ok_or("No virtual keyboard manager")?; + + let im = im_manager.get_input_method(seat, qh, ()); + let grab = im.grab_keyboard(qh, ()); + let vk = vk_manager.create_virtual_keyboard(seat, qh, ()); + + self.im_v2 = Some(InputMethodV2State { + im, + grab, + vk, + pending_activate: false, + pending_deactivate: false, + current_activate: false, + current_deactivate: false, + grab_activate: false, + keymap_init: false, + }); + + Ok(()) + } + + pub fn setup_input_method_v1(&mut self, _qh: &QueueHandle) -> Result<(), Box> { + // v1 is already bound through registry in Dispatch + // The actual context activation happens via the Activate event + if self.globals.im_v1.is_none() { + return Err("No input method v1".into()); + } + + self.im_v1 = Some(InputMethodV1State { + im_ctx: None, + keyboard: None, + keymap: None, + grab_activate: false, + }); + + Ok(()) + } + + pub fn run_event_loop(mut self, conn: &Connection, mut event_queue: EventQueue) { + let mut poll = Poll::new().expect("Initialize epoll()"); + let registry = poll.registry(); + + const POLL_WAYLAND: Token = Token(0); + registry + .register( + &mut SourceFd(&conn.as_fd().as_raw_fd()), + POLL_WAYLAND, + Interest::READABLE | Interest::WRITABLE, + ) + .expect("Register wayland socket to the epoll()"); + + const POLL_TIMER: Token = Token(1); + registry + .register(&mut self.timer, POLL_TIMER, Interest::READABLE) + .expect("Register timer to the epoll()"); + + let mut events = MioEvents::with_capacity(1024); + + loop { + // Flush pending writes first + if let Err(e) = conn.flush() { + if let wayland_client::backend::WaylandError::Io(ref io_err) = e { + if io_err.kind() != std::io::ErrorKind::WouldBlock { + log::error!("Connection flush error: {}", e); + break; + } + } else { + log::error!("Connection flush error: {}", e); + break; + } + } + + // Sleep until next event + if let Err(e) = poll.poll(&mut events, None) { + if e.kind() == std::io::ErrorKind::Interrupted { + continue; + } + log::error!("Poll error: {}", e); + break; + } + + for event in &events { + match event.token() { + POLL_WAYLAND => { + // Read events - prepare_read returns Option + if let Some(guard) = conn.prepare_read() { + if let Err(e) = guard.read() { + if let wayland_client::backend::WaylandError::Io(ref io_err) = e { + if io_err.kind() != std::io::ErrorKind::WouldBlock { + log::error!("Read error: {}", e); + return; + } + } else { + log::error!("Read error: {}", e); + return; + } + } + } + + // Dispatch pending events + if let Err(e) = event_queue.dispatch_pending(&mut self) { + log::error!("Dispatch error: {}", e); + return; + } + + // Check if we should exit (e.g., Unavailable event) + if self.should_exit { + log::info!("Exiting due to should_exit flag"); + return; + } + } + POLL_TIMER => { + if let Err(e) = self.handle_timer_ev() { + log::error!("Timer error: {}", e); + return; + } + } + _ => unreachable!(), + } + } + } + } + + fn handle_timer_ev(&mut self) -> std::io::Result<()> { + let overrun_count = self.timer.read()?; + if overrun_count == 0 { + return Ok(()); + } + if overrun_count != 1 { + log::warn!("Some timer events were not properly handled!"); + } + + if let Some(( + info, + PressState::Pressing { + pressed_at, + ref mut is_repeating, + key, + wayland_time, + }, + )) = self.repeat_state + { + // Check if key should repeat (v1 uses keymap, v2 always repeats) + let should_start_repeat = if let Some(ref im_state) = self.im_v1 { + im_state + .keymap + .as_ref() + .map_or(true, |km| km.key_repeats(Keycode::new(key + 8))) + } else { + true + }; + + if !*is_repeating && should_start_repeat { + log::trace!("Start repeating {}", key); + let interval = Duration::from_secs_f64(1.0 / info.rate as f64); + self.timer.set_timeout_interval(interval, interval)?; + *is_repeating = true; + } + + // Emit key repeat event + let elapsed_ms = pressed_at.elapsed().as_millis() as u32; + let _time = wayland_time.wrapping_add(elapsed_ms); + + // Handle v2 + if let Some(ref mut im_state) = self.im_v2 { + if im_state.grab_activate { + let hwcode = (key + 8) as u16; + if let Some(code) = KeyCode::from_hardware_code(hwcode, self.numlock) { + let ret = self + .engine + .press_key(Key::new(code, self.mod_state), &self.config); + self.process_input_result_v2(ret); + } + } + } + + // Handle v1 + let v1_grab_activate = self + .im_v1 + .as_ref() + .map(|s| s.grab_activate) + .unwrap_or(false); + if v1_grab_activate { + let hwcode = (key + 8) as u16; + if let Some(code) = KeyCode::from_hardware_code(hwcode, self.numlock) { + let ret = self + .engine + .press_key(Key::new(code, self.mod_state), &self.config); + self.process_input_result_v1(ret); + } + } + } + + Ok(()) + } + + fn process_input_result_v2(&mut self, ret: InputResult) -> bool { + if ret.contains(InputResult::NOT_READY) { + self.engine_ready = false; + } + + if ret.contains(InputResult::LANGUAGE_CHANGED) { + self.engine.update_layout_state().ok(); + } + + if let Some(ref im_state) = self.im_v2 { + if ret.contains(InputResult::HAS_PREEDIT) { + let preedit = self.engine.preedit_str(); + let len = preedit.len(); + im_state + .im + .set_preedit_string(preedit.into(), 0, len as i32); + self.last_preedit_len = len; + } else if self.last_preedit_len > 0 { + im_state.im.set_preedit_string(String::new(), -1, -1); + self.last_preedit_len = 0; + } + + if ret.contains(InputResult::HAS_COMMIT) { + let commit_str = self.engine.commit_str(); + if !commit_str.is_empty() { + im_state.im.commit_string(commit_str.into()); + } + self.engine.clear_commit(); + } + + im_state.im.commit(self.serial); + } + + !ret.contains(InputResult::CONSUMED) + } + + fn process_input_result_v1(&mut self, ret: InputResult) -> bool { + if ret.contains(InputResult::NOT_READY) { + self.engine_ready = false; + } + + if ret.contains(InputResult::LANGUAGE_CHANGED) { + self.engine.update_layout_state().ok(); + } + + if let Some(ref im_state) = self.im_v1 { + if let Some(ref im_ctx) = im_state.im_ctx { + if ret.contains(InputResult::HAS_COMMIT) { + let commit_str = self.engine.commit_str(); + if !commit_str.is_empty() { + im_ctx.commit_string(self.serial, commit_str.into()); + } + self.engine.clear_commit(); + } + + if ret.contains(InputResult::HAS_PREEDIT) { + let preedit = self.engine.preedit_str(); + let len = preedit.len(); + im_ctx.preedit_cursor(len as i32); + im_ctx.preedit_styling( + 0, + len as u32, + ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE, + ); + im_ctx.preedit_string(self.serial, preedit.to_string(), preedit.into()); + self.last_preedit_len = len; + } else if self.last_preedit_len > 0 { + im_ctx.preedit_cursor(0); + im_ctx.preedit_string(self.serial, String::new(), String::new()); + self.last_preedit_len = 0; + } + } + } + + !ret.contains(InputResult::CONSUMED) + } + + fn key_v1(&mut self, time: u32, key: u32, state: KeyState) { + if let Some(ref im_state) = self.im_v1 { + if let Some(ref im_ctx) = im_state.im_ctx { + im_ctx.key(self.serial, time, key, state as u32); + } + } + } + + fn modifiers_v1( + &mut self, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + group: u32, + ) { + if let Some(ref im_state) = self.im_v1 { + if let Some(ref im_ctx) = im_state.im_ctx { + im_ctx.modifiers( + self.serial, + mods_depressed, + mods_latched, + mods_locked, + group, + ); + } + } + } +} + +// Registry dispatch +impl Dispatch for AppState { + fn event( + state: &mut Self, + registry: &WlRegistry, + event: wl_registry::Event, + _data: &(), + _conn: &Connection, + qh: &QueueHandle, + ) { + if let wl_registry::Event::Global { + name, + interface, + version, + } = event + { + log::debug!("Registry global [{}]: {} v{}", name, interface, version); + match interface.as_str() { + "wl_seat" => { + log::debug!("Binding wl_seat"); + let seat = registry.bind::(name, version.min(7), qh, ()); + state.globals.seat = Some(seat); + } + "zwp_input_method_manager_v2" => { + log::debug!("Binding zwp_input_method_manager_v2"); + let mgr = registry.bind::( + name, + version.min(1), + qh, + (), + ); + state.globals.im_manager_v2 = Some(mgr); + } + "zwp_virtual_keyboard_manager_v1" => { + log::debug!("Binding zwp_virtual_keyboard_manager_v1"); + let mgr = registry.bind::( + name, + version.min(1), + qh, + (), + ); + state.globals.vk_manager = Some(mgr); + } + "zwp_input_method_v1" => { + log::debug!("Binding zwp_input_method_v1"); + let im = registry.bind::(name, version.min(1), qh, ()); + state.globals.im_v1 = Some(im); + } + _ => {} + } + } + } +} + +// Seat dispatch (no-op, just need to bind) +impl Dispatch for AppState { + fn event( + _state: &mut Self, + _seat: &WlSeat, + _event: wl_seat::Event, + _data: &(), + _conn: &Connection, + _qh: &QueueHandle, + ) { + } +} + +// Input method manager v2 dispatch (no events) +delegate_noop!(AppState: ignore ZwpInputMethodManagerV2); +delegate_noop!(AppState: ignore ZwpVirtualKeyboardManagerV1); +delegate_noop!(AppState: ignore ZwpVirtualKeyboardV1); + +// Input method v2 dispatch +impl Dispatch for AppState { + fn event( + state: &mut Self, + _im: &ZwpInputMethodV2, + event: zwp_input_method_v2::Event, + _data: &(), + _conn: &Connection, + _qh: &QueueHandle, + ) { + log::trace!("input_method_v2 event: {:?}", event); + match event { + zwp_input_method_v2::Event::Activate => { + if let Some(ref mut im_state) = state.im_v2 { + im_state.pending_activate = true; + } + } + zwp_input_method_v2::Event::Deactivate => { + if let Some(ref mut im_state) = state.im_v2 { + im_state.pending_deactivate = true; + } + } + zwp_input_method_v2::Event::Unavailable => { + log::error!("Receive Unavailable event, is another server already running?"); + state.should_exit = true; + } + zwp_input_method_v2::Event::Done => { + state.serial += 1; + + let (should_activate, should_deactivate) = { + if let Some(ref im_state) = state.im_v2 { + let activate = !im_state.current_activate && im_state.pending_activate; + let deactivate = + !im_state.current_deactivate && im_state.pending_deactivate; + (activate, deactivate) + } else { + (false, false) + } + }; + + if should_activate { + state.engine.update_layout_state().ok(); + if !state.engine_ready && state.engine.check_ready() { + let ret = state.engine.end_ready(); + state.process_input_result_v2(ret); + state.engine_ready = true; + } + if let Some(ref mut im_state) = state.im_v2 { + im_state.grab_activate = true; + } + } else if should_deactivate { + if state.engine_ready { + state.engine.reset(); + } + if let Some(ref mut im_state) = state.im_v2 { + im_state.grab_activate = false; + } + let _ = state.timer.set_timeout_oneshot(Duration::ZERO); + if let Some((_, ref mut ps)) = state.repeat_state { + *ps = PressState::NotPressing; + } + } + + if let Some(ref mut im_state) = state.im_v2 { + im_state.current_activate = im_state.pending_activate; + im_state.current_deactivate = im_state.pending_deactivate; + im_state.pending_activate = false; + im_state.pending_deactivate = false; + } + } + _ => {} + } + } +} + +// Keyboard grab v2 dispatch +impl Dispatch for AppState { + fn event( + state: &mut Self, + _grab: &ZwpInputMethodKeyboardGrabV2, + event: zwp_input_method_keyboard_grab_v2::Event, + _data: &(), + _conn: &Connection, + _qh: &QueueHandle, + ) { + match event { + zwp_input_method_keyboard_grab_v2::Event::Keymap { format, fd, size } => { + if let Some(ref mut im_state) = state.im_v2 { + if !im_state.keymap_init { + im_state.vk.keymap(format.into(), fd.as_fd(), size); + im_state.keymap_init = true; + } + } + } + zwp_input_method_keyboard_grab_v2::Event::Key { + time, + key, + state: key_state, + .. + } => { + let is_pressed = matches!(key_state, WEnum::Value(KeyState::Pressed)) + || key_state == WEnum::Value(KeyState::Pressed) + || matches!(&key_state, WEnum::Unknown(2)); // Repeated + + let grab_activate = state + .im_v2 + .as_ref() + .map(|s| s.grab_activate) + .unwrap_or(false); + + if is_pressed && grab_activate { + let hwcode = (key + 8) as u16; + if let Some(code) = KeyCode::from_hardware_code(hwcode, state.numlock) { + let ret = state + .engine + .press_key(Key::new(code, state.mod_state), &state.config); + + let bypassed = state.process_input_result_v2(ret); + + if bypassed { + if let Some(ref im_state) = state.im_v2 { + im_state.vk.key(time, key, key_state.into()); + } + } else { + // Handle key repeat + if let Some((info, ref mut press_state)) = state.repeat_state { + if !press_state.is_pressing(key) { + let duration = Duration::from_millis(info.delay as u64); + if let Err(e) = state.timer.set_timeout_oneshot(duration) { + log::warn!("failed to set repeat timer: {}", e); + } + *press_state = PressState::Pressing { + pressed_at: Instant::now(), + is_repeating: false, + key, + wayland_time: time, + }; + } + } + } + } else if let Some(ref im_state) = state.im_v2 { + im_state.vk.key(time, key, key_state.into()); + } + } else if matches!(key_state, WEnum::Value(KeyState::Released)) { + if let Some((.., ref mut press_state)) = state.repeat_state { + if press_state.is_pressing(key) { + let _ = state.timer.set_timeout_oneshot(Duration::ZERO); + *press_state = PressState::NotPressing; + } + } + if let Some(ref im_state) = state.im_v2 { + im_state.vk.key(time, key, key_state.into()); + } + } else if !is_pressed { + if let Some(ref im_state) = state.im_v2 { + im_state.vk.key(time, key, key_state.into()); + } + } + } + zwp_input_method_keyboard_grab_v2::Event::Modifiers { + mods_depressed, + mods_latched, + mods_locked, + group, + .. + } => { + state.mod_state = ModifierState::empty(); + if mods_depressed & 0x1 != 0 { + state.mod_state |= ModifierState::SHIFT; + } + if mods_depressed & 0x4 != 0 { + state.mod_state |= ModifierState::CONTROL; + } + if mods_depressed & 0x8 != 0 { + state.mod_state |= ModifierState::ALT; + } + if mods_depressed & 0x40 != 0 { + state.mod_state |= ModifierState::SUPER; + } + state.numlock = mods_depressed & 0x10 != 0; + + if let Some(ref im_state) = state.im_v2 { + im_state + .vk + .modifiers(mods_depressed, mods_latched, mods_locked, group); + } + } + zwp_input_method_keyboard_grab_v2::Event::RepeatInfo { rate, delay } => { + state.repeat_state = if rate == 0 { + None + } else { + let info = RepeatInfo { rate, delay }; + let press_state = state + .repeat_state + .map(|pair| pair.1) + .unwrap_or(PressState::NotPressing); + Some((info, press_state)) + }; + } + _ => {} + } + } +} + +// Input method v1 dispatch +impl Dispatch for AppState { + event_created_child!(AppState, ZwpInputMethodV1, [ + // opcode 0 = activate event, creates ZwpInputMethodContextV1 + 0 => (ZwpInputMethodContextV1, ()) + ]); + + fn event( + state: &mut Self, + _im: &ZwpInputMethodV1, + event: zwp_input_method_v1::Event, + _data: &(), + _conn: &Connection, + qh: &QueueHandle, + ) { + match event { + zwp_input_method_v1::Event::Activate { id: im_ctx } => { + log::trace!("input_method_v1: Activate"); + state.engine.update_layout_state().ok(); + if !state.engine_ready && state.engine.check_ready() { + let ret = state.engine.end_ready(); + state.process_input_result_v1(ret); + state.engine_ready = true; + } + + // Grab keyboard + let keyboard = im_ctx.grab_keyboard(qh, ()); + + if let Some(ref mut im_state) = state.im_v1 { + im_state.im_ctx = Some(im_ctx); + im_state.keyboard = Some(keyboard); + im_state.grab_activate = true; + } + } + zwp_input_method_v1::Event::Deactivate { context: im_ctx } => { + log::trace!("input_method_v1: Deactivate"); + if state.engine_ready { + state.engine.reset(); + } + + // Stop repeating + let _ = state.timer.set_timeout_oneshot(Duration::ZERO); + if let Some((_, ref mut press_state)) = state.repeat_state { + *press_state = PressState::NotPressing; + } + + im_ctx.destroy(); + + if let Some(ref mut im_state) = state.im_v1 { + im_state.im_ctx = None; + if let Some(ref keyboard) = im_state.keyboard { + if keyboard.version() >= wl_keyboard::REQ_RELEASE_SINCE { + keyboard.release(); + } + } + im_state.keyboard = None; + im_state.grab_activate = false; + } + } + _ => {} + } + } +} + +// Input method context v1 dispatch +impl Dispatch for AppState { + fn event( + state: &mut Self, + _im_ctx: &ZwpInputMethodContextV1, + event: zwp_input_method_context_v1::Event, + _data: &(), + _conn: &Connection, + _qh: &QueueHandle, + ) { + match event { + zwp_input_method_context_v1::Event::CommitState { serial } => { + state.serial = serial; + } + _ => {} + } + } +} + +// WlKeyboard dispatch for v1 +impl Dispatch for AppState { + fn event( + state: &mut Self, + _keyboard: &WlKeyboard, + event: wl_keyboard::Event, + _data: &(), + _conn: &Connection, + _qh: &QueueHandle, + ) { + match event { + wl_keyboard::Event::Keymap { format, fd, size } => { + if let WEnum::Value(KeymapFormat::XkbV1) = format { + // fd is already OwnedFd in wayland-client 0.31, use it directly + if let Some(ref mut im_state) = state.im_v1 { + im_state.keymap = unsafe { + Keymap::new_from_fd( + &XkbContext::new(CONTEXT_NO_FLAGS), + fd, + size as usize, + KEYMAP_FORMAT_TEXT_V1, + KEYMAP_COMPILE_NO_FLAGS, + ) + .unwrap_or(None) + }; + } + } + } + wl_keyboard::Event::Key { + time, + key, + state: key_state, + .. + } => { + let is_pressed = matches!(key_state, WEnum::Value(KeyState::Pressed)) + || matches!(&key_state, WEnum::Unknown(2)); // Repeated + + let grab_activate = state + .im_v1 + .as_ref() + .map(|s| s.grab_activate) + .unwrap_or(false); + + if is_pressed && grab_activate { + let hwcode = (key + 8) as u16; + if let Some(code) = KeyCode::from_hardware_code(hwcode, state.numlock) { + let ret = state + .engine + .press_key(Key::new(code, state.mod_state), &state.config); + + let bypassed = state.process_input_result_v1(ret); + + if bypassed { + if let WEnum::Value(ks) = key_state { + state.key_v1(time, key, ks); + } + } else { + // Handle key repeat + if let Some((info, ref mut press_state)) = state.repeat_state { + if !press_state.is_pressing(key) { + let duration = Duration::from_millis(info.delay as u64); + if let Err(e) = state.timer.set_timeout_oneshot(duration) { + log::warn!("failed to set repeat timer: {}", e); + } + *press_state = PressState::Pressing { + pressed_at: Instant::now(), + is_repeating: false, + key, + wayland_time: time, + }; + } + } + } + } else if let WEnum::Value(ks) = key_state { + state.key_v1(time, key, ks); + } + } else if matches!(key_state, WEnum::Value(KeyState::Released)) { + if let Some((.., ref mut press_state)) = state.repeat_state { + if press_state.is_pressing(key) { + let _ = state.timer.set_timeout_oneshot(Duration::ZERO); + *press_state = PressState::NotPressing; + } + } + state.key_v1(time, key, KeyState::Released); + } else if let WEnum::Value(ks) = key_state { + state.key_v1(time, key, ks); + } + } + wl_keyboard::Event::Modifiers { + mods_depressed, + mods_latched, + mods_locked, + group, + .. + } => { + state.mod_state = ModifierState::empty(); + if mods_depressed & 0x1 != 0 { + state.mod_state |= ModifierState::SHIFT; + } + if mods_depressed & 0x4 != 0 { + state.mod_state |= ModifierState::CONTROL; + } + if mods_depressed & 0x8 != 0 { + state.mod_state |= ModifierState::ALT; + } + if mods_depressed & 0x40 != 0 { + state.mod_state |= ModifierState::SUPER; + } + state.numlock = mods_depressed & 0x10 != 0; + + state.modifiers_v1(mods_depressed, mods_latched, mods_locked, group); + } + wl_keyboard::Event::RepeatInfo { rate, delay } => { + state.repeat_state = if rate == 0 { + None + } else { + let info = RepeatInfo { rate, delay }; + let press_state = state + .repeat_state + .map(|pair| pair.1) + .unwrap_or(PressState::NotPressing); + Some((info, press_state)) + }; + } + _ => {} + } + } +} diff --git a/src/frontends/xim/Cargo.toml b/src/frontends/xim/Cargo.toml index cd7b13f8..12673ae8 100644 --- a/src/frontends/xim/Cargo.toml +++ b/src/frontends/xim/Cargo.toml @@ -21,5 +21,5 @@ x11rb = { version = "0.13", features = [ ], default-features = false } pico-args = "0.5.0" image = { version = "0.25", default-features = false, features = ["png"] } -imageproc = { version = "0.25", default-features = false } +imageproc = { version = "0.26", default-features = false, features = ["text"] } ab_glyph = "0.2" diff --git a/src/tools/candidate-window/Cargo.toml b/src/tools/candidate-window/Cargo.toml index c4438c39..e509813b 100644 --- a/src/tools/candidate-window/Cargo.toml +++ b/src/tools/candidate-window/Cargo.toml @@ -9,5 +9,5 @@ license = "GPL-3.0-or-later" [dependencies] kime-engine-core = { path = "../../engine/core" } -eframe = { version = "0.31", default-features = false, features = ["glow", "default_fonts", "wayland", "x11"] } -egui = "0.31" +eframe = { version = "0.33", default-features = false, features = ["glow", "default_fonts", "wayland", "x11"] } +egui = "0.33" diff --git a/src/tools/check/Cargo.toml b/src/tools/check/Cargo.toml index b8ba6c9a..a206cd68 100644 --- a/src/tools/check/Cargo.toml +++ b/src/tools/check/Cargo.toml @@ -10,5 +10,5 @@ owo-colors = "4" kime-engine-core = { path = "../../engine/core" } pad = "0.1.6" serde_yaml = "0.9" -strum = { version = "0.26", features = ["derive"] } -xdg = "2.5" +strum = { version = "0.27", features = ["derive"] } +xdg = "3.0" diff --git a/src/tools/check/src/main.rs b/src/tools/check/src/main.rs index 277d8b66..f67f64d4 100644 --- a/src/tools/check/src/main.rs +++ b/src/tools/check/src/main.rs @@ -66,7 +66,7 @@ impl Check { pub fn cond(self) -> CondResult { match self { Check::Icons => { - let dirs = xdg::BaseDirectories::new().expect("Load xdg dirs"); + let dirs = xdg::BaseDirectories::new(); let icons = &[ "kime-hangul-black.png", @@ -78,7 +78,7 @@ impl Check { for icon in icons { match dirs.find_data_file(format!("icons/hicolor/64x64/apps/{}", icon)) { Some(path) => println!("Found icon: {}", path.display()), - _ => return CondResult::Fail(format!("Can't find icon {}", icon)), + None => return CondResult::Fail(format!("Can't find icon {}", icon)), } } @@ -104,8 +104,8 @@ impl Check { ) } Check::Config => { - let dirs = xdg::BaseDirectories::with_prefix("kime").expect("Load xdg dirs"); - let config_path = match dirs.find_config_file("config.yaml") { + let dirs = xdg::BaseDirectories::with_prefix("kime"); + let config_path: std::path::PathBuf = match dirs.find_config_file("config.yaml") { Some(path) => path, _ => { return CondResult::Ignore( @@ -125,7 +125,7 @@ impl Check { match config.engine.translation_layer { Some(ref raw_path) => { - let path = match dirs.find_config_file(raw_path) { + let path: std::path::PathBuf = match dirs.find_config_file(raw_path) { Some(path) => path, _ => { return CondResult::Ignore( @@ -177,8 +177,8 @@ impl Check { let current_desktop = env::var("XDG_CURRENT_DESKTOP").map_or(String::new(), |x| x); let session_type = env::var("XDG_SESSION_TYPE").map_or(String::new(), |x| x); if current_desktop.contains("KDE") && session_type == "wayland" { - let dirs = xdg::BaseDirectories::new().expect("Load xdg dirs"); - let config_path = match dirs.find_config_file("kwinrc") { + let dirs = xdg::BaseDirectories::new(); + let config_path: std::path::PathBuf = match dirs.find_config_file("kwinrc") { Some(path) => path, _ => { return CondResult::Fail( diff --git a/src/tools/indicator/src/main.rs b/src/tools/indicator/src/main.rs index b2c7a014..a7f414f8 100644 --- a/src/tools/indicator/src/main.rs +++ b/src/tools/indicator/src/main.rs @@ -2,13 +2,10 @@ use anyhow::Result; use kime_engine_core::{load_raw_config_from_config_dir, IconColor}; use ksni::menu::*; use ksni::TrayMethods; -use std::net::Shutdown; -use std::os::unix::net::{UnixListener, UnixStream}; use std::path::Path; -use std::{ - io::{Read, Write}, - time::Duration, -}; +use std::time::Duration; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::{UnixListener, UnixStream}; #[derive(Clone, Copy, Debug)] enum InputCategory { @@ -83,19 +80,25 @@ impl KimeTray { const EXIT_MESSAGE: &[u8; 1] = b"Z"; -fn try_terminate_previous_server(file_path: &Path) -> Result<()> { - let mut client = UnixStream::connect(file_path)?; +async fn try_terminate_previous_server(file_path: &Path) -> Result<()> { + let mut client = UnixStream::connect(file_path).await?; - client.write_all(EXIT_MESSAGE)?; + client.write_all(EXIT_MESSAGE).await?; Ok(()) } async fn indicator_server(file_path: &Path, color: IconColor) -> Result<()> { - let handle = KimeTray::new(color).spawn().await?; + // Use assume_sni_available to handle the case when desktop environment + // is not fully initialized yet. This allows the tray to appear later + // when StatusNotifierWatcher becomes available. + let handle = KimeTray::new(color) + .assume_sni_available(true) + .spawn() + .await?; if file_path.exists() { - try_terminate_previous_server(file_path).ok(); + try_terminate_previous_server(file_path).await.ok(); std::fs::remove_file(file_path).ok(); } @@ -105,13 +108,19 @@ async fn indicator_server(file_path: &Path, color: IconColor) -> Result<()> { let mut read_buf = [0; 1]; loop { - let mut client = listener.accept()?.0; - client.set_read_timeout(Some(Duration::from_secs(2))).ok(); - client.set_write_timeout(Some(Duration::from_secs(2))).ok(); - client.write_all(¤t_bytes).ok(); - client.shutdown(Shutdown::Write).ok(); - match client.read_exact(&mut read_buf) { - Ok(_) => { + let (mut client, _) = listener.accept().await?; + + let timeout = Duration::from_secs(2); + + // Write current state with timeout + let _ = tokio::time::timeout(timeout, client.write_all(¤t_bytes)).await; + + // Shutdown write side + let _ = client.shutdown().await; + + // Read with timeout + match tokio::time::timeout(timeout, client.read_exact(&mut read_buf)).await { + Ok(Ok(_)) => { if &read_buf == EXIT_MESSAGE { log::info!("Receive exit message"); return Ok(());