diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 224bb8f3..63abe526 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,10 @@ -The core team is busy and does not have time to mentor nor babysit new contributors. If a member of the core team thinks that reviewing and understanding your work will take more time and effort than writing it from scratch by themselves, your contribution will be dismissed. It is your responsibility to communicate and figure out how to reduce the likelihood of this! +Replace this text with a description of your changes, and why these changes are important. Read and acknowledge the list below. PRs without a completed checkbox will be closed. -Read the contributing guidelines for more details: https://github.com/iced-rs/iced/blob/master/CONTRIBUTING.md +--- + +- I have not included any LLM (also known as AI) generated content in this PR, including code, comments, and descriptions. +- I understand these changes in full and will be able to respond to review comments. +- My change is accurately described in the commit message. +- My contribution is tested and working as described. +- I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions. +- [ ] I have read and followed all of the above requirements. For more information, see our [contributor guide](https://github.com/pop-os/pop/blob/master/CONTRIBUTING.md). diff --git a/.github/workflows/require-checklist.yml b/.github/workflows/require-checklist.yml new file mode 100644 index 00000000..fd4ca8c6 --- /dev/null +++ b/.github/workflows/require-checklist.yml @@ -0,0 +1,14 @@ +# From https://github.com/mheap/require-checklist-action, license MIT +name: Require Checklist + +on: + pull_request: + types: [opened, edited, synchronize] + +jobs: + job1: + runs-on: ubuntu-latest + steps: + - uses: mheap/require-checklist-action@v2 + with: + requireChecklist: true diff --git a/Cargo.lock b/Cargo.lock index 805e6066..de62ab66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "accesskit" version = "0.22.0" -source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" dependencies = [ "uuid", ] @@ -29,7 +29,7 @@ dependencies = [ [[package]] name = "accesskit_atspi_common" version = "0.15.0" -source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" dependencies = [ "accesskit", "accesskit_consumer", @@ -41,7 +41,7 @@ dependencies = [ [[package]] name = "accesskit_consumer" version = "0.32.0" -source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" dependencies = [ "accesskit", "hashbrown 0.16.1", @@ -50,7 +50,7 @@ dependencies = [ [[package]] name = "accesskit_macos" version = "0.23.0" -source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" dependencies = [ "accesskit", "accesskit_consumer", @@ -63,7 +63,7 @@ dependencies = [ [[package]] name = "accesskit_unix" version = "0.18.0" -source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -82,7 +82,7 @@ dependencies = [ [[package]] name = "accesskit_windows" version = "0.30.0" -source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" dependencies = [ "accesskit", "accesskit_consumer", @@ -95,7 +95,7 @@ dependencies = [ [[package]] name = "accesskit_winit" version = "0.30.0" -source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" dependencies = [ "accesskit", "accesskit_macos", @@ -126,9 +126,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -157,6 +157,12 @@ dependencies = [ "equator", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-activity" version = "0.6.1" @@ -164,23 +170,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" dependencies = [ "android-properties", - "bitflags 2.11.0", + "bitflags 2.13.2", "cc", - "jni 0.22.4", + "jni", "libc", "log", "ndk", "ndk-context", "ndk-sys", "num_enum", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] name = "android-build" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cac4c64175d504608cf239756339c07f6384a476f97f20a7043f92920b0b8fd" +checksum = "f9fc9904ad2ad097c3c1cfe2eacaaf0fc24710936fa9ed941cb310b7c6ed2ab7" dependencies = [ "windows-sys 0.52.0", ] @@ -193,9 +199,9 @@ checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] @@ -214,9 +220,9 @@ checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "approx" @@ -248,7 +254,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -259,9 +265,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "as-raw-xcb-connection" @@ -298,14 +304,14 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.2", + "rand 0.9.5", "raw-window-handle", "serde", "serde_repr", "url", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "zbus", ] @@ -315,7 +321,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.4.1", + "event-listener 5.4.2", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -397,7 +403,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.1.4", + "rustix 1.1.5", "slab", "windows-sys 0.61.2", ] @@ -408,7 +414,7 @@ version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ - "event-listener 5.4.1", + "event-listener 5.4.2", "event-listener-strategy", "pin-project-lite", ] @@ -437,9 +443,9 @@ dependencies = [ "async-task", "blocking", "cfg-if", - "event-listener 5.4.1", + "event-listener 5.4.2", "futures-lite", - "rustix 1.1.4", + "rustix 1.1.5", ] [[package]] @@ -450,7 +456,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -465,7 +471,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.1.4", + "rustix 1.1.5", "signal-hook-registry", "slab", "windows-sys 0.61.2", @@ -505,13 +511,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -539,9 +545,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atomic_refcell" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" +checksum = "21e4227379beff4205943696e6c3e0cd809bacdf3f0edd6e3dd153e2269571a4" [[package]] name = "atspi" @@ -582,9 +588,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "av-scenechange" @@ -601,7 +607,7 @@ dependencies = [ "num-traits", "pastey", "rayon", - "thiserror 2.0.18", + "thiserror 2.0.20", "v_frame", "y4m", ] @@ -622,9 +628,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" dependencies = [ "arrayvec", ] @@ -641,6 +647,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bezier_tool" version = "0.1.0" @@ -706,17 +718,17 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "bitstream-io" -version = "4.9.0" +version = "4.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" dependencies = [ - "core2", + "no_std_io2", ] [[package]] @@ -754,9 +766,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" dependencies = [ "async-channel 2.5.0", "async-task", @@ -773,25 +785,32 @@ checksum = "e79769241dcd44edf79a732545e8b5cec84c247ac060f5252cd51885d093a8fc" [[package]] name = "borsh" -version = "1.6.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a" dependencies = [ "bytes", "cfg_aliases", ] +[[package]] +name = "build_helpers" +version = "0.14.0" +dependencies = [ + "cfg_aliases", +] + [[package]] name = "built" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" +checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "by_address" @@ -801,22 +820,22 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.2" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -833,9 +852,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "calloop" @@ -843,7 +862,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "log", "polling", "rustix 0.38.44", @@ -857,7 +876,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "log", "polling", "rustix 0.38.44", @@ -871,9 +890,9 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "polling", - "rustix 1.1.4", + "rustix 1.1.5", "slab", "tracing", ] @@ -897,7 +916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" dependencies = [ "calloop 0.14.4", - "rustix 1.1.4", + "rustix 1.1.5", "wayland-backend", "wayland-client", ] @@ -922,9 +941,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.60" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313" dependencies = [ "find-msvc-tools", "jobserver", @@ -932,17 +951,11 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-expr" -version = "0.20.7" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" +checksum = "fe4ece8474b5f766c63426647e7b4b316b67431ade1036a8313cee24a03ae917" dependencies = [ "smallvec", "target-lexicon", @@ -950,25 +963,25 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", - "rand_core 0.10.0", + "cpufeatures 0.3.1", + "rand_core 0.10.1", ] [[package]] @@ -980,7 +993,7 @@ dependencies = [ "log", "reqwest", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tracing-subscriber", "webbrowser", @@ -995,9 +1008,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -1035,18 +1048,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" dependencies = [ "anstyle", "clap_lex", @@ -1054,9 +1067,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" [[package]] name = "clipboard-win" @@ -1162,9 +1175,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "combine" -version = "4.6.7" +version = "4.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" dependencies = [ "bytes", "memchr", @@ -1198,9 +1211,9 @@ dependencies = [ [[package]] name = "console_log" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f" +checksum = "86919cef3e37b9356ccf54d4421208c17ecfda01beae61393e7ffd72916c0ef1" dependencies = [ "log", "web-sys", @@ -1262,19 +1275,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "core-foundation 0.10.1", "libc", ] [[package]] -name = "core2" -version = "0.4.0" +name = "core_detect" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" [[package]] name = "core_maths" @@ -1288,25 +1298,25 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" version = "0.2.0" -source = "git+https://github.com/pop-os/cosmic-protocols?rev=160b086#160b086abe03cd34a8a375d7fbe47b24308d1f38" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=c0cff4d#c0cff4db14c37ed954983158e4055aa94c7741d9" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cosmic-protocols", "libc", "smithay-client-toolkit 0.20.0", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", ] [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os/cosmic-protocols?rev=160b086#160b086abe03cd34a8a375d7fbe47b24308d1f38" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=c0cff4d#c0cff4db14c37ed954983158e4055aa94c7741d9" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "wayland-protocols-wlr 0.3.12", "wayland-scanner", "wayland-server", @@ -1314,18 +1324,19 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.18.2" -source = "git+https://github.com/pop-os/cosmic-text.git#4d74f795cc771fdcc7ea0f9cacba63fcf036fad6" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "fontdb", "harfrust", "linebender_resource_handle", "log", "rangemap", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "self_cell", - "skrifa", + "skrifa 0.40.0", "smol_str", "swash", "sys-locale", @@ -1353,18 +1364,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" dependencies = [ "cfg-if", ] @@ -1407,9 +1418,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1417,18 +1428,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" [[package]] name = "crunchy" @@ -1439,12 +1450,12 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "git+https://github.com/pop-os/glyphon.git?tag=cosmic-0.14#c49de15bce4d8254ac136d1be9911960cc85ce12" +source = "git+https://github.com/iced-rs/cryoglyph.git?rev=e429a025df36ab8145708acb309080ae3deec17a#e429a025df36ab8145708acb309080ae3deec17a" dependencies = [ "cosmic-text", "etagere", "lru", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "wgpu", ] @@ -1459,10 +1470,13 @@ dependencies = [ ] [[package]] -name = "ctor-lite" -version = "0.1.2" +name = "ctor" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e162d0c2e2068eb736b71e5597eff0b9944e6b973cd9f37b6a288ab9bf20e300" +checksum = "83cf0d42651b16c6dfe68685716d18480d18a9c39c62d76e8cf3eb6ed5d8bcbf" +dependencies = [ + "dtor", +] [[package]] name = "cursor-icon" @@ -1485,7 +1499,7 @@ dependencies = [ "glam", "iced", "image", - "rand 0.8.5", + "rand 0.8.8", ] [[package]] @@ -1497,9 +1511,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "data-url" @@ -1507,6 +1521,37 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.20", +] + [[package]] name = "delineate" version = "0.1.0" @@ -1519,9 +1564,6 @@ name = "deranged" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] [[package]] name = "digest" @@ -1560,8 +1602,8 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users 0.5.2", - "windows-sys 0.60.2", + "redox_users 0.5.3", + "windows-sys 0.61.2", ] [[package]] @@ -1587,7 +1629,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -1595,13 +1637,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -1618,7 +1660,7 @@ name = "dnd" version = "0.1.0" source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "mime 0.1.0", "raw-window-handle", "smithay-client-toolkit 0.20.0", @@ -1651,7 +1693,7 @@ dependencies = [ [[package]] name = "dpi" version = "0.1.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" [[package]] name = "drm" @@ -1659,7 +1701,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "drm-ffi", "drm-fourcc", @@ -1692,6 +1734,12 @@ dependencies = [ "linux-raw-sys 0.6.5", ] +[[package]] +name = "dtor" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b" + [[package]] name = "editor" version = "0.1.0" @@ -1703,17 +1751,23 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "encoding_rs" -version = "0.8.35" +version = "0.8.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "7b5ef0006ac9ab233c38522f5ae99cae3625151de8f706cacee1cba4b8e2832a" dependencies = [ "cfg-if", + "core_detect", + "multiversion", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", ] [[package]] @@ -1740,7 +1794,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1773,7 +1827,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1789,14 +1843,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "error-code" -version = "3.3.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7" [[package]] name = "etagere" @@ -1825,11 +1879,10 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] @@ -1840,7 +1893,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener 5.4.2", "pin-project-lite", ] @@ -1860,14 +1913,16 @@ dependencies = [ [[package]] name = "exr" -version = "1.74.0" +version = "1.74.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +checksum = "711fe42c9964295e01ee3fba3f9fe0e1d24b98886950d68efe81b1c76e21adf3" dependencies = [ "bit_field", "half", "lebe", - "miniz_oxide", + "miniz_oxide 0.8.9", + "num-complex", + "pulp", "rayon-core", "smallvec", "zune-inflate", @@ -1884,37 +1939,17 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "fast-srgb8" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" - [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "fax" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" [[package]] name = "fdeflate" @@ -1934,18 +1969,19 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "ef25905e51abafe4dcea6c15fec58c57b601cdbd0ee53d22ea1d3016c587d39b" [[package]] name = "flate2" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.9.1", + "zlib-rs", ] [[package]] @@ -1980,9 +2016,18 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "font-types" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9237c6d82152100c691fb77ea18037b402bcc7257d2c876a4ffac81bc22a1c" +checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "font-types" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8eb065f3251655b3c90e22e5e363f310fc5332fb3402e37bbc94752283248f6" dependencies = [ "bytemuck", ] @@ -2004,7 +2049,7 @@ checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", - "memmap2 0.9.10", + "memmap2 0.9.11", "slotmap", "tinyvec", "ttf-parser", @@ -2031,13 +2076,13 @@ dependencies = [ [[package]] name = "foreign-types-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -2063,9 +2108,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -2078,9 +2123,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -2088,15 +2133,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -2105,9 +2150,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-lite" @@ -2124,32 +2169,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -2182,7 +2227,7 @@ version = "0.1.0" dependencies = [ "iced", "itertools 0.12.1", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "tokio", "tracing-subscriber", ] @@ -2210,7 +2255,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "rustix 1.1.4", + "rustix 1.1.5", "windows-link 0.2.1", ] @@ -2250,16 +2295,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "rand_core 0.10.0", - "wasip2", - "wasip3", + "rand_core 0.10.1", ] [[package]] @@ -2292,7 +2335,7 @@ dependencies = [ "gobject-sys", "libc", "system-deps", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2321,7 +2364,7 @@ version = "0.20.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc4b6e352d4716d84d7dde562dd9aee2a7d48beb872dd9ece7f2d1515b2d683" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "futures-channel", "futures-core", "futures-executor", @@ -2346,7 +2389,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2403,35 +2446,18 @@ dependencies = [ "system-deps", ] -[[package]] -name = "gpu-alloc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" -dependencies = [ - "bitflags 2.11.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.11.0", -] - [[package]] name = "gpu-allocator" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" dependencies = [ + "ash", + "hashbrown 0.16.1", "log", "presser", - "thiserror 1.0.69", - "windows 0.58.0", + "thiserror 2.0.20", + "windows 0.62.2", ] [[package]] @@ -2440,7 +2466,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -2451,7 +2477,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] @@ -2484,7 +2510,7 @@ dependencies = [ "paste", "pin-project-lite", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -2594,7 +2620,7 @@ dependencies = [ "gstreamer-video-sys", "libc", "once_cell", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -2642,16 +2668,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.13" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.4.0", + "http 1.5.0", "indexmap", "slab", "tokio", @@ -2677,10 +2703,10 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9da2e5ae821f6e96664977bf974d6d6a2d6682f9ccee23e62ec1d134246845f9" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "core_maths", - "read-fonts", + "read-fonts 0.37.0", "smallvec", ] @@ -2699,14 +2725,16 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "headers" @@ -2746,9 +2774,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" [[package]] name = "hex" @@ -2775,9 +2803,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -2796,24 +2824,24 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", - "http 1.4.0", + "http 1.5.0", ] [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", - "http 1.4.0", - "http-body 1.0.1", + "http 1.5.0", + "http-body 1.1.0", "pin-project-lite", ] @@ -2831,9 +2859,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" [[package]] name = "hyper" @@ -2861,17 +2889,17 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2 0.4.13", - "http 1.4.0", - "http-body 1.0.1", + "h2 0.4.19", + "http 1.5.0", + "http-body 1.1.0", "httparse", "itoa", "pin-project-lite", @@ -2882,17 +2910,16 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" dependencies = [ - "http 1.4.0", - "hyper 1.9.0", + "http 1.5.0", + "hyper 1.11.1", "hyper-util", - "rustls 0.23.37", - "rustls-pki-types", + "rustls 0.23.45", "tokio", - "tokio-rustls 0.26.4", + "tokio-rustls 0.26.5", "tower-service", ] @@ -2904,7 +2931,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.9.0", + "hyper 1.11.1", "hyper-util", "native-tls", "tokio", @@ -2922,14 +2949,14 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.4.0", - "http-body 1.0.1", - "hyper 1.9.0", + "http 1.5.0", + "http-body 1.1.0", + "hyper 1.11.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.6.5", "system-configuration", "tokio", "tower-service", @@ -2965,6 +2992,7 @@ dependencies = [ name = "iced" version = "0.14.0" dependencies = [ + "build_helpers", "criterion", "dnd", "iced_accessibility", @@ -2982,7 +3010,7 @@ dependencies = [ "iced_winit", "image", "mime 0.1.0", - "thiserror 2.0.18", + "thiserror 2.0.20", "window_clipboard", ] @@ -3006,7 +3034,7 @@ dependencies = [ "log", "semver", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", ] @@ -3015,7 +3043,8 @@ name = "iced_core" version = "0.14.0" dependencies = [ "approx", - "bitflags 2.11.0", + "bitflags 2.13.2", + "build_helpers", "bytes", "cosmic-client-toolkit", "dnd", @@ -3027,10 +3056,11 @@ dependencies = [ "num-traits", "palette", "raw-window-handle", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "serde", "smol_str", - "thiserror 2.0.18", + "thiserror 2.0.20", + "unicode-segmentation", "web-time", "window_clipboard", ] @@ -3063,7 +3093,7 @@ dependencies = [ "futures", "iced_core", "log", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "smol", "tokio", "wasm-bindgen-futures", @@ -3074,7 +3104,7 @@ dependencies = [ name = "iced_graphics" version = "0.14.0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "cosmic-text", "half", @@ -3085,8 +3115,8 @@ dependencies = [ "log", "lyon_path", "raw-window-handle", - "rustc-hash 2.1.2", - "thiserror 2.0.18", + "rustc-hash 2.1.3", + "thiserror 2.0.20", "unicode-segmentation", ] @@ -3114,13 +3144,14 @@ dependencies = [ "iced_tiny_skia", "iced_wgpu", "log", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] name = "iced_runtime" version = "0.14.0" dependencies = [ + "build_helpers", "bytes", "cosmic-client-toolkit", "dnd", @@ -3130,7 +3161,7 @@ dependencies = [ "iced_selector", "raw-window-handle", "sipper", - "thiserror 2.0.18", + "thiserror 2.0.20", "window_clipboard", ] @@ -3153,7 +3184,7 @@ dependencies = [ "nom", "png 0.18.1", "sha2", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -3177,9 +3208,9 @@ dependencies = [ "kurbo 0.10.4", "log", "resvg", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "softbuffer", - "tiny-skia", + "tiny-skia 0.11.4", ] [[package]] @@ -3187,7 +3218,8 @@ name = "iced_wgpu" version = "0.14.0" dependencies = [ "as-raw-xcb-connection", - "bitflags 2.11.0", + "bitflags 2.13.2", + "build_helpers", "bytemuck", "cosmic-client-toolkit", "cryoglyph", @@ -3200,13 +3232,13 @@ dependencies = [ "lyon", "raw-window-handle", "resvg", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "rustix 0.38.44", - "thiserror 2.0.18", + "thiserror 2.0.20", "tiny-xlib", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "wayland-sys", "wgpu", "x11rb", @@ -3216,6 +3248,7 @@ dependencies = [ name = "iced_widget" version = "0.14.2" dependencies = [ + "build_helpers", "cosmic-client-toolkit", "dnd", "iced_accessibility", @@ -3227,8 +3260,8 @@ dependencies = [ "ouroboros", "pulldown-cmark", "qrcode", - "rustc-hash 2.1.2", - "thiserror 2.0.18", + "rustc-hash 2.1.3", + "thiserror 2.0.20", "unicode-segmentation", "window_clipboard", ] @@ -3237,6 +3270,7 @@ dependencies = [ name = "iced_winit" version = "0.14.0" dependencies = [ + "build_helpers", "cosmic-client-toolkit", "cursor-icon", "dnd", @@ -3249,14 +3283,14 @@ dependencies = [ "log", "mundy", "raw-window-handle", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "rustix 0.38.44", "sysinfo", - "thiserror 2.0.18", + "thiserror 2.0.20", "wasm-bindgen-futures", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "web-sys", "winapi", "window_clipboard", @@ -3269,9 +3303,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -3283,9 +3317,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -3296,9 +3330,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -3310,16 +3344,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -3330,15 +3365,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -3349,12 +3384,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "idna" version = "1.1.0" @@ -3368,9 +3397,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -3396,7 +3425,7 @@ dependencies = [ "rayon", "rgb", "tiff", - "zune-core 0.5.1", + "zune-core 0.5.3", "zune-jpeg 0.5.15", ] @@ -3418,20 +3447,18 @@ checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" [[package]] name = "imgref" -version = "1.12.0" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" +checksum = "6e44b0a4eaa4c82f441d50a963f2d5f05a787240aeee097597033e72accfd22f" [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", - "hashbrown 0.17.0", - "serde", - "serde_core", + "hashbrown 0.17.1", ] [[package]] @@ -3442,24 +3469,14 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" [[package]] name = "is-docker" @@ -3478,7 +3495,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3526,35 +3543,48 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb" dependencies = [ + "defmt", + "jiff-core", "jiff-static", "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.52.0", + "windows-link 0.2.1", +] + +[[package]] +name = "jiff-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" +dependencies = [ + "defmt", + "log", ] [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212" dependencies = [ + "jiff-core", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "jiff-tzdb" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" [[package]] name = "jiff-tzdb-platform" @@ -3565,22 +3595,6 @@ dependencies = [ "jiff-tzdb", ] -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - [[package]] name = "jni" version = "0.22.4" @@ -3593,7 +3607,7 @@ dependencies = [ "jni-sys 0.4.1", "log", "simd_cesu8", - "thiserror 2.0.18", + "thiserror 2.0.20", "walkdir", "windows-link 0.2.1", ] @@ -3608,7 +3622,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.119", ] [[package]] @@ -3636,28 +3650,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -3676,7 +3689,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "serde", ] @@ -3747,12 +3760,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "lebe" version = "0.5.3" @@ -3761,15 +3768,15 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libc" -version = "0.2.184" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libfuzzer-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" dependencies = [ "arbitrary", "cc", @@ -3793,21 +3800,21 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "libc", "plain", - "redox_syscall 0.7.4", + "redox_syscall 0.9.4", ] [[package]] name = "lilt" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f67562e5eff6b20553fa9be1c503356768420994e28f67e3eafe6f41910e57ad" +checksum = "337d4c256f7d9f2dbd633891d48ace853efa5b1554122d9220b172ad9c03c3a9" dependencies = [ "web-time", ] @@ -3851,9 +3858,9 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "litrs" @@ -3880,9 +3887,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" dependencies = [ "value-bag", ] @@ -3905,9 +3912,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" [[package]] name = "lyon" @@ -3921,9 +3928,9 @@ dependencies = [ [[package]] name = "lyon_algorithms" -version = "1.0.19" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9815fac08e6fd96733a11dce4f9d15a3f338e96a2e2311ee21e1b738efc2bc0f" +checksum = "cdfa8785f95e57914ddb35e3b59994aeba6f5e79e9cfd03da1c269f010f36009" dependencies = [ "lyon_path", "num-traits", @@ -3952,9 +3959,9 @@ dependencies = [ [[package]] name = "lyon_tessellation" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e43b7e44161571868f5c931d12583592c223c5583eef86b08aa02b7048a3552" +checksum = "43b8dcf906637ecef61b3c0740c7a4e7f27caeb31257cfac0cc579ce15be6005" dependencies = [ "float_next_after", "lyon_path", @@ -4003,17 +4010,17 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memfd" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" +checksum = "57804b2c9b69967f1536a56f86297e367a33b19e98852ed624b84551cdbc0d90" dependencies = [ - "rustix 1.1.4", + "rustix 1.1.5", ] [[package]] @@ -4027,9 +4034,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -4045,11 +4052,11 @@ dependencies = [ [[package]] name = "metal" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" +checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -4093,10 +4100,20 @@ dependencies = [ ] [[package]] -name = "mio" -version = "1.2.0" +name = "miniz_oxide" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" dependencies = [ "libc", "wasi", @@ -4161,10 +4178,37 @@ dependencies = [ ] [[package]] -name = "mundy" -version = "0.2.2" +name = "multiversion" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523813c9e194ec43693805214eb112551f99382115b67f38600d724a692e7e8b" +checksum = "b4ca4bea16ffc3f443cf7d866912118196bfef4c6a1556ca00f9f9b00bb43f7c" +dependencies = [ + "multiversion-macros", +] + +[[package]] +name = "multiversion-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d416831a7317ef4b08bee00b69cbbb9c8763da7959a7026244d6266869f9c83" +dependencies = [ + "proc-macro2", + "quote", + "rustversion", + "syn 3.0.6", +] + +[[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + +[[package]] +name = "mundy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f32eb0db40f2df2bcfb05c93b8f73938d4c26ce9ac8881f1df0c8d3296921a73" dependencies = [ "android-build", "async-io", @@ -4172,7 +4216,7 @@ dependencies = [ "dispatch", "futures-channel", "futures-lite", - "jni 0.21.1", + "jni", "ndk-context", "objc2 0.6.4", "objc2-app-kit 0.3.2", @@ -4193,13 +4237,13 @@ checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" [[package]] name = "naga" -version = "27.0.3" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" +checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -4213,7 +4257,7 @@ dependencies = [ "once_cell", "rustc-hash 1.1.0", "spirv", - "thiserror 2.0.18", + "thiserror 2.0.20", "unicode-ident", ] @@ -4240,7 +4284,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "jni-sys 0.3.1", "log", "ndk-sys", @@ -4270,6 +4314,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + [[package]] name = "nom" version = "8.0.0" @@ -4300,24 +4353,34 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", ] [[package]] -name = "num-conv" -version = "0.2.1" +name = "num-complex" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "bytemuck", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-derive" @@ -4327,14 +4390,14 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] @@ -4379,7 +4442,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -4433,7 +4496,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -4449,7 +4512,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -4470,7 +4533,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2 0.6.4", "objc2-foundation 0.3.2", ] @@ -4481,7 +4544,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -4493,7 +4556,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2 0.6.4", "objc2-foundation 0.3.2", ] @@ -4504,7 +4567,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.6.2", "dispatch2", "objc2 0.6.4", @@ -4516,7 +4579,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "dispatch2", "libc", "objc2 0.6.4", @@ -4552,7 +4615,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", @@ -4564,7 +4627,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", @@ -4583,7 +4646,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -4595,7 +4658,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -4608,7 +4671,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2 0.6.4", "objc2-core-foundation", ] @@ -4619,7 +4682,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -4631,7 +4694,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -4644,7 +4707,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2 0.6.4", "objc2-foundation 0.3.2", ] @@ -4655,7 +4718,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -4684,26 +4747,24 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "open" -version = "5.3.3" +version = "5.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" +checksum = "aa576c76302b7b808eecc68061e67336c47833ef9d22caa74dda10fa9675eebc" dependencies = [ "is-wsl", "libc", - "pathdiff", ] [[package]] name = "openssl" -version = "0.10.76" +version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg-if", "foreign-types 0.3.2", "libc", - "once_cell", "openssl-macros", "openssl-sys", ] @@ -4716,7 +4777,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -4727,9 +4788,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.112" +version = "0.9.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" dependencies = [ "cc", "libc", @@ -4754,9 +4815,9 @@ dependencies = [ [[package]] name = "orbclient" -version = "0.3.51" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59aed3b33578edcfa1bc96a321d590d31832b6ad55a26f0313362ce687e9abd6" +checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" dependencies = [ "libc", "libredox", @@ -4764,9 +4825,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "5.3.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0" dependencies = [ "num-traits", ] @@ -4802,7 +4863,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -4816,28 +4877,34 @@ dependencies = [ [[package]] name = "palette" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +checksum = "ddeed8580d347d2abf3dcf06a5f0b3dc020258338526b277847cd4248a70fc64" dependencies = [ "approx", - "fast-srgb8", "palette_derive", + "palette_math", "phf", ] [[package]] name = "palette_derive" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +checksum = "88537020289b719d81be994ccf1bbf4990f477e2f69ee52fe3e45f43a02e56be" dependencies = [ "by_address", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] +[[package]] +name = "palette_math" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6eb142958d64335fb0e345c5b9ead2ecd6fc438c307e9d7d3c4fd428dbaf12" + [[package]] name = "pane_grid" version = "0.1.0" @@ -4886,12 +4953,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - [[package]] name = "percent-encoding" version = "2.3.2" @@ -4900,42 +4961,18 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn", ] [[package]] name = "phf_shared" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ "siphasher", ] @@ -4955,22 +4992,22 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -4998,9 +5035,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "plain" @@ -5010,13 +5047,13 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "plist" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" +checksum = "2896bade328c13f7042a297ea5ac5b0951f6cf989dea5f32c2fd98da398195cb" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "indexmap", - "quick-xml 0.38.4", + "quick-xml 0.42.0", "serde", "time", ] @@ -5059,7 +5096,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.8.9", ] [[package]] @@ -5068,11 +5105,11 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.8.9", ] [[package]] @@ -5081,7 +5118,7 @@ version = "0.1.0" dependencies = [ "getrandom 0.2.17", "iced", - "rand 0.8.5", + "rand 0.8.8", "reqwest", "serde", "serde_json", @@ -5097,7 +5134,7 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.1.4", + "rustix 1.1.5", "windows-sys 0.61.2", ] @@ -5109,24 +5146,24 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -5152,16 +5189,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -5173,9 +5200,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -5188,28 +5215,28 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "version_check", "yansi", ] [[package]] name = "profiling" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -5225,7 +5252,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "getopts", "memchr", "pulldown-cmark-escape", @@ -5239,10 +5266,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" [[package]] -name = "pxfm" -version = "0.1.28" +name = "pulp" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" +checksum = "046aa45b989642ec2e4717c8e72d677b13edd831a4d3b6cf37d9a3e54912496a" +dependencies = [ + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "paste", + "pulp-wasm-simd-flag", + "raw-cpuid", + "reborrow", + "version_check", +] + +[[package]] +name = "pulp-wasm-simd-flag" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8f70e07b9c3962945a74e59ca1c511bba65b6419468acc217c457d93f3c740" + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" [[package]] name = "qoi" @@ -5274,28 +5324,27 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", - "serde", ] [[package]] name = "quick-xml" -version = "0.39.2" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -5314,9 +5363,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -5325,9 +5374,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -5335,13 +5384,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" dependencies = [ "chacha20", - "getrandom 0.4.2", - "rand_core 0.10.0", + "getrandom 0.4.3", + "rand_core 0.10.1", ] [[package]] @@ -5384,9 +5433,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "range-alloc" @@ -5396,9 +5445,9 @@ checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" [[package]] name = "rangemap" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" +checksum = "a611d15b50743feb4c76b7d03edcb0e64f399c26961e4efe6975bc398be6aa3d" [[package]] name = "rav1e" @@ -5427,10 +5476,10 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand 0.9.2", + "rand 0.9.5", "rand_chacha 0.9.0", "simd_helpers", - "thiserror 2.0.18", + "thiserror 2.0.20", "v_frame", "wasm-bindgen", ] @@ -5450,6 +5499,15 @@ dependencies = [ "rgb", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.13.2", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -5458,9 +5516,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -5484,7 +5542,34 @@ checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" dependencies = [ "bytemuck", "core_maths", - "font-types", + "font-types 0.11.3", +] + +[[package]] +name = "read-fonts" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709" +dependencies = [ + "bytemuck", + "font-types 0.12.5", + "once_cell", +] + +[[package]] +name = "reborrow" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" + +[[package]] +name = "redox_event" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5018d583d6d2f5499352aea8d177e9067d1eb03ab17c78169d5ba7a30001b15" +dependencies = [ + "bitflags 2.13.2", + "libredox", ] [[package]] @@ -5493,16 +5578,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] name = "redox_syscall" -version = "0.7.4" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] @@ -5518,20 +5603,19 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0" dependencies = [ - "getrandom 0.2.17", "libredox", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -5541,9 +5625,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -5552,9 +5636,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "renderdoc-sys" @@ -5573,11 +5657,11 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.13", - "http 1.4.0", - "http-body 1.0.1", + "h2 0.4.19", + "http 1.5.0", + "http-body 1.1.0", "http-body-util", - "hyper 1.9.0", + "hyper 1.11.1", "hyper-rustls", "hyper-tls", "hyper-util", @@ -5617,7 +5701,7 @@ dependencies = [ "pico-args", "rgb", "svgtypes", - "tiny-skia", + "tiny-skia 0.11.4", "usvg", "zune-jpeg 0.4.21", ] @@ -5683,9 +5767,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -5702,24 +5786,24 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustix" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5738,22 +5822,22 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" dependencies = [ "once_cell", "rustls-pki-types", - "rustls-webpki 0.103.11", + "rustls-webpki 0.103.15", "subtle", "zeroize", ] [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] @@ -5771,9 +5855,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.11" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20a6af516fea4b20eccceaf166e8aa666ac996208e8a644ce3ef5aa783bc7cd4" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -5782,9 +5866,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rustybuzz" @@ -5792,7 +5876,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "core_maths", "log", @@ -5859,15 +5943,15 @@ dependencies = [ [[package]] name = "sctk-adwaita" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd3accc0f3f4bbaf2c9e1957a030dc582028130c67660d44c0a0345a22ca69b" +checksum = "ea2a4360b9abcdcee43809e6fc11d6d61ca5d25734026a8fc46c2883eea3f13f" dependencies = [ "ab_glyph", "log", - "memmap2 0.9.10", + "memmap2 0.9.11", "smithay-client-toolkit 0.20.0", - "tiny-skia", + "tiny-skia 0.12.0", ] [[package]] @@ -5965,7 +6049,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5984,9 +6068,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" [[package]] name = "semver" @@ -6000,9 +6084,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -6010,29 +6094,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -6043,13 +6127,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -6075,9 +6159,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -6106,16 +6190,16 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "sierpinski_triangle" version = "0.1.0" dependencies = [ "iced", - "rand 0.8.5", + "rand 0.8.8", ] [[package]] @@ -6130,15 +6214,15 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simd_cesu8" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" dependencies = [ "rustc_version", "simdutf8", @@ -6170,9 +6254,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "sipper" @@ -6191,7 +6275,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" dependencies = [ "bytemuck", - "read-fonts", + "read-fonts 0.37.0", +] + +[[package]] +name = "skrifa" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68" +dependencies = [ + "bytemuck", + "read-fonts 0.41.0", ] [[package]] @@ -6218,23 +6312,23 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.1" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" [[package]] name = "smithay-client-toolkit" version = "0.18.0" source = "git+https://github.com/smithay/client-toolkit?rev=828b1eb#828b1eb469ad022eb3ac30be02bd5645b879922f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "calloop 0.12.4", "calloop-wayland-source 0.2.0", "cursor-icon", "libc", "log", - "memmap2 0.9.10", + "memmap2 0.9.11", "pkg-config", "rustix 0.38.44", "thiserror 1.0.69", @@ -6255,22 +6349,22 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "calloop 0.14.4", "calloop-wayland-source 0.4.1", "cursor-icon", "libc", "log", - "memmap2 0.9.10", + "memmap2 0.9.11", "pkg-config", - "rustix 1.1.4", - "thiserror 2.0.18", + "rustix 1.1.5", + "thiserror 2.0.20", "wayland-backend", "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "wayland-protocols-experimental", "wayland-protocols-misc", "wayland-protocols-wlr 0.3.12", @@ -6329,18 +6423,18 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "softbuffer" version = "0.4.1" -source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#a3f77e251e7422803f693df6e3fc313c010c4dcb" +source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#c2b2c19ddb38ff17495643699f97cb1f2064a1be" dependencies = [ "as-raw-xcb-connection", "bytemuck", @@ -6352,7 +6446,7 @@ dependencies = [ "foreign-types 0.5.0", "js-sys", "log", - "memmap2 0.9.10", + "memmap2 0.9.11", "objc", "raw-window-handle", "redox_syscall 0.5.18", @@ -6372,15 +6466,15 @@ name = "solar_system" version = "0.1.0" dependencies = [ "iced", - "rand 0.8.5", + "rand 0.8.8", "tracing-subscriber", ] [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" [[package]] name = "spirv" @@ -6388,7 +6482,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] @@ -6430,18 +6524,18 @@ dependencies = [ [[package]] name = "subsecond" -version = "0.7.4" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dbb9f2928b6654ccc28d4ddfef5213e97ed66afed4907774d049b376c62a838" +checksum = "d350d5788fa94d560d92269266a50efc77b036bfca6675e420b64df7b3211f37" dependencies = [ "js-sys", "libc", "libloading", "memfd", - "memmap2 0.9.10", + "memmap2 0.9.11", "serde", "subsecond-types", - "thiserror 2.0.18", + "thiserror 2.0.20", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -6449,9 +6543,9 @@ dependencies = [ [[package]] name = "subsecond-types" -version = "0.7.4" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388bb28e6ddbee717745963b8932d9a6e24a5d3c93350655f733e938de04d81f" +checksum = "dcf32d66269b5fbb8558334e8d22b657b2f7af0dd2ef56c231541099a862e464" dependencies = [ "serde", ] @@ -6487,20 +6581,31 @@ dependencies = [ [[package]] name = "swash" -version = "0.2.7" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "842f3cd369c2ba38966204f983eaa5e54a8e84a7d7159ed36ade2b6c335aae64" +checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e" dependencies = [ - "skrifa", + "skrifa 0.44.0", "yazi", "zeno", ] [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" dependencies = [ "proc-macro2", "quote", @@ -6518,13 +6623,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -6543,7 +6648,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.20", "walkdir", "yaml-rust", ] @@ -6577,7 +6682,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6614,9 +6719,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "tempfile" @@ -6625,10 +6730,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", - "rustix 1.1.4", - "windows-sys 0.52.0", + "rustix 1.1.5", + "windows-sys 0.61.2", ] [[package]] @@ -6645,7 +6750,7 @@ name = "the_matrix" version = "0.1.0" dependencies = [ "iced", - "rand 0.8.5", + "rand 0.8.8", "tracing-subscriber", ] @@ -6660,11 +6765,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.20", ] [[package]] @@ -6675,25 +6780,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -6714,12 +6819,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -6729,15 +6833,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -6755,7 +6859,21 @@ dependencies = [ "cfg-if", "log", "png 0.17.16", - "tiny-skia-path", + "tiny-skia-path 0.11.4", +] + +[[package]] +name = "tiny-skia" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path 0.12.0", ] [[package]] @@ -6770,13 +6888,24 @@ dependencies = [ ] [[package]] -name = "tiny-xlib" -version = "0.2.4" +name = "tiny-skia-path" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" +checksum = "edca365c3faccca67d06593c5980fa6c57687de727a03131735bb85f01fdeeb9" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tiny-xlib" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90a0ca3ee6a69f2ad28fd11621a4c3f03b371f366be500b64df260c4ffbafb4" dependencies = [ "as-raw-xcb-connection", - "ctor-lite", + "ctor", "libloading", "pkg-config", "tracing", @@ -6784,9 +6913,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -6804,18 +6933,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "toast" @@ -6842,29 +6962,29 @@ dependencies = [ [[package]] name = "tokio" -version = "1.51.1" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2 0.6.5", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -6890,19 +7010,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.4" +version = "0.26.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" dependencies = [ - "rustls 0.23.37", + "rustls 0.23.45", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -6923,22 +7043,23 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a" dependencies = [ "indexmap", "serde_core", @@ -6946,7 +7067,7 @@ dependencies = [ "toml_datetime", "toml_parser", "toml_writer", - "winnow 1.0.1", + "winnow", ] [[package]] @@ -6960,30 +7081,30 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.15+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614" dependencies = [ "indexmap", "toml_datetime", "toml_parser", - "winnow 1.0.1", + "winnow", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.1", + "winnow", ] [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tooltip" @@ -7020,20 +7141,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytes", "futures-util", - "http 1.4.0", - "http-body 1.0.1", - "iri-string", + "http 1.5.0", + "http-body 1.1.0", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -7068,7 +7189,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -7130,10 +7251,10 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.4.0", + "http 1.5.0", "httparse", "log", - "rand 0.8.5", + "rand 0.8.8", "rustls 0.22.4", "rustls-pki-types", "sha1", @@ -7155,9 +7276,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uds_windows" @@ -7167,7 +7288,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7196,9 +7317,9 @@ checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" [[package]] name = "unicode-linebreak" @@ -7220,9 +7341,9 @@ checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-vo" @@ -7236,12 +7357,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "untrusted" version = "0.9.0" @@ -7293,7 +7408,7 @@ dependencies = [ "siphasher", "strict-num", "svgtypes", - "tiny-skia-path", + "tiny-skia-path 0.11.4", "unicode-bidi", "unicode-script", "unicode-vo", @@ -7314,13 +7429,13 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.0" +version = "1.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", - "rand 0.10.0", + "rand 0.10.3", "serde_core", "wasm-bindgen", ] @@ -7344,9 +7459,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-bag" -version = "1.12.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" +checksum = "2799ffb329a792ecfd902b71306c8a815a6ef1c0470fa9953a6aa4d4cecbe511" [[package]] name = "vcpkg" @@ -7444,27 +7559,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" dependencies = [ "cfg-if", "once_cell", @@ -7475,9 +7581,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" dependencies = [ "js-sys", "wasm-bindgen", @@ -7485,9 +7591,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7495,48 +7601,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 3.0.6", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.4.2" @@ -7550,18 +7634,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "wasmtimer" version = "0.4.3" @@ -7578,13 +7650,13 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078" dependencies = [ "cc", "downcast-rs", - "rustix 1.1.4", + "rustix 1.1.5", "scoped-tls", "smallvec", "wayland-sys", @@ -7592,12 +7664,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.14" +version = "0.31.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" dependencies = [ - "bitflags 2.11.0", - "rustix 1.1.4", + "bitflags 2.13.2", + "rustix 1.1.5", "wayland-backend", "wayland-scanner", ] @@ -7608,7 +7680,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cursor-icon", "wayland-backend", ] @@ -7619,7 +7691,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" dependencies = [ - "rustix 1.1.4", + "rustix 1.1.5", "wayland-client", "xcursor", ] @@ -7630,7 +7702,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7638,11 +7710,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.12" +version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7655,10 +7727,10 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "wayland-scanner", ] @@ -7668,10 +7740,10 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "wayland-scanner", ] @@ -7681,10 +7753,10 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "wayland-scanner", ] @@ -7694,7 +7766,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", "wayland-protocols 0.31.2", @@ -7707,34 +7779,34 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "wayland-scanner", "wayland-server", ] [[package]] name = "wayland-scanner" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ "proc-macro2", - "quick-xml 0.39.2", + "quick-xml 0.41.0", "quote", ] [[package]] name = "wayland-server" -version = "0.31.13" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1846eb04c49182e04f4a099e2a830a2b745610bbc1d61246e206f29c7000a0" +checksum = "0dde9c29be0f723a573977de51ee455bf3dfa03652730a74f9dd3b337e374d75" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "downcast-rs", - "rustix 1.1.4", + "rustix 1.1.5", "wayland-backend", "wayland-scanner", ] @@ -7753,9 +7825,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8" dependencies = [ "js-sys", "wasm-bindgen", @@ -7773,15 +7845,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.0" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe985f41e291eecef5e5c0770a18d28390addb03331c043964d9e916453d6f16" +checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" dependencies = [ - "core-foundation 0.10.1", - "jni 0.22.4", + "jni", "log", "ndk-context", "objc2 0.6.4", + "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "url", "web-sys", @@ -7793,14 +7865,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.9", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -7823,12 +7895,13 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" +checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" dependencies = [ "arrayvec", - "bitflags 2.11.0", + "bitflags 2.13.2", + "bytemuck", "cfg-if", "cfg_aliases", "document-features", @@ -7852,14 +7925,14 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "27.0.3" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" +checksum = "d23f4642f53f666adcfd2d3218ab174d1e6681101aef18696b90cbe64d1c10f9" dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "cfg_aliases", "document-features", @@ -7874,7 +7947,7 @@ dependencies = [ "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.20", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-wasm", @@ -7885,51 +7958,51 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" +checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" +checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-wasm" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1027dcf3b027a877e44819df7ceb0e2e98578830f8cd34cd6c3c7c2a7a50b7" +checksum = "12a2cf578ce8d7d50d0e63ddc2345c7dcb599f6eb90b888813406ea78b9b7010" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "27.0.4" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" +checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" dependencies = [ "android_system_properties", "arrayvec", "ash", "bit-set", - "bitflags 2.11.0", + "bitflags 2.13.2", "block", "bytemuck", "cfg-if", @@ -7937,7 +8010,6 @@ dependencies = [ "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", - "gpu-alloc", "gpu-allocator", "gpu-descriptor", "hashbrown 0.16.1", @@ -7960,25 +8032,24 @@ dependencies = [ "raw-window-handle", "renderdoc-sys", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.20", "wasm-bindgen", "web-sys", "wgpu-types", - "windows 0.58.0", - "windows-core 0.58.0", + "windows 0.62.2", + "windows-core 0.62.2", ] [[package]] name = "wgpu-types" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" +checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "js-sys", "log", - "thiserror 2.0.18", "web-sys", ] @@ -8004,7 +8075,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8038,16 +8109,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.61.3" @@ -8103,19 +8164,6 @@ dependencies = [ "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 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.61.2" @@ -8172,18 +8220,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn", -] - -[[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", + "syn 2.0.119", ] [[package]] @@ -8194,7 +8231,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -8205,18 +8242,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn", -] - -[[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", + "syn 2.0.119", ] [[package]] @@ -8227,7 +8253,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -8282,15 +8308,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[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-result" version = "0.3.4" @@ -8309,16 +8326,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows-strings" version = "0.4.2" @@ -8337,15 +8344,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -8382,21 +8380,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -8448,12 +8431,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -8466,12 +8443,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -8484,12 +8455,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -8514,12 +8479,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -8532,12 +8491,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -8550,12 +8503,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -8568,12 +8515,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -8589,15 +8530,15 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cfg_aliases", "cursor-icon", "dpi", "libc", "raw-window-handle", - "rustix 1.1.4", + "rustix 1.1.5", "smol_str", "tracing", "winit-android", @@ -8615,10 +8556,10 @@ dependencies = [ [[package]] name = "winit-android" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ "android-activity", - "bitflags 2.11.0", + "bitflags 2.13.2", "dpi", "ndk", "raw-window-handle", @@ -8630,9 +8571,9 @@ dependencies = [ [[package]] name = "winit-appkit" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.6.2", "dispatch2", "dpi", @@ -8652,9 +8593,9 @@ dependencies = [ [[package]] name = "winit-common" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ - "memmap2 0.9.10", + "memmap2 0.9.11", "objc2 0.6.4", "objc2-core-foundation", "smol_str", @@ -8667,9 +8608,9 @@ dependencies = [ [[package]] name = "winit-core" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cursor-icon", "dpi", "keyboard-types", @@ -8681,14 +8622,14 @@ dependencies = [ [[package]] name = "winit-orbital" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "dpi", "libredox", "orbclient", "raw-window-handle", - "redox_syscall 0.7.4", + "redox_event", "smol_str", "tracing", "winit-core", @@ -8697,9 +8638,9 @@ dependencies = [ [[package]] name = "winit-uikit" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2 0.6.2", "dispatch2", "dpi", @@ -8717,24 +8658,24 @@ dependencies = [ [[package]] name = "winit-wayland" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ "ahash", - "bitflags 2.11.0", + "bitflags 2.13.2", "calloop 0.14.4", "cursor-icon", "dpi", "libc", - "memmap2 0.9.10", + "memmap2 0.9.11", "raw-window-handle", - "rustix 1.1.4", + "rustix 1.1.5", "sctk-adwaita", "smithay-client-toolkit 0.20.0", "smol_str", "tracing", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.12", + "wayland-protocols 0.32.13", "wayland-protocols-plasma", "winit-common", "winit-core", @@ -8743,10 +8684,10 @@ dependencies = [ [[package]] name = "winit-web" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ "atomic-waker", - "bitflags 2.11.0", + "bitflags 2.13.2", "concurrent-queue", "cursor-icon", "dpi", @@ -8765,9 +8706,9 @@ dependencies = [ [[package]] name = "winit-win32" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cursor-icon", "dpi", "raw-window-handle", @@ -8781,9 +8722,9 @@ dependencies = [ [[package]] name = "winit-x11" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytemuck", "calloop 0.14.4", "cursor-icon", @@ -8791,7 +8732,7 @@ dependencies = [ "libc", "percent-encoding", "raw-window-handle", - "rustix 1.1.4", + "rustix 1.1.5", "smol_str", "tracing", "winit-common", @@ -8803,115 +8744,24 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.15" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" -version = "0.51.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "x11-dl" @@ -8935,7 +8785,7 @@ dependencies = [ "libc", "libloading", "once_cell", - "rustix 1.1.4", + "rustix 1.1.5", "x11rb-protocol", "xcursor", ] @@ -8948,9 +8798,9 @@ checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "xcursor" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" +checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" [[package]] name = "xkbcommon" @@ -8970,7 +8820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" dependencies = [ "libc", - "memmap2 0.9.10", + "memmap2 0.9.11", "xkeysym", ] @@ -8980,7 +8830,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "dlib", "log", "once_cell", @@ -8998,9 +8848,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.28" +version = "0.8.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" [[package]] name = "xmlwriter" @@ -9037,9 +8887,9 @@ checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -9048,21 +8898,21 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", "synstructure", ] [[package]] name = "zbus" -version = "5.14.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" dependencies = [ "async-broadcast", "async-executor", @@ -9074,20 +8924,20 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener 5.4.1", + "event-listener 5.4.2", "futures-core", "futures-lite", "hex", "libc", "ordered-stream", - "rustix 1.1.4", + "rustix 1.1.5", "serde", "serde_repr", "tracing", "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 0.7.15", + "winnow", "zbus_macros", "zbus_names", "zvariant", @@ -9111,7 +8961,7 @@ checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "zbus-lockstep", "zbus_xml", "zvariant", @@ -9119,14 +8969,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.14.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 3.0.6", "zbus_names", "zvariant", "zvariant_utils", @@ -9134,27 +8984,36 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" dependencies = [ "serde", - "winnow 0.7.15", + "winnow", "zvariant", ] [[package]] name = "zbus_xml" -version = "5.1.0" +version = "5.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" +checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" dependencies = [ - "quick-xml 0.38.4", "serde", + "winnow", "zbus_names", "zvariant", ] +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + [[package]] name = "zeno" version = "0.3.3" @@ -9163,56 +9022,56 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -9221,9 +9080,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -9232,20 +9091,26 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] -name = "zmij" -version = "1.0.21" +name = "zlib-rs" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zune-core" @@ -9255,9 +9120,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-core" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" [[package]] name = "zune-inflate" @@ -9283,46 +9148,47 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ - "zune-core 0.5.1", + "zune-core 0.5.3", ] [[package]] name = "zvariant" -version = "5.10.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147" dependencies = [ "endi", "enumflags2", "serde", "url", - "winnow 0.7.15", + "winnow", + "zcheapstr", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.10.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 3.0.6", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.3.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" dependencies = [ "proc-macro2", "quote", "serde", - "syn", - "winnow 0.7.15", + "syn 3.0.6", + "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 49b4b5d7..a1498ba6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,13 +118,17 @@ winit = [ "iced_accessibility?/accesskit_winit", "iced_program/winit", ] -wayland = ["iced_renderer/wayland", "iced_winit/wayland"] -cctk = ["iced_winit/cctk", "iced_widget/cctk", "iced_core/cctk", "wayland"] -# Enables the sctk shell. +wayland = [ + "iced_renderer/wayland", + "iced_winit/wayland", + "iced_runtime/wayland", + "iced_widget/wayland", + "iced_core/wayland", +] + [dependencies] - - iced_devtools.workspace = true +iced_devtools.optional = true iced_debug.workspace = true iced_program.workspace = true @@ -134,10 +138,9 @@ iced_renderer.workspace = true iced_runtime.workspace = true iced_widget.workspace = true iced_winit.workspace = true -iced_devtools.optional = true +iced_winit.optional = true iced_tester.workspace = true iced_tester.optional = true -iced_winit.optional = true iced_highlighter.workspace = true iced_highlighter.optional = true iced_accessibility.workspace = true @@ -190,6 +193,7 @@ members = [ "winit", "examples/*", "accessibility", + "build_helpers", ] exclude = ["examples/integration"] @@ -202,7 +206,7 @@ repository = "https://github.com/iced-rs/iced" homepage = "https://iced.rs" categories = ["gui"] keywords = ["gui", "ui", "graphics", "interface", "widgets"] -rust-version = "1.89" +rust-version = "1.92" [workspace.dependencies] iced = { version = "0.14.0", path = "." } @@ -238,10 +242,9 @@ async-std = "1.0" bitflags = "2.5" bytemuck = { version = "1.0", features = ["derive"] } bytes = "1.6" -cosmic-text = { git = "https://github.com/pop-os/cosmic-text.git" } -# cosmic-text = "0.10" +cosmic-text = { path = "../../cosmic-text" } +cryoglyph = { package = "cryoglyph", path = "../../glyphon" } dark-light = "1.0" -cryoglyph = { package = "cryoglyph", git = "https://github.com/pop-os/glyphon.git", tag = "cosmic-0.14" } resvg = "0.45" web-sys = "0.3.69" guillotiere = "0.6" @@ -275,8 +278,11 @@ tiny-skia = { version = "0.11", default-features = false, features = [ "std", "simd", ] } -cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "160b086" } -softbuffer = { git = "https://github.com/pop-os/softbuffer", tag = "cosmic-4.0" } +cctk = { path = "../../cosmic-protocols/client-toolkit", package = "cosmic-client-toolkit" } +# Yoda: default-features=false drops softbuffer's x11 / x11-dlopen (pulled +# tiny-xlib + as-raw-xcb-connection). The wayland + wayland-dlopen features +# are re-activated via iced_tiny_skia's own `wayland` feature propagation. +softbuffer = { path = "../../softbuffer", default-features = false } syntect = "5.2" tokio = "1.0" tracing = "0.1" @@ -288,7 +294,7 @@ url = "2.5" wasm-bindgen-futures = "0.4" wasmtimer = "0.4.2" web-time = "1.1" -wgpu = { version = "27.0", default-features = false, features = [ +wgpu = { version = "28.0", default-features = false, features = [ "std", "wgsl", ] } @@ -302,13 +308,40 @@ wayland-client = { version = "0.31.5" } winapi = "0.3" # window_clipboard = "0.4.1" -window_clipboard = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" } -dnd = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" } -mime = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" } -winit = { git = "https://github.com/pop-os/winit.git", tag = "cosmic-0.14" } -winit-core = { git = "https://github.com/pop-os/winit.git", tag = "cosmic-0.14" } +# Yoda: window_clipboard pinned to our public fork that has x11 gated behind +# an opt-in feature. default-features=false + features=["wayland"] drops +# clipboard_x11 + x11rb from the build. Branch yoda-x11-optional on +# https://forge.aditua.com/leyoda/window_clipboard. +window_clipboard = { path = "../../window_clipboard", default-features = false, features = ["wayland"] } +dnd = { path = "../../window_clipboard/dnd" } +mime = { path = "../../window_clipboard/mime" } +# Yoda: default-features = false drops winit's x11 default, which otherwise +# pulls winit-x11, x11-dl, tiny-xlib, as-raw-xcb-connection. Our Wayland-only +# fork doesn't need any of that — the wayland + wayland-dlopen features are +# re-enabled via iced_winit's own feature propagation from libcosmic-yoda. +winit = { path = "../../winit/winit", default-features = false } +winit-core = { path = "../../winit/winit-core" } cursor-icon = "1.1.0" +[patch.'https://github.com/pop-os/softbuffer'] +softbuffer = { path = "../../softbuffer" } + +[patch.'https://github.com/pop-os/smithay-clipboard'] +smithay-clipboard = { path = "../../smithay-clipboard" } + +[patch.'https://github.com/pop-os/winit.git'] +dpi = { path = "../../winit/dpi" } +winit = { path = "../../winit/winit" } +winit-android = { path = "../../winit/winit-android" } +winit-appkit = { path = "../../winit/winit-appkit" } +winit-common = { path = "../../winit/winit-common" } +winit-core = { path = "../../winit/winit-core" } +winit-orbital = { path = "../../winit/winit-orbital" } +winit-uikit = { path = "../../winit/winit-uikit" } +winit-wayland = { path = "../../winit/winit-wayland" } +winit-web = { path = "../../winit/winit-web" } +winit-win32 = { path = "../../winit/winit-win32" } +winit-x11 = { path = "../../winit/winit-x11" } [workspace.lints.rust] rust_2018_idioms = { level = "deny", priority = -1 } @@ -334,3 +367,6 @@ useless_conversion = "deny" [workspace.lints.rustdoc] broken_intra_doc_links = "forbid" + +[build-dependencies] +build_helpers = { path = "./build_helpers" } diff --git a/accessibility/Cargo.toml b/accessibility/Cargo.toml index 40188149..0882a480 100644 --- a/accessibility/Cargo.toml +++ b/accessibility/Cargo.toml @@ -10,10 +10,10 @@ async-io = ["accesskit_winit?/async-io"] tokio = ["accesskit_winit?/tokio"] [dependencies] -accesskit = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14" } -accesskit_windows = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14", optional = true } -accesskit_macos = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14", optional = true } -accesskit_winit = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14", optional = true, default-features = false, features = [ +accesskit = { git = "https://github.com/wash2/accesskit", rev = "6c20249" } +accesskit_windows = { git = "https://github.com/wash2/accesskit", rev = "6c20249", optional = true } +accesskit_macos = { git = "https://github.com/wash2/accesskit", rev = "6c20249", optional = true } +accesskit_winit = { git = "https://github.com/wash2/accesskit", rev = "6c20249", optional = true, default-features = false, features = [ "rwh_06", ] } # accesskit = { path = "../../../accesskit/common" } diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..db00a773 --- /dev/null +++ b/build.rs @@ -0,0 +1,3 @@ +fn main() { + build_helpers::cfg_aliases_setup(); +} diff --git a/build_helpers/Cargo.toml b/build_helpers/Cargo.toml new file mode 100644 index 00000000..ff319f69 --- /dev/null +++ b/build_helpers/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "build_helpers" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +categories.workspace = true +keywords.workspace = true +rust-version.workspace = true + +[dependencies] +cfg_aliases = { version = "0.2.1" } + +[lints] +workspace = true diff --git a/build_helpers/src/lib.rs b/build_helpers/src/lib.rs new file mode 100644 index 00000000..364b6ed8 --- /dev/null +++ b/build_helpers/src/lib.rs @@ -0,0 +1,24 @@ +use cfg_aliases::cfg_aliases; + +pub fn cfg_aliases_setup() { + // The script doesn't depend on our code + println!("cargo:rerun-if-changed=build.rs"); + + // Setup cfg aliases + cfg_aliases! { + // Systems. + android_platform: { target_os = "android" }, + wasm_platform: { all(target_family = "wasm", not(target_os = "emscripten")) }, + macos_platform: { target_os = "macos" }, + ios_platform: { target_os = "ios" }, + windows_platform: { target_os = "windows" }, + apple: { any(target_os = "ios", target_os = "macos") }, + free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) }, + redox: { target_os = "redox" }, + + // Native displays. + wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) }, + xdg_portal: { all(feature = "xdg-portal", free_unix, not(wasm), not(redox)) }, + orbital_platform: { redox }, + } +} diff --git a/core/Cargo.toml b/core/Cargo.toml index e64a5c37..f3136a87 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -19,6 +19,7 @@ crisp = [] basic-shaping = [] advanced-shaping = [] a11y = ["iced_accessibility"] +wayland = ["cctk"] [dependencies] palette = "0.7" @@ -31,6 +32,7 @@ num-traits.workspace = true rustc-hash.workspace = true smol_str.workspace = true thiserror.workspace = true +unicode-segmentation.workspace = true web-time.workspace = true serde.workspace = true @@ -42,8 +44,6 @@ window_clipboard.workspace = true dnd.workspace = true mime.workspace = true -cctk.workspace = true -cctk.optional = true # /TODO(POP) [dependencies.iced_accessibility] version = "0.1.0" @@ -55,3 +55,10 @@ raw-window-handle.workspace = true [dev-dependencies] approx = "0.5" + +[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies] +cctk.workspace = true +cctk.optional = true + +[build-dependencies] +build_helpers = { path = "../build_helpers" } diff --git a/core/build.rs b/core/build.rs new file mode 100644 index 00000000..db00a773 --- /dev/null +++ b/core/build.rs @@ -0,0 +1,3 @@ +fn main() { + build_helpers::cfg_aliases_setup(); +} diff --git a/core/src/element.rs b/core/src/element.rs index f97ed6bc..dbe449c5 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -1,4 +1,4 @@ -use crate::event::{self, Event}; +use crate::event::Event; use crate::id::Id; use crate::layout; use crate::mouse; diff --git a/core/src/event.rs b/core/src/event.rs index 1aaf4ff4..b4a28a29 100644 --- a/core/src/event.rs +++ b/core/src/event.rs @@ -7,7 +7,8 @@ use crate::keyboard; use crate::mouse; use crate::touch; use crate::window; -#[cfg(all(feature = "cctk", target_os = "linux"))] + +#[cfg(wayland_platform)] /// A platform specific event for wayland pub mod wayland; /// A user interface event. @@ -49,7 +50,7 @@ pub enum Event { /// A platform specific event #[derive(Debug, Clone, PartialEq)] pub enum PlatformSpecific { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] /// A Wayland specific event Wayland(wayland::Event), } diff --git a/core/src/event/wayland/mod.rs b/core/src/event/wayland/mod.rs index 03e74407..5050ce66 100644 --- a/core/src/event/wayland/mod.rs +++ b/core/src/event/wayland/mod.rs @@ -46,4 +46,6 @@ pub enum Event { Subsurface(SubsurfaceEvent), /// Keyboard inhibit shortcuts ShortcutsInhibited(bool), + /// Blur Support Enabled + BlurEnabled, } diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index ec0906e7..1a4ce672 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -3,7 +3,7 @@ use crate::mouse; use crate::overlay; use crate::renderer; use crate::widget; -use crate::{Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size}; +use crate::{Clipboard, Event, Layout, Overlay, Shell, Size}; /// An [`Overlay`] container that displays multiple overlay [`overlay::Element`] /// children. diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index e0b87756..ff89af01 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -78,6 +78,8 @@ pub enum Action { SelectLine, /// Select the entire buffer. SelectAll, + /// Clear the selection + ClearSelection, /// Perform an [`Edit`]. Edit(Edit), /// Click the [`Editor`] at the given [`Point`]. diff --git a/core/src/widget/operation.rs b/core/src/widget/operation.rs index 40f01064..ff0c920d 100644 --- a/core/src/widget/operation.rs +++ b/core/src/widget/operation.rs @@ -9,7 +9,7 @@ pub use scrollable::Scrollable; pub use text_input::TextInput; use crate::widget::Id; -use crate::{Rectangle, Vector}; +use crate::{Rectangle, Vector, window}; use std::any::Any; use std::fmt; @@ -75,6 +75,12 @@ pub trait Operation: Send { fn finish(&self) -> Outcome { Outcome::None } + + /// Track the active window id being processed if relevant to the operation + fn set_window_id(&mut self, _id: window::Id) {} + + /// Used to mark the beginning of operations for a widget and its children + fn pre_operation(&mut self, _id: Option<&Id>) {} } impl Operation for Box @@ -140,6 +146,14 @@ where fn finish(&self) -> Outcome { self.as_ref().finish() } + + fn set_window_id(&mut self, id: window::Id) { + self.as_mut().set_window_id(id); + } + + fn pre_operation(&mut self, id: Option<&Id>) { + self.as_mut().pre_operation(id); + } } /// The result of an [`Operation`]. @@ -243,6 +257,14 @@ where fn finish(&self) -> Outcome { Outcome::None } + + fn pre_operation(&mut self, id: Option<&Id>) { + self.operation.pre_operation(id); + } + + fn set_window_id(&mut self, id: window::Id) { + self.operation.set_window_id(id); + } } BlackBox { operation } @@ -341,6 +363,10 @@ where ) { self.operation.custom(id, bounds, state); } + + fn pre_operation(&mut self, id: Option<&Id>) { + self.operation.pre_operation(id); + } } self.operation.traverse(&mut |operation| { @@ -410,6 +436,14 @@ where })), } } + + fn set_window_id(&mut self, id: window::Id) { + self.operation.set_window_id(id); + } + + fn pre_operation(&mut self, id: Option<&Id>) { + self.operation.pre_operation(id); + } } Map { @@ -515,6 +549,14 @@ where } } } + + fn set_window_id(&mut self, id: window::Id) { + self.operation.set_window_id(id); + } + + fn pre_operation(&mut self, id: Option<&Id>) { + self.operation.pre_operation(id); + } } Chain { @@ -566,6 +608,14 @@ pub fn scoped( outcome => outcome, } } + + fn pre_operation(&mut self, id: Option<&Id>) { + self.operation.pre_operation(id); + } + + fn set_window_id(&mut self, id: window::Id) { + self.operation.set_window_id(id); + } } ScopedOperation { diff --git a/core/src/widget/operation/search_id.rs b/core/src/widget/operation/search_id.rs index 1286abaf..e03fac8a 100644 --- a/core/src/widget/operation/search_id.rs +++ b/core/src/widget/operation/search_id.rs @@ -4,7 +4,7 @@ use super::Operation; use crate::{ Rectangle, id::Id, - widget::operation::{Outcome, focusable::Count}, + widget::operation::Outcome, }; /// Produces an [`Operation`] that searches for the Id diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 1e40bf9e..e57df01d 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -22,16 +22,18 @@ //! ``` use crate::alignment; use crate::layout; -use crate::mouse; +use crate::mouse::{self, click}; use crate::renderer; +use crate::text; use crate::text::paragraph::{self, Paragraph}; -use crate::text::{self, Fragment}; use crate::widget::tree::{self, Tree}; use crate::{ - Color, Element, Layout, Length, Pixels, Rectangle, Size, Theme, Widget, + Clipboard, Color, Element, Event, Layout, Length, Pixels, Point, Rectangle, + Shell, Size, Theme, Widget, keyboard, touch, }; -use std::borrow::Cow; +use unicode_segmentation::UnicodeSegmentation; + pub use text::{Alignment, Ellipsize, LineHeight, Shaping, Wrapping}; /// A bunch of text. @@ -65,6 +67,7 @@ where fragment: text::Fragment<'a>, format: Format, class: Theme::Class<'a>, + selectable: bool, } impl<'a, Theme, Renderer> Text<'a, Theme, Renderer> @@ -79,6 +82,7 @@ where fragment: fragment.into_fragment(), format: Format::default(), class: Theme::default(), + selectable: false, } } @@ -189,7 +193,17 @@ where { let color = color.map(Into::into); - self.style(move |_theme| Style { color }) + self.style(move |_theme| Style { + color, + ..Style::default() + }) + } + + /// Makes the [`Text`] selectable. When enabled, the user can click and + /// drag to select text, and copy it with Ctrl+C / Cmd+C. + pub fn selectable(mut self) -> Self { + self.selectable = true; + self } /// Sets the style class of the [`Text`]. @@ -202,7 +216,116 @@ where } /// The internal state of a [`Text`] widget. -pub type State

= paragraph::Plain

; +pub struct State { + /// The cached paragraph layout. + pub paragraph: paragraph::Plain

, + /// Lazily allocated when text is selectable and first interacted with. + selection: Option>, + focused: bool, + keyboard_focused: bool, + context_menu_position: Option, + clipboard_has_text: bool, +} + +impl Default for State

{ + fn default() -> Self { + Self { + paragraph: paragraph::Plain::default(), + selection: None, + focused: false, + keyboard_focused: false, + context_menu_position: None, + clipboard_has_text: false, + } + } +} + +impl std::fmt::Debug for State

{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("State") + .field("selection", &self.selection) + .field("focused", &self.focused) + .finish_non_exhaustive() + } +} + +impl State

{ + /// Returns `true` if the widget currently has focus. + pub fn is_focused(&self) -> bool { + self.focused + } + + /// Returns `true` if focus was gained via keyboard navigation (Tab). + pub fn is_keyboard_focused(&self) -> bool { + self.keyboard_focused + } + + /// Clears focus, selection, and all interaction state. + pub fn clear_focus(&mut self) { + self.focused = false; + self.keyboard_focused = false; + self.context_menu_position = None; + if let Some(sel) = &mut self.selection { + sel.clear(); + } + } + + /// Returns the context menu position, if a context menu should be shown. + pub fn context_menu_position(&self) -> Option { + self.context_menu_position + } + + /// Sets or clears the context menu position. + pub fn set_context_menu_position(&mut self, pos: Option) { + self.context_menu_position = pos; + } +} + +impl std::ops::Deref for State

{ + type Target = paragraph::Plain

; + + fn deref(&self) -> ¶graph::Plain

{ + &self.paragraph + } +} + +impl std::ops::DerefMut for State

{ + fn deref_mut(&mut self) -> &mut paragraph::Plain

{ + &mut self.paragraph + } +} + +#[derive(Debug, Clone, Default)] +struct SelectionState { + anchor: usize, + end: usize, + dragging: bool, + modifiers: keyboard::Modifiers, + last_click: Option, +} + +impl SelectionState { + fn clear(&mut self) { + self.anchor = 0; + self.end = 0; + self.dragging = false; + } +} + +impl crate::widget::operation::Focusable for State

{ + fn is_focused(&self) -> bool { + self.focused + } + + fn focus(&mut self) { + self.focused = true; + self.keyboard_focused = true; + } + + fn unfocus(&mut self) { + self.clear_focus(); + } +} impl Widget for Text<'_, Theme, Renderer> @@ -215,7 +338,7 @@ where } fn state(&self) -> tree::State { - tree::State::new(paragraph::Plain::::default()) + tree::State::new(State::::default()) } fn size(&self) -> Size { @@ -231,8 +354,10 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { + let state = tree.state.downcast_mut::>(); + layout( - tree.state.downcast_mut::>(), + &mut state.paragraph, renderer, limits, &self.fragment, @@ -252,25 +377,372 @@ where ) { let state = tree.state.downcast_ref::>(); let style = theme.style(&self.class); - - draw( - renderer, - defaults, - layout.bounds(), - state.raw(), - style, - viewport, + let bounds = layout.bounds(); + let paragraph = state.paragraph.raw(); + let anchor = bounds.anchor( + paragraph.min_bounds(), + paragraph.align_x(), + paragraph.align_y(), ); + + let rects: Vec = state + .selection + .as_ref() + .filter(|sel| sel.anchor != sel.end) + .map(|sel| { + let content: &str = self.fragment.as_ref(); + let lo_byte = + grapheme_to_byte(content, sel.anchor.min(sel.end)); + let hi_byte = + grapheme_to_byte(content, sel.anchor.max(sel.end)); + + paragraph + .highlight( + 0, + (lo_byte, text::Affinity::After), + (hi_byte, text::Affinity::Before), + ) + .into_iter() + .map(|r| Rectangle { + x: anchor.x + r.x, + y: anchor.y + r.y, + width: r.width, + height: r.height, + }) + .collect() + }) + .unwrap_or_default(); + + let fill_selection = |renderer: &mut Renderer| { + for r in &rects { + renderer.fill_quad( + renderer::Quad { + bounds: *r, + ..renderer::Quad::default() + }, + style.selected_fill, + ); + } + }; + + if style.selected_text_color.is_none() { + fill_selection(renderer); + } + draw(renderer, defaults, bounds, paragraph, style, viewport); + if let Some(color) = style.selected_text_color { + fill_selection(renderer); + for r in &rects { + renderer.with_layer(*r, |renderer| { + renderer + .fill_paragraph(paragraph, anchor, color, *viewport); + }); + } + } + } + + fn update( + &mut self, + tree: &mut Tree, + event: &Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + _renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + _viewport: &Rectangle, + ) { + if !self.selectable { + return; + } + + let state = tree.state.downcast_mut::>(); + let bounds = layout.bounds(); + let content: &str = self.fragment.as_ref(); + let grapheme_count = content.graphemes(true).count(); + let paragraph = state.paragraph.raw(); + + // Any click outside this widget clears selection and focus. + if matches!( + event, + Event::Mouse(mouse::Event::ButtonPressed(_)) + | Event::Touch(touch::Event::FingerPressed { .. }) + ) { + if cursor.position_over(bounds).is_none() { + let was_visible = state.focused + || state.keyboard_focused + || state + .selection + .as_ref() + .is_some_and(|sel| sel.anchor != sel.end); + + state.focused = false; + state.keyboard_focused = false; + if let Some(sel) = &mut state.selection { + sel.clear(); + } + + if was_visible { + shell.request_redraw(); + } + } + } + + match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { + if state.context_menu_position.take().is_some() { + shell.capture_event(); + return; + } + + if let Some(pos) = cursor.position_over(bounds) { + let sel = state.selection.get_or_insert_with(|| { + Box::new(SelectionState::default()) + }); + + let anchor = bounds.anchor( + paragraph.min_bounds(), + paragraph.align_x(), + paragraph.align_y(), + ); + let relative = + Point::new(pos.x - anchor.x, pos.y - anchor.y); + + let grapheme_pos = + hit_to_grapheme(paragraph, relative, content); + + let new_click = click::Click::new( + pos, + mouse::Button::Left, + sel.last_click.take(), + ); + + match new_click.kind() { + click::Kind::Single => { + if sel.modifiers.shift() { + sel.end = grapheme_pos; + } else { + sel.anchor = grapheme_pos; + sel.end = grapheme_pos; + } + sel.dragging = true; + } + click::Kind::Double => { + sel.anchor = + previous_start_of_word(content, grapheme_pos); + sel.end = next_end_of_word(content, grapheme_pos); + sel.dragging = true; + } + click::Kind::Triple => { + sel.anchor = 0; + sel.end = grapheme_count; + sel.dragging = true; + } + } + + sel.last_click = Some(new_click); + state.focused = true; + state.keyboard_focused = false; + shell.capture_event(); + shell.request_redraw(); + } + } + + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => { + if let Some(pos) = cursor.position_over(bounds) { + state.context_menu_position = Some(pos); + state.clipboard_has_text = clipboard_has_text(clipboard); + state.focused = true; + state.keyboard_focused = false; + shell.capture_event(); + shell.request_redraw(); + } + } + + Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerLifted { .. }) => { + if let Some(sel) = &mut state.selection { + sel.dragging = false; + } + } + + Event::Mouse(mouse::Event::CursorMoved { position }) + | Event::Touch(touch::Event::FingerMoved { position, .. }) => { + if let Some(sel) = &mut state.selection { + if sel.dragging { + let anchor = bounds.anchor( + paragraph.min_bounds(), + paragraph.align_x(), + paragraph.align_y(), + ); + let relative = Point::new( + position.x - anchor.x, + position.y - anchor.y, + ); + + let end = hit_to_grapheme(paragraph, relative, content); + + // Only the drag reaching a new grapheme changes what + // is drawn; plain motion inside one does not. + if end != sel.end { + sel.end = end; + shell.request_redraw(); + } + + shell.capture_event(); + } + } + } + + Event::Keyboard(keyboard::Event::KeyPressed { + key, + modifiers, + physical_key, + text: _, + .. + }) => { + if !state.focused { + return; + } + let sel = state + .selection + .get_or_insert_with(|| Box::new(SelectionState::default())); + + if modifiers.command() { + match key.to_latin(*physical_key) { + Some('c') => { + let left = sel.anchor.min(sel.end); + let right = sel.anchor.max(sel.end); + if left != right { + let selected: String = content + .graphemes(true) + .skip(left) + .take(right - left) + .collect(); + clipboard.write( + crate::clipboard::Kind::Standard, + selected, + ); + } + shell.capture_event(); + return; + } + Some('a') => { + sel.anchor = 0; + sel.end = grapheme_count; + shell.capture_event(); + shell.request_redraw(); + return; + } + _ => {} + } + } + + match key { + keyboard::Key::Named(keyboard::key::Named::ArrowLeft) => { + let by_word = is_jump_modifier(*modifiers); + if modifiers.shift() { + sel.end = if by_word { + previous_start_of_word(content, sel.end) + } else { + sel.end.saturating_sub(1) + }; + } else { + let left = sel.anchor.min(sel.end); + let pos = if by_word { + previous_start_of_word(content, left) + } else { + left.saturating_sub(1) + }; + sel.anchor = pos; + sel.end = pos; + } + shell.capture_event(); + shell.request_redraw(); + } + keyboard::Key::Named(keyboard::key::Named::ArrowRight) => { + let by_word = is_jump_modifier(*modifiers); + if modifiers.shift() { + sel.end = if by_word { + next_end_of_word(content, sel.end) + } else { + (sel.end + 1).min(grapheme_count) + }; + } else { + let right = sel.anchor.max(sel.end); + let pos = if by_word { + next_end_of_word(content, right) + } else { + (right + 1).min(grapheme_count) + }; + sel.anchor = pos; + sel.end = pos; + } + shell.capture_event(); + shell.request_redraw(); + } + keyboard::Key::Named(keyboard::key::Named::Home) => { + if modifiers.shift() { + sel.end = 0; + } else { + sel.anchor = 0; + sel.end = 0; + } + shell.capture_event(); + shell.request_redraw(); + } + keyboard::Key::Named(keyboard::key::Named::End) => { + if modifiers.shift() { + sel.end = grapheme_count; + } else { + sel.anchor = grapheme_count; + sel.end = grapheme_count; + } + shell.capture_event(); + shell.request_redraw(); + } + _ => {} + } + } + + Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { + if let Some(sel) = &mut state.selection { + sel.modifiers = *modifiers; + } + } + + _ => {} + } + } + + fn mouse_interaction( + &self, + _tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + _renderer: &Renderer, + ) -> mouse::Interaction { + if self.selectable && cursor.is_over(layout.bounds()) { + mouse::Interaction::Text + } else { + mouse::Interaction::None + } } fn operate( &mut self, - _tree: &mut Tree, + tree: &mut Tree, layout: Layout<'_>, _renderer: &Renderer, operation: &mut dyn super::Operation, ) { operation.text(None, layout.bounds(), &self.fragment); + + if self.selectable { + let state = tree.state.downcast_mut::>(); + operation.focusable(None, layout.bounds(), state); + } } #[cfg(feature = "a11y")] @@ -471,12 +943,28 @@ where } /// The appearance of some text. -#[derive(Debug, Clone, Copy, PartialEq, Default)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { /// The [`Color`] of the text. /// /// The default, `None`, means using the inherited color. pub color: Option, + /// The fill [`Color`] of the selection highlight. + pub selected_fill: Color, + /// The [`Color`] of selected text. + /// + /// The default, `None`, keeps the regular text color. + pub selected_text_color: Option, +} + +impl Default for Style { + fn default() -> Self { + Self { + color: None, + selected_fill: DEFAULT_SELECTION_COLOR, + selected_text_color: None, + } + } } /// The theme catalog of a [`Text`]. @@ -510,13 +998,14 @@ impl Catalog for Theme { /// The default text styling; color is inherited. pub fn default(_theme: &Theme) -> Style { - Style { color: None } + Style::default() } /// Text with the default base color. pub fn base(theme: &Theme) -> Style { Style { color: Some(theme.palette().text), + ..Style::default() } } @@ -524,6 +1013,7 @@ pub fn base(theme: &Theme) -> Style { pub fn primary(theme: &Theme) -> Style { Style { color: Some(theme.palette().primary), + ..Style::default() } } @@ -531,6 +1021,7 @@ pub fn primary(theme: &Theme) -> Style { pub fn secondary(theme: &Theme) -> Style { Style { color: Some(theme.extended_palette().secondary.base.color), + ..Style::default() } } @@ -538,6 +1029,7 @@ pub fn secondary(theme: &Theme) -> Style { pub fn success(theme: &Theme) -> Style { Style { color: Some(theme.palette().success), + ..Style::default() } } @@ -545,6 +1037,7 @@ pub fn success(theme: &Theme) -> Style { pub fn warning(theme: &Theme) -> Style { Style { color: Some(theme.palette().warning), + ..Style::default() } } @@ -552,5 +1045,206 @@ pub fn warning(theme: &Theme) -> Style { pub fn danger(theme: &Theme) -> Style { Style { color: Some(theme.palette().danger), + ..Style::default() + } +} + +use crate::widget::tree::Tree as WidgetTree; + +/// Returns `true` if the clipboard currently holds non-empty text. +pub fn clipboard_has_text(clipboard: &dyn Clipboard) -> bool { + clipboard + .read(crate::clipboard::Kind::Standard) + .is_some_and(|s| !s.is_empty()) +} + +/// Implement this on a **widget** to enable context menu support for +/// text selection (Copy, Select All, and optionally Cut / Paste) in libcosmic +pub trait HasSelectableText { + /// Returns the currently selected text, if any. + fn selected_text(&self, tree: &WidgetTree) -> Option; + + /// Selects all text. + fn select_all(&self, tree: &mut WidgetTree); + + /// Returns `true` if the widget is editable (shows Cut / Paste). + fn is_editable(&self) -> bool { + false + } + + /// Returns `true` if the widget has any text (enables Select All). + fn has_text(&self, _tree: &WidgetTree) -> bool { + true + } + + /// Returns whether the clipboard held text when the context menu was + /// requested (enables Paste). Widgets cache this on right-click because + /// overlays have no clipboard access. + fn clipboard_has_text(&self, _tree: &WidgetTree) -> bool { + true + } + + /// Returns `true` if the widget is currently focused. + fn is_focused(&self, tree: &WidgetTree) -> bool; + + /// Returns the position where the context menu should appear + fn context_menu_position(&self, tree: &WidgetTree) -> Option; + + /// Sets or clears the context menu position. + fn set_context_menu_position( + &self, + tree: &mut WidgetTree, + pos: Option, + ); + + /// Copies the selection to the clipboard. + fn copy_to_clipboard( + &self, + tree: &WidgetTree, + clipboard: &mut dyn Clipboard, + ) { + if let Some(text) = self.selected_text(tree) { + clipboard.write(crate::clipboard::Kind::Standard, text); + } + } + + /// Deletes the selected text and returns the new full content. + /// Only called when [`is_editable`](Self::is_editable) is `true`. + fn delete_selection(&self, _tree: &mut WidgetTree) -> Option { + None + } + + /// Inserts `text` at the cursor (replacing any selection) and returns + /// the new full content. + /// Only called when [`is_editable`](Self::is_editable) is `true`. + fn paste_text( + &self, + _tree: &mut WidgetTree, + _text: &str, + ) -> Option { + None + } +} + +impl HasSelectableText + for Text<'_, Theme, Renderer> +{ + fn selected_text(&self, tree: &WidgetTree) -> Option { + let state = tree.state.downcast_ref::>(); + let sel = state.selection.as_ref()?; + let left = sel.anchor.min(sel.end); + let right = sel.anchor.max(sel.end); + if left == right { + return None; + } + let content = state.paragraph.content(); + let lo = grapheme_to_byte(content, left); + let hi = grapheme_to_byte(content, right); + content.get(lo..hi).map(|s| s.to_owned()) + } + + fn select_all(&self, tree: &mut WidgetTree) { + let state = tree.state.downcast_mut::>(); + let count = state.paragraph.content().graphemes(true).count(); + let sel = state + .selection + .get_or_insert_with(|| Box::new(SelectionState::default())); + sel.anchor = 0; + sel.end = count; + } + + fn has_text(&self, tree: &WidgetTree) -> bool { + let state = tree.state.downcast_ref::>(); + !state.paragraph.content().is_empty() + } + + fn clipboard_has_text(&self, tree: &WidgetTree) -> bool { + let state = tree.state.downcast_ref::>(); + state.clipboard_has_text + } + + fn is_focused(&self, tree: &WidgetTree) -> bool { + let state = tree.state.downcast_ref::>(); + state.is_focused() + } + + fn context_menu_position(&self, tree: &WidgetTree) -> Option { + let state = tree.state.downcast_ref::>(); + state.context_menu_position() + } + + fn set_context_menu_position( + &self, + tree: &mut WidgetTree, + pos: Option, + ) { + let state = tree.state.downcast_mut::>(); + state.set_context_menu_position(pos); + } +} + +const DEFAULT_SELECTION_COLOR: Color = Color { + r: 0.0, + g: 0.47, + b: 0.84, + a: 0.3, +}; + +fn grapheme_to_byte(content: &str, grapheme_index: usize) -> usize { + content + .graphemes(true) + .take(grapheme_index) + .map(|g| g.len()) + .sum() +} + +fn hit_to_grapheme( + paragraph: &P, + point: Point, + content: &str, +) -> usize { + match paragraph.hit_test(point) { + Some(hit) => { + let byte_offset = hit.cursor().min(content.len()); + content[..byte_offset].graphemes(true).count() + } + None => content.graphemes(true).count(), + } +} + +fn previous_start_of_word(content: &str, grapheme_index: usize) -> usize { + let graphemes: Vec<&str> = content.graphemes(true).collect(); + let clamped = grapheme_index.min(graphemes.len()); + let before: String = graphemes[..clamped].concat(); + + UnicodeSegmentation::split_word_bound_indices(&*before) + .filter(|(_, word)| !word.trim_start().is_empty()) + .next_back() + .map_or(0, |(i, prev_word)| { + clamped + - prev_word.graphemes(true).count() + - before[i + prev_word.len()..].graphemes(true).count() + }) +} + +fn next_end_of_word(content: &str, grapheme_index: usize) -> usize { + let graphemes: Vec<&str> = content.graphemes(true).collect(); + let clamped = grapheme_index.min(graphemes.len()); + let after: String = graphemes[clamped..].concat(); + + UnicodeSegmentation::split_word_bound_indices(&*after) + .find(|(_, word)| !word.trim_start().is_empty()) + .map_or(graphemes.len(), |(i, next_word)| { + clamped + + next_word.graphemes(true).count() + + after[..i].graphemes(true).count() + }) +} + +fn is_jump_modifier(modifiers: keyboard::Modifiers) -> bool { + if cfg!(target_os = "macos") { + modifiers.alt() + } else { + modifiers.control() } } diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs index 5027afd7..cfae782a 100644 --- a/examples/counter/src/main.rs +++ b/examples/counter/src/main.rs @@ -1,5 +1,5 @@ +use iced::widget::{button, column, text, Column}; use iced::Center; -use iced::widget::{Column, button, column, text}; pub fn main() -> iced::Result { iced::run(Counter::update, Counter::view) @@ -42,7 +42,7 @@ impl Counter { #[cfg(test)] mod tests { use super::*; - use iced_test::{Error, simulator}; + use iced_test::{simulator, Error}; #[test] fn it_counts() -> Result<(), Error> { diff --git a/examples/custom_shader/src/scene/pipeline.rs b/examples/custom_shader/src/scene/pipeline.rs index 80b0d533..30262a5d 100644 --- a/examples/custom_shader/src/scene/pipeline.rs +++ b/examples/custom_shader/src/scene/pipeline.rs @@ -140,7 +140,7 @@ impl Pipeline { address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::MipmapFilterMode::Linear, ..Default::default() }); @@ -223,7 +223,7 @@ impl Pipeline { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("cubes pipeline layout"), bind_group_layouts: &[&uniform_bind_group_layout], - push_constant_ranges: &[], + immediate_size: 0, }); let shader = @@ -280,7 +280,7 @@ impl Pipeline { compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -388,6 +388,7 @@ impl Pipeline { ), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport( @@ -478,7 +479,7 @@ impl DepthPipeline { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("cubes.depth_pipeline.layout"), bind_group_layouts: &[&bind_group_layout], - push_constant_ranges: &[], + immediate_size: 0, }); let shader = @@ -520,7 +521,7 @@ impl DepthPipeline { compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -586,6 +587,7 @@ impl DepthPipeline { ), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport( diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs index 4e4757fa..1b248036 100644 --- a/examples/integration/src/scene.rs +++ b/examples/integration/src/scene.rs @@ -43,6 +43,7 @@ impl Scene { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }) } @@ -64,8 +65,8 @@ fn build_pipeline( let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, - push_constant_ranges: &[], bind_group_layouts: &[], + immediate_size: 0, }); device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -101,7 +102,7 @@ fn build_pipeline( mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, + multiview_mask: None, cache: None, }) } diff --git a/examples/sctk_drag/src/dnd_destination.rs b/examples/sctk_drag/src/dnd_destination.rs index 1081d8bb..d1b3374b 100644 --- a/examples/sctk_drag/src/dnd_destination.rs +++ b/examples/sctk_drag/src/dnd_destination.rs @@ -3,20 +3,20 @@ use std::{ sync::atomic::{AtomicU64, Ordering}, }; -use iced::{Element, id::Id}; use iced::{ - Event, Length, Rectangle, clipboard::{ dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent}, mime::AllowedMimeTypes, }, event, id::Internal, - mouse, overlay, + mouse, overlay, Event, Length, Rectangle, }; +use iced::{id::Id, Element}; use iced_core::{ - self, Clipboard, Layout, Shell, Widget, layout, - widget::{Tree, tree}, + self, layout, + widget::{tree, Tree}, + Clipboard, Layout, Shell, Widget, }; pub fn dnd_destination<'a, Message: 'static>( diff --git a/examples/sctk_drag/src/dnd_source.rs b/examples/sctk_drag/src/dnd_source.rs index 1d8b85c4..e93b925e 100644 --- a/examples/sctk_drag/src/dnd_source.rs +++ b/examples/sctk_drag/src/dnd_source.rs @@ -1,16 +1,16 @@ use std::any::Any; -use iced::Element; use iced::id::Id; use iced::widget::container; +use iced::Element; use iced::{ - Event, Length, Point, Rectangle, clipboard::dnd::{DndAction, DndEvent, SourceEvent}, - event, mouse, overlay, + event, mouse, overlay, Event, Length, Point, Rectangle, }; use iced_core::{ - Clipboard, Shell, layout, renderer, - widget::{Tree, tree}, + layout, renderer, + widget::{tree, Tree}, + Clipboard, Shell, }; use iced_core::{Layout, Widget}; @@ -37,11 +37,11 @@ pub struct DndSource<'a, Message, AppMessage, D> { } impl< - 'a, - Message: 'static, - AppMessage: 'static, - D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static, -> DndSource<'a, Message, AppMessage, D> + 'a, + Message: 'static, + AppMessage: 'static, + D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static, + > DndSource<'a, Message, AppMessage, D> { pub fn new(child: impl Into>) -> Self { Self { @@ -119,11 +119,11 @@ impl< } impl< - 'a, - Message: 'static, - AppMessage: 'static, - D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static, -> Widget + 'a, + Message: 'static, + AppMessage: 'static, + D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static, + > Widget for DndSource<'a, Message, AppMessage, D> { fn children(&self) -> Vec { @@ -357,11 +357,11 @@ impl< } impl< - 'a, - Message: 'static, - AppMessage: 'static, - D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static, -> From> for Element<'a, Message> + 'a, + Message: 'static, + AppMessage: 'static, + D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static, + > From> for Element<'a, Message> { fn from(e: DndSource<'a, Message, AppMessage, D>) -> Element<'a, Message> { Element::new(e) diff --git a/examples/sctk_subsurface/src/main.rs b/examples/sctk_subsurface/src/main.rs index c00f7225..a204572c 100644 --- a/examples/sctk_subsurface/src/main.rs +++ b/examples/sctk_subsurface/src/main.rs @@ -7,7 +7,6 @@ use cctk::sctk::reexports::{ use iced::platform_specific::shell::commands::subsurface::get_subsurface; use iced::{ - Element, Length, Subscription, Task, event::wayland::Event as WaylandEvent, platform_specific::{ runtime::wayland::subsurface::SctkSubsurfaceSettings, @@ -15,6 +14,7 @@ use iced::{ }, widget::{button, column, text, text_input}, window::{self, Id, Settings}, + Element, Length, Subscription, Task, }; use std::sync::{Arc, Mutex}; diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 2e3722b1..9fe39035 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -477,6 +477,7 @@ fn delete_icon() -> Text<'static> { fn subtle(theme: &Theme) -> text::Style { text::Style { color: Some(theme.extended_palette().background.strongest.color), + ..text::Style::default() } } diff --git a/graphics/src/image.rs b/graphics/src/image.rs index e5fe9281..8ef868cd 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -1,9 +1,9 @@ //! Load and operate on images. #[cfg(feature = "image")] use crate::core::Bytes; +#[cfg(feature = "image")] +use crate::core::Size; -use crate::core::Color; -use crate::core::Radians; use crate::core::Rectangle; use crate::core::image; use crate::core::svg; @@ -180,3 +180,105 @@ fn to_error(error: ::image::ImageError) -> image::Error { error => image::Error::Invalid(Arc::new(error)), } } + +/// resample a raster image to this target +#[cfg(feature = "image")] +pub fn downsample_target( + native: Size, + bounds: Size, +) -> Option> { + if !(bounds.width >= 1.0 && bounds.height >= 1.0) { + return None; + } + + // Round up to 4px so an animated resize does not make a copy per pixel + // TODO: maybe find a better approach + let quantize = |length: f32| (length.ceil() as u32).div_ceil(4) * 4; + + let target = Size::new( + quantize(bounds.width).min(native.width), + quantize(bounds.height).min(native.height), + ); + + let minified = native.width as f32 >= target.width as f32 * 1.25 + || native.height as f32 >= target.height as f32 * 1.25; + + // If the target is big then Lanczos will be too expensive, and sampler is good enough + let small = u64::from(target.width) * u64::from(target.height) <= 1 << 18; + + (minified && small).then_some(target) +} + +/// Resamples premultiplied pixels down to `target`. +#[cfg(feature = "image")] +pub fn downsample_premultiplied( + pixels: &[u8], + size: Size, + target: Size, +) -> Vec { + let image = + ::image::RgbaImage::from_raw(size.width, size.height, pixels.to_vec()) + .expect("pixels hold width * height RGBA pixels"); + + resize(&image, target).into_raw() +} + +#[cfg(feature = "image")] +fn resize(image: &::image::RgbaImage, target: Size) -> ::image::RgbaImage { + use ::image::imageops::{self, FilterType}; + use std::borrow::Cow; + + // if image is too big compared to target, box average it to double the target + // and then do Lanczos. The final result is almost the same, but lanczos is too + // expensive on large images. + let image = if image.width() >= target.width * 4 + && image.height() >= target.height * 4 + { + Cow::Owned(imageops::thumbnail( + image, + target.width * 2, + target.height * 2, + )) + } else { + Cow::Borrowed(image) + }; + + imageops::resize(&*image, target.width, target.height, FilterType::Lanczos3) +} + +/// Resamples RGBA pixels down to `target`. +/// +/// Premultiplies the image, to avoid fringing the edges of an icon when interpolating +/// transparent pixels. +#[cfg(feature = "image")] +pub fn downsample(image: &Buffer, target: Size) -> ::image::RgbaImage { + let mut image = ::image::RgbaImage::from_raw( + image.width(), + image.height(), + image.as_raw().to_vec(), + ) + .expect("buffer holds width * height RGBA pixels"); + + for pixel in image.pixels_mut() { + let alpha = u32::from(pixel[3]); + + for channel in &mut pixel.0[..3] { + *channel = ((u32::from(*channel) * alpha + 127) / 255) as u8; + } + } + + let mut image = resize(&image, target); + + for pixel in image.pixels_mut() { + let alpha = u32::from(pixel[3]); + + if alpha > 0 { + for channel in &mut pixel.0[..3] { + *channel = ((u32::from(*channel) * 255 + alpha / 2) / alpha) + .min(255) as u8; + } + } + } + + image +} diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index 31cfe68a..210b8309 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -83,13 +83,20 @@ impl Stack { /// Pushes a new clipping region in the [`Stack`]; creating a new layer in the /// process. pub fn push_clip(&mut self, bounds: Rectangle) { + let bounds = bounds * self.transformation(); + let bounds = bounds + .intersection(&self.layers[self.current].bounds()) + .unwrap_or(Rectangle { + width: 0.0, + height: 0.0, + ..bounds + }); + self.previous.push(self.current); self.current = self.active_count; self.active_count += 1; - let bounds = bounds * self.transformation(); - if self.current == self.layers.len() { self.layers.push(T::with_bounds(bounds)); } else { diff --git a/graphics/src/text.rs b/graphics/src/text.rs index 230a362d..910cbbde 100644 --- a/graphics/src/text.rs +++ b/graphics/src/text.rs @@ -161,6 +161,14 @@ impl FontSystem { &mut self.raw } + /// Returns a reference to the underlying [`fontdb::Database`]. + /// + /// Unlike going through [`raw`](Self::raw) and `db_mut`, this does not + /// clear `cosmic-text`'s internal font-match cache. + pub fn db(&self) -> &cosmic_text::fontdb::Database { + self.raw.db() + } + /// Loads a font from its bytes. pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) { if let Cow::Borrowed(bytes) = bytes { diff --git a/graphics/src/text/editor.rs b/graphics/src/text/editor.rs index b284c76b..1586479e 100644 --- a/graphics/src/text/editor.rs +++ b/graphics/src/text/editor.rs @@ -336,6 +336,10 @@ impl editor::Editor for Editor { } } + Action::ClearSelection => { + editor.set_selection(cosmic_text::Selection::None); + } + // Editing events Action::Edit(edit) => { let topmost_line_before_edit = editor diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index ea4741d1..1cb23e23 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -17,7 +17,7 @@ workspace = true debug = [] selector = ["dep:iced_selector"] a11y = ["iced_accessibility", "iced_core/a11y"] -cctk = ["iced_core/cctk", "dep:cctk"] +wayland = ["iced_core/wayland", "dep:cctk"] [dependencies] bytes.workspace = true @@ -33,9 +33,14 @@ sipper.optional = true iced_selector.workspace = true iced_selector.optional = true iced_futures.features = ["thread-pool"] -cctk.workspace = true -cctk.optional = true iced_accessibility.workspace = true iced_accessibility.optional = true window_clipboard.workspace = true dnd.workspace = true + +[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies] +cctk.workspace = true +cctk.optional = true + +[build-dependencies] +build_helpers = { path = "../build_helpers" } diff --git a/runtime/build.rs b/runtime/build.rs new file mode 100644 index 00000000..db00a773 --- /dev/null +++ b/runtime/build.rs @@ -0,0 +1,3 @@ +fn main() { + build_helpers::cfg_aliases_setup(); +} diff --git a/runtime/src/dnd.rs b/runtime/src/dnd.rs index e6f2b267..2e526eb6 100644 --- a/runtime/src/dnd.rs +++ b/runtime/src/dnd.rs @@ -1,12 +1,10 @@ //! Access the clipboard. -use std::any::Any; use dnd::{DndDestinationRectangle, DndSurface}; -use iced_core::clipboard::DndSource; -use window_clipboard::mime::{AllowedMimeTypes, AsMimeTypes}; +use window_clipboard::mime::AllowedMimeTypes; -use crate::{oneshot, task, Action, Task}; +use crate::{Action, Task, oneshot, task}; /// An action to be performed on the system. pub enum DndAction { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 22da3d66..68034bfc 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -28,7 +28,6 @@ pub use task::Task; pub use user_interface::UserInterface; pub use window::Window; -use crate::core::Color; use crate::futures::futures::channel::oneshot; use std::borrow::Cow; diff --git a/runtime/src/platform_specific/mod.rs b/runtime/src/platform_specific/mod.rs index 4ef5ad9f..9cc66ddd 100644 --- a/runtime/src/platform_specific/mod.rs +++ b/runtime/src/platform_specific/mod.rs @@ -2,23 +2,23 @@ use std::fmt; -#[cfg(all(feature = "cctk", target_os = "linux"))] +#[cfg(wayland_platform)] /// Platform specific actions defined for wayland pub mod wayland; /// Platform specific actions defined for wayland pub enum Action { /// Wayland Specific Actions - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] Wayland(wayland::Action), } impl fmt::Debug for Action { fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] Action::Wayland(action) => action.fmt(_f), - #[cfg(not(all(feature = "cctk", target_os = "linux")))] + #[cfg(not(wayland_platform))] _ => Ok(()), } } diff --git a/runtime/src/platform_specific/wayland/activation.rs b/runtime/src/platform_specific/wayland/activation.rs index 64d4dc4d..3f48f01c 100644 --- a/runtime/src/platform_specific/wayland/activation.rs +++ b/runtime/src/platform_specific/wayland/activation.rs @@ -36,7 +36,7 @@ impl fmt::Debug for Action { f, "Action::ActivationAction::Activate {{ window: {:?}, token: {:?} }}", window, token, - ) + ), } } } diff --git a/runtime/src/platform_specific/wayland/layer_surface.rs b/runtime/src/platform_specific/wayland/layer_surface.rs index 4745ef67..60e44caf 100644 --- a/runtime/src/platform_specific/wayland/layer_surface.rs +++ b/runtime/src/platform_specific/wayland/layer_surface.rs @@ -6,8 +6,8 @@ use cctk::sctk::{ }; use iced_core::layout::Limits; -use iced_core::window::Id; use iced_core::Rectangle; +use iced_core::window::Id; /// output for layer surface #[derive(Debug, Clone)] @@ -150,6 +150,18 @@ pub enum Action { /// layer of the layer surface layer: Layer, }, + /// padding of the surface geometry + Padding { + /// id of the layer surface + id: Id, + /// padding of the surface geometry + padding: IcedMargin, + }, + ShowOnLock { + /// id of the layer surface + id: Id, + value: bool, + } } impl fmt::Debug for Action { @@ -162,35 +174,56 @@ impl fmt::Debug for Action { ), Action::Size { id, width, height } => write!( f, - "Action::LayerSurfaceAction::Size {{ id: {:#?}, width: {:?}, height: {:?} }}", id, width, height + "Action::LayerSurfaceAction::Size {{ id: {:#?}, width: {:?}, height: {:?} }}", + id, width, height ), Action::Destroy(id) => write!( f, - "Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}", id + "Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}", + id ), Action::Anchor { id, anchor } => write!( f, - "Action::LayerSurfaceAction::Anchor {{ id: {:#?}, anchor: {:?} }}", id, anchor + "Action::LayerSurfaceAction::Anchor {{ id: {:#?}, anchor: {:?} }}", + id, anchor ), Action::ExclusiveZone { id, exclusive_zone } => write!( f, - "Action::LayerSurfaceAction::ExclusiveZone {{ id: {:#?}, exclusive_zone: {exclusive_zone} }}", id + "Action::LayerSurfaceAction::ExclusiveZone {{ id: {:#?}, exclusive_zone: {exclusive_zone} }}", + id ), Action::Margin { id, margin } => write!( f, - "Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}", id, margin + "Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}", + id, margin ), - Action::KeyboardInteractivity { id, keyboard_interactivity } => write!( + Action::KeyboardInteractivity { + id, + keyboard_interactivity, + } => write!( f, - "Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}", id, keyboard_interactivity + "Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}", + id, keyboard_interactivity ), Action::InputZone { id, zone } => write!( f, - "Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}", id, zone + "Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}", + id, zone ), Action::Layer { id, layer } => write!( f, - "Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}", id, layer + "Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}", + id, layer + ), + Action::Padding { id, padding } => write!( + f, + "Action::LayerSurfaceAction::Padding {{ id: {:#?}, padding: {:?} }}", + id, padding + ), + Action::ShowOnLock { id, value } => write!( + f, + "Action::LayerSurfaceAction::ShowOnLock {{ id: {:#?}, value: {:?} }}", + id, value ), } } diff --git a/runtime/src/platform_specific/wayland/popup.rs b/runtime/src/platform_specific/wayland/popup.rs index e0475eb8..27279ab6 100644 --- a/runtime/src/platform_specific/wayland/popup.rs +++ b/runtime/src/platform_specific/wayland/popup.rs @@ -124,26 +124,34 @@ pub enum Action { /// height height: u32, }, + /// set the size of the popup + Reposition { + /// id of the popup + id: Id, + /// positioner of the popup + positioner: SctkPositioner, + }, } impl fmt::Debug for Action { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Action::Popup { popup, .. } => write!( - f, - "Action::PopupAction::Popup {{ popup: {:?} }}", - popup - ), - Action::Destroy { id } => write!( - f, - "Action::PopupAction::Destroy {{ id: {:?} }}", - id - ), + Action::Popup { popup, .. } => { + write!(f, "Action::PopupAction::Popup {{ popup: {:?} }}", popup) + } + Action::Destroy { id } => { + write!(f, "Action::PopupAction::Destroy {{ id: {:?} }}", id) + } Action::Size { id, width, height } => write!( f, "Action::PopupAction::Size {{ id: {:?}, width: {:?}, height: {:?} }}", id, width, height ), + Action::Reposition { id, positioner } => write!( + f, + "Action::Reposition {{ id: {:?}, positioner: {:?} }}", + id, positioner + ), } } } diff --git a/runtime/src/widget/selector.rs b/runtime/src/widget/selector.rs index 77d4eb2d..82d7e838 100644 --- a/runtime/src/widget/selector.rs +++ b/runtime/src/widget/selector.rs @@ -1,6 +1,6 @@ //! Find and query widgets in your applications. pub use iced_selector::{ - Bounded, Candidate, Selector, Target, Text, id, is_focused, + Bounded, Candidate, Selector, Target, Text, focus, id, is_focused, }; use crate::Task; diff --git a/runtime/src/window.rs b/runtime/src/window.rs index 4d2f5650..ae6445ec 100644 --- a/runtime/src/window.rs +++ b/runtime/src/window.rs @@ -222,7 +222,7 @@ pub fn frames() -> Subscription<(Id, Instant)> { _ => None, }) } -#[cfg(all(feature = "cctk", target_os = "linux"))] +#[cfg(wayland_platform)] /// Subscribes to the frames of the window of the running application. /// /// The resulting [`Subscription`] will produce items at a rate equal to the diff --git a/selector/src/find.rs b/selector/src/find.rs index cfdba7b3..e4b01e66 100644 --- a/selector/src/find.rs +++ b/selector/src/find.rs @@ -1,3 +1,5 @@ +use iced_core::window; + use crate::Selector; use crate::core::widget::operation::{ Focusable, Outcome, Scrollable, TextInput, @@ -100,6 +102,10 @@ where fn finish(&self) -> Self::Output { self.outputs.clone() } + + fn set_window_id(&mut self, id: window::Id) { + self.selector.set_window_id(id); + } } pub trait Strategy { @@ -110,6 +116,8 @@ pub trait Strategy { fn is_done(&self) -> bool; fn finish(&self) -> Self::Output; + + fn set_window_id(&mut self, _id: window::Id) {} } #[derive(Debug)] @@ -273,4 +281,12 @@ where fn finish(&self) -> Outcome { Outcome::Some(self.strategy.finish()) } + + fn set_window_id(&mut self, id: window::Id) { + self.strategy.set_window_id(id); + } + + fn pre_operation(&mut self, id: Option<&Id>) { + self.strategy.feed(Candidate::PreOperation { id }); + } } diff --git a/selector/src/lib.rs b/selector/src/lib.rs index 5c377276..e07d9262 100644 --- a/selector/src/lib.rs +++ b/selector/src/lib.rs @@ -1,5 +1,6 @@ //! Select data from the widget tree. use iced_core as core; +use iced_core::window; mod find; mod target; @@ -48,6 +49,8 @@ pub trait Selector { { FindAll::new(find::All::new(self)) } + + fn set_window_id(&mut self, _id: window::Id) {} } impl Selector for &str { @@ -171,3 +174,42 @@ pub fn is_focused() -> impl Selector { IsFocused } + +/// Returns a [`Selector`] that matches widgets that are focusable. +pub fn focus() -> impl Selector { + struct Focus { + cur_window_id: window::Id, + } + + impl Selector for Focus { + type Output = (bool, Target, window::Id); + + fn select(&mut self, candidate: Candidate<'_>) -> Option { + if let Candidate::Focusable { state, .. } = candidate { + Some(( + state.is_focused(), + Target::from(candidate), + self.cur_window_id, + )) + } else if matches!(candidate, Candidate::Scrollable { .. }) { + Some((false, Target::from(candidate), self.cur_window_id)) + } else if matches!(candidate, Candidate::PreOperation { .. }) { + Some((false, Target::from(candidate), self.cur_window_id)) + } else { + None + } + } + + fn description(&self) -> String { + "is focusable and focused".to_owned() + } + + fn set_window_id(&mut self, id: iced_core::window::Id) { + self.cur_window_id = id; + } + } + + Focus { + cur_window_id: window::Id::NONE, + } +} diff --git a/selector/src/target.rs b/selector/src/target.rs index d2d20651..5aa29920 100644 --- a/selector/src/target.rs +++ b/selector/src/target.rs @@ -42,6 +42,9 @@ pub enum Target { bounds: Rectangle, visible_bounds: Option, }, + PreOperation { + id: Option, + }, } impl Target { @@ -54,6 +57,7 @@ impl Target { | Target::TextInput { bounds, .. } | Target::Text { bounds, .. } | Target::Custom { bounds, .. } => *bounds, + Target::PreOperation { .. } => Rectangle::default(), } } @@ -66,6 +70,7 @@ impl Target { | Target::TextInput { visible_bounds, .. } | Target::Text { visible_bounds, .. } | Target::Custom { visible_bounds, .. } => *visible_bounds, + Target::PreOperation { .. } => None, } } } @@ -138,6 +143,9 @@ impl From> for Target { bounds, visible_bounds, }, + Candidate::PreOperation { id } => { + Self::PreOperation { id: id.cloned() } + } } } } @@ -195,6 +203,8 @@ pub enum Candidate<'a> { visible_bounds: Option, state: &'a dyn Any, }, + /// Used to mark the beginning of operations for a widget and its children + PreOperation { id: Option<&'a Id> }, } impl<'a> Candidate<'a> { @@ -206,7 +216,8 @@ impl<'a> Candidate<'a> { | Candidate::Scrollable { id, .. } | Candidate::TextInput { id, .. } | Candidate::Text { id, .. } - | Candidate::Custom { id, .. } => *id, + | Candidate::Custom { id, .. } + | Candidate::PreOperation { id } => *id, } } @@ -219,6 +230,7 @@ impl<'a> Candidate<'a> { | Candidate::TextInput { bounds, .. } | Candidate::Text { bounds, .. } | Candidate::Custom { bounds, .. } => *bounds, + Candidate::PreOperation { .. } => Rectangle::default(), } } @@ -231,6 +243,7 @@ impl<'a> Candidate<'a> { | Candidate::TextInput { visible_bounds, .. } | Candidate::Text { visible_bounds, .. } | Candidate::Custom { visible_bounds, .. } => *visible_bounds, + Candidate::PreOperation { .. } => None, } } } diff --git a/src/lib.rs b/src/lib.rs index 8d408969..28870ee2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -606,7 +606,7 @@ pub mod font { pub mod event { //! Handle events of a user interface. pub use crate::core::event::PlatformSpecific; - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] pub use crate::core::event::wayland; pub use crate::core::event::{Event, Status}; pub use iced_futures::event::{listen, listen_raw, listen_with}; @@ -683,6 +683,8 @@ pub use event::Event; pub use executor::Executor; pub use font::Font; pub use renderer::Renderer; +#[cfg(feature = "canvas")] +pub use renderer::graphics::geometry; pub use task::Task; pub use window::Window; diff --git a/test/src/emulator.rs b/test/src/emulator.rs index bdb2415c..20c52f59 100644 --- a/test/src/emulator.rs +++ b/test/src/emulator.rs @@ -273,6 +273,11 @@ impl Emulator

{ runtime::Action::Reload => { // TODO } + runtime::Action::Dnd(_) + | runtime::Action::PlatformSpecific(_) => { + // Drag and drop and platform surfaces need a + // windowing system, which an emulator has none of. + } }, } } @@ -474,7 +479,9 @@ impl Emulator

{ &mut self.renderer, theme, &renderer::Style { + icon_color: style.text_color, text_color: style.text_color, + scale_factor: f64::from(scale_factor), }, mouse::Cursor::Unavailable, ); diff --git a/test/src/simulator.rs b/test/src/simulator.rs index 63fbe3f7..20972088 100644 --- a/test/src/simulator.rs +++ b/test/src/simulator.rs @@ -209,17 +209,19 @@ where &mut self.messages, ); + let scale_factor = 2.0_f32; + self.raw.draw( &mut self.renderer, theme, &core::renderer::Style { + icon_color: base.text_color, text_color: base.text_color, + scale_factor: f64::from(scale_factor), }, self.cursor, ); - let scale_factor = 2.0; - let physical_size = Size::new( (self.size.width * scale_factor).round() as u32, (self.size.height * scale_factor).round() as u32, diff --git a/tester/src/lib.rs b/tester/src/lib.rs index 334f17bc..f41fbb68 100644 --- a/tester/src/lib.rs +++ b/tester/src/lib.rs @@ -775,6 +775,7 @@ impl Tester

{ } _ => None, }, + ..text::Style::default() }) .into() }, @@ -945,6 +946,7 @@ where text(label).size(14).style(|theme: &core::Theme| { text::Style { color: Some(theme.extended_palette().background.weak.text), + ..text::Style::default() } }), space::horizontal(), diff --git a/tiny_skia/src/engine.rs b/tiny_skia/src/engine.rs index 61323f04..9f0b54f2 100644 --- a/tiny_skia/src/engine.rs +++ b/tiny_skia/src/engine.rs @@ -263,6 +263,10 @@ impl Engine { } else { // Draw corners that have too small border radii as having no border radius, // but mask them with the rounded rectangle with the correct border radius. + if path_bounds.width < 1.0 || path_bounds.height < 1.0 { + return; + } + let mut temp_pixmap = tiny_skia::Pixmap::new( path_bounds.width as u32, path_bounds.height as u32, diff --git a/tiny_skia/src/raster.rs b/tiny_skia/src/raster.rs index 7c820917..0535158a 100644 --- a/tiny_skia/src/raster.rs +++ b/tiny_skia/src/raster.rs @@ -51,7 +51,21 @@ impl Pipeline { ) { let mut cache = self.cache.borrow_mut(); - let Ok(mut image) = cache.allocate(handle) else { + let target = { + let Ok(image) = cache.allocate(handle) else { + return; + }; + + graphics::image::downsample_target( + Size::new(image.width(), image.height()), + bounds.size(), + ) + }; + + let Ok(mut image) = (match target { + Some(target) => cache.allocate_resampled(handle, target), + None => cache.allocate(handle), + }) else { return; }; @@ -110,6 +124,8 @@ impl Pipeline { struct Cache { entries: FxHashMap>, hits: FxHashSet, + resampled: FxHashMap<(raster::Id, u32, u32), Entry>, + resampled_hits: FxHashSet<(raster::Id, u32, u32)>, } impl Cache { @@ -166,9 +182,59 @@ impl Cache { Ok(ret) } + /// Like [`Self::allocate`], resampled to `target`. Call after + /// [`Self::allocate`] has decoded the image. + pub fn allocate_resampled( + &mut self, + handle: &raster::Handle, + target: Size, + ) -> Result, raster::Error> { + let key = (handle.id(), target.width, target.height); + + if !self.resampled.contains_key(&key) { + let native = self + .entries + .get(&handle.id()) + .and_then(Option::as_ref) + .ok_or(raster::Error::Empty)?; + + // Stored pixels are already premultiplied, so resample them as is. + let pixels = graphics::image::downsample_premultiplied( + bytemuck::cast_slice(&native.pixels), + Size::new(native.width, native.height), + target, + ); + + let _ = self.resampled.insert( + key, + Entry { + width: target.width, + height: target.height, + pixels: pixels + .chunks_exact(4) + .map(|p| u32::from_ne_bytes([p[0], p[1], p[2], p[3]])) + .collect(), + }, + ); + } + + let _ = self.resampled_hits.insert(key); + let entry = &self.resampled[&key]; + + Ok(tiny_skia::PixmapRef::from_bytes( + bytemuck::cast_slice(&entry.pixels), + entry.width, + entry.height, + ) + .expect("Build pixmap from image bytes")) + } + fn trim(&mut self) { self.entries.retain(|key, _| self.hits.contains(key)); + self.resampled + .retain(|key, _| self.resampled_hits.contains(key)); self.hits.clear(); + self.resampled_hits.clear(); } } diff --git a/tiny_skia/src/vector.rs b/tiny_skia/src/vector.rs index 59cd1993..2c3b219c 100644 --- a/tiny_skia/src/vector.rs +++ b/tiny_skia/src/vector.rs @@ -1,5 +1,6 @@ use crate::core::svg::{Data, Handle}; use crate::core::{Color, Rectangle, Size}; +use crate::graphics::text; use resvg::usvg; use rustc_hash::{FxHashMap, FxHashSet}; @@ -74,6 +75,7 @@ struct Cache { rasters: FxHashMap, raster_hits: FxHashSet, fontdb: Option>, + fontdb_version: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -87,12 +89,15 @@ impl Cache { fn load(&mut self, handle: &Handle) -> Option<&usvg::Tree> { let id = handle.id(); - // TODO: Reuse `cosmic-text` font database - if self.fontdb.is_none() { - let mut fontdb = usvg::fontdb::Database::new(); - fontdb.load_system_fonts(); + // Reuse the font database already loaded by the `cosmic-text` font + // system. We clone it once and refresh only when a font is + // loaded (i.e. when the font system's version changes). + let font_system = text::font_system().read().expect("Read font system"); + let version = font_system.version(); - self.fontdb = Some(Arc::new(fontdb)); + if self.fontdb_version != Some(version) { + self.fontdb = Some(Arc::new(font_system.db().clone())); + self.fontdb_version = Some(version); } let options = usvg::Options { diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 48fca1b8..c278fb17 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -25,6 +25,12 @@ svg = ["iced_graphics/svg", "resvg/text"] web-colors = ["iced_graphics/web-colors"] webgl = ["wgpu/webgl"] strict-assertions = [] +# Yoda: opt-in X11 backend. Pulls tiny-xlib/as-raw-xcb-connection/x11rb +# (which otherwise were unconditional direct deps on unix). Wayland-only +# builds just leave this off — window/x11.rs and the DRI3 device-id +# fallback in window/compositor.rs are cfg-gated on it. +x11 = ["dep:as-raw-xcb-connection", "dep:tiny-xlib", "dep:x11rb"] +wayland = ["cctk"] [dependencies] iced_debug.workspace = true @@ -57,11 +63,14 @@ wayland-protocols.workspace = true wayland-backend = { version = "0.3.3", features = ["client_system"] } wayland-client = { version = "0.31.2" } wayland-sys = { version = "0.31.1", features = ["dlopen"] } -as-raw-xcb-connection = "1.0.1" -tiny-xlib = "0.2.3" -x11rb = { version = "0.13.1", features = [ +as-raw-xcb-connection = { version = "1.0.1", optional = true } +tiny-xlib = { version = "0.2.3", optional = true } +x11rb = { version = "0.13.1", optional = true, features = [ "allow-unsafe-code", "dl-libxcb", "dri3", "randr", ] } + +[build-dependencies] +build_helpers = { path = "../build_helpers" } diff --git a/wgpu/build.rs b/wgpu/build.rs new file mode 100644 index 00000000..db00a773 --- /dev/null +++ b/wgpu/build.rs @@ -0,0 +1,3 @@ +fn main() { + build_helpers::cfg_aliases_setup(); +} diff --git a/wgpu/src/buffer.rs b/wgpu/src/buffer.rs index 172922da..e251e979 100644 --- a/wgpu/src/buffer.rs +++ b/wgpu/src/buffer.rs @@ -63,7 +63,6 @@ impl Buffer { /// Returns the size of the written bytes. pub fn write( &mut self, - device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, offset: usize, @@ -79,7 +78,6 @@ impl Buffer { &self.raw, (offset + bytes_written) as u64, MAX_WRITE_SIZE_U64, - device, ) .copy_from_slice( &bytes[bytes_written..bytes_written + MAX_WRITE_SIZE], @@ -100,7 +98,6 @@ impl Buffer { &self.raw, (offset + bytes_written) as u64, bytes_left, - device, ) .copy_from_slice(&bytes[bytes_written..]); diff --git a/wgpu/src/color.rs b/wgpu/src/color.rs index 52fe8463..642d4ea7 100644 --- a/wgpu/src/color.rs +++ b/wgpu/src/color.rs @@ -99,7 +99,7 @@ pub fn convert( device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu.offscreen.blit.pipeline_layout"), bind_group_layouts: &[&constant_layout, &texture_layout], - push_constant_ranges: &[], + immediate_size: 0, }); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { @@ -149,7 +149,7 @@ pub fn convert( }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); @@ -194,6 +194,7 @@ pub fn convert( depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_pipeline(&pipeline); diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs index 09258329..d23e7452 100644 --- a/wgpu/src/image/atlas.rs +++ b/wgpu/src/image/atlas.rs @@ -132,7 +132,7 @@ impl Atlas { match &entry { Entry::Contiguous(allocation) => { self.upload_allocation( - pixels, width, 0, allocation, device, encoder, belt, + pixels, width, 0, allocation, encoder, belt, ); } Entry::Fragmented { fragments, .. } => { @@ -145,7 +145,6 @@ impl Atlas { width, offset, &fragment.allocation, - device, encoder, belt, ); @@ -312,7 +311,6 @@ impl Atlas { image_width: u32, offset: usize, allocation: &Allocation, - device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, ) { @@ -334,7 +332,6 @@ impl Atlas { let buffer_slice = belt.allocate( wgpu::BufferSize::new(total_bytes as u64).unwrap(), wgpu::BufferSize::new(8 * 4).unwrap(), - device, ); const PIXEL: usize = 4; diff --git a/wgpu/src/image/cache.rs b/wgpu/src/image/cache.rs index 097a383f..37f1e33d 100644 --- a/wgpu/src/image/cache.rs +++ b/wgpu/src/image/cache.rs @@ -5,6 +5,8 @@ use crate::image::atlas::{self, Atlas}; #[cfg(all(feature = "image", not(target_arch = "wasm32")))] use worker::Worker; +#[cfg(feature = "image")] +use rustc_hash::{FxHashMap, FxHashSet}; #[cfg(feature = "image")] use std::collections::HashMap; @@ -38,7 +40,13 @@ impl Cache { raster: Raster { cache: crate::image::raster::Cache::default(), pending: HashMap::new(), - belt: wgpu::util::StagingBelt::new(2 * 1024 * 1024), + resampled: FxHashMap::default(), + resampled_hits: FxHashSet::default(), + should_trim: false, + belt: wgpu::util::StagingBelt::new( + device.clone(), + 2 * 1024 * 1024, + ), }, #[cfg(feature = "svg")] vector: crate::image::vector::Cache::default(), @@ -206,7 +214,9 @@ impl Cache { encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, handle: &core::image::Handle, + bounds: Size, ) -> Option<(&atlas::Entry, &Arc)> { + use crate::graphics::image::{downsample, downsample_target, load}; use crate::image::raster::Memory; self.receive(); @@ -220,6 +230,34 @@ impl Cache { None, )?; + if let Some(target) = downsample_target(memory.dimensions(), bounds) { + let key = (handle.id(), target.width, target.height); + + if !self.raster.resampled.contains_key(&key) { + let image = memory.host().or_else(|| load(handle).ok())?; + let image = downsample(&image, target); + + let entry = self.atlas.upload( + device, + encoder, + belt, + target.width, + target.height, + &image, + )?; + + let _ = self.raster.resampled.insert(key, entry); + self.raster.should_trim = true; + } + + let _ = self.raster.resampled_hits.insert(key); + + return Some(( + self.raster.resampled.get(&key)?, + self.atlas.bind_group(), + )); + } + if let Memory::Device { entry, bind_group, .. } = memory @@ -300,6 +338,23 @@ impl Cache { #[cfg(not(target_arch = "wasm32"))] self.worker.drop(_bind_group); }); + + if self.raster.should_trim { + let hits = &self.raster.resampled_hits; + let atlas = &mut self.atlas; + + self.raster.resampled.retain(|key, entry| { + let retain = hits.contains(key); + + if !retain { + atlas.remove(entry); + } + + retain + }); + self.raster.resampled_hits.clear(); + self.raster.should_trim = false; + } } #[cfg(feature = "svg")] @@ -373,6 +428,9 @@ impl Drop for Cache { struct Raster { cache: crate::image::raster::Cache, pending: HashMap>, + resampled: FxHashMap<(core::image::Id, u32, u32), atlas::Entry>, + resampled_hits: FxHashSet<(core::image::Id, u32, u32)>, + should_trim: bool, belt: wgpu::util::StagingBelt, } @@ -451,7 +509,10 @@ mod worker { backend, texture_layout, shell: shell.clone(), - belt: wgpu::util::StagingBelt::new(4 * 1024 * 1024), + belt: wgpu::util::StagingBelt::new( + device.clone(), + 4 * 1024 * 1024, + ), jobs: jobs_receiver, output: work_sender, quit: quit_receiver, diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index ba98f443..2b5f7ade 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -45,7 +45,7 @@ impl Pipeline { address_mode_w: wgpu::AddressMode::ClampToEdge, min_filter: wgpu::FilterMode::Nearest, mag_filter: wgpu::FilterMode::Nearest, - mipmap_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::MipmapFilterMode::Nearest, ..Default::default() }); @@ -55,7 +55,7 @@ impl Pipeline { address_mode_w: wgpu::AddressMode::ClampToEdge, min_filter: wgpu::FilterMode::Linear, mag_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::MipmapFilterMode::Linear, ..Default::default() }); @@ -106,8 +106,8 @@ impl Pipeline { let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu::image pipeline layout"), - push_constant_ranges: &[], bind_group_layouts: &[&constant_layout, &texture_layout], + immediate_size: 0, }); let shader = @@ -191,7 +191,7 @@ impl Pipeline { mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, + multiview_mask: None, cache: None, }); @@ -267,7 +267,13 @@ impl State { clip_bounds, } => { if let Some((atlas_entry, bind_group)) = cache - .upload_raster(device, encoder, belt, &image.handle) + .upload_raster( + device, + encoder, + belt, + &image.handle, + bounds.size() * scale, + ) { match atlas.as_mut() { None => { @@ -528,7 +534,6 @@ impl Layer { &self.uniforms, 0, (bytes.len() as u64).try_into().expect("Sized uniforms"), - device, ) .copy_from_slice(bytes); @@ -539,11 +544,11 @@ impl Layer { let mut offset = 0; if !nearest.is_empty() { - offset += self.instances.write(device, encoder, belt, 0, nearest); + offset += self.instances.write(encoder, belt, 0, nearest); } if !linear.is_empty() { - let _ = self.instances.write(device, encoder, belt, offset, linear); + let _ = self.instances.write(encoder, belt, offset, linear); } } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 4eb52cc8..03a72042 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -126,6 +126,7 @@ impl Renderer { // It would be great if the `StagingBelt` API exposed methods // for introspection to detect when a resize may be worth it. staging_belt: wgpu::util::StagingBelt::new( + engine.device.clone(), buffer::MAX_WRITE_SIZE as u64, ), @@ -454,6 +455,7 @@ impl Renderer { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }, )); @@ -528,6 +530,7 @@ impl Renderer { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }, )); } @@ -621,6 +624,7 @@ impl Renderer { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }, )); } diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 31bf69cf..4cab2598 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -209,7 +209,7 @@ impl Layer { transformation: Transformation, scale: f32, ) { - self.update(device, encoder, belt, transformation, scale); + self.update(encoder, belt, transformation, scale); if !quads.solids.is_empty() { self.solid.prepare(device, encoder, belt, &quads.solids); @@ -223,7 +223,6 @@ impl Layer { pub fn update( &mut self, - device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, transformation: Transformation, @@ -237,7 +236,6 @@ impl Layer { &self.constants_buffer, 0, (bytes.len() as u64).try_into().expect("Sized uniforms"), - device, ) .copy_from_slice(bytes); } diff --git a/wgpu/src/quad/gradient.rs b/wgpu/src/quad/gradient.rs index 6a5eed8d..55e62e79 100644 --- a/wgpu/src/quad/gradient.rs +++ b/wgpu/src/quad/gradient.rs @@ -51,7 +51,7 @@ impl Layer { instances: &[Gradient], ) { let _ = self.instances.resize(device, instances.len()); - let _ = self.instances.write(device, encoder, belt, 0, instances); + let _ = self.instances.write(encoder, belt, 0, instances); self.instance_count = instances.len(); } @@ -75,8 +75,8 @@ impl Pipeline { let layout = device.create_pipeline_layout( &wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu.quad.gradient.pipeline"), - push_constant_ranges: &[], bind_group_layouts: &[constants_layout], + immediate_size: 0, }, ); @@ -155,7 +155,7 @@ impl Pipeline { mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, + multiview_mask: None, cache: None, }, ); diff --git a/wgpu/src/quad/solid.rs b/wgpu/src/quad/solid.rs index 7e48358e..49e4cf01 100644 --- a/wgpu/src/quad/solid.rs +++ b/wgpu/src/quad/solid.rs @@ -45,7 +45,7 @@ impl Layer { instances: &[Solid], ) { let _ = self.instances.resize(device, instances.len()); - let _ = self.instances.write(device, encoder, belt, 0, instances); + let _ = self.instances.write(encoder, belt, 0, instances); self.instance_count = instances.len(); } @@ -65,8 +65,8 @@ impl Pipeline { let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu.quad.solid.pipeline"), - push_constant_ranges: &[], bind_group_layouts: &[constants_layout], + immediate_size: 0, }); let shader = @@ -139,7 +139,7 @@ impl Pipeline { mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 16efa092..b6d9e679 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -619,7 +619,7 @@ fn prepare( )?; Some(cryoglyph::TextArea { - buffer, + text: buffer.layout_runs(), left: position.x, top: position.y, scale: transformation.scale_factor() diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index fa5ecb9c..5607d03a 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -313,6 +313,7 @@ fn render<'a>( depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }) }; @@ -425,18 +426,13 @@ impl Layer { let indices = mesh.indices(); - index_offset += self.index_buffer.write( - device, - encoder, - belt, - index_offset, - indices, - ); + index_offset += + self.index_buffer + .write(encoder, belt, index_offset, indices); match mesh { Mesh::Solid { buffers, .. } => { solid_vertex_offset += self.solid.vertices.write( - device, encoder, belt, solid_vertex_offset, @@ -444,16 +440,14 @@ impl Layer { ); solid_uniform_offset += self.solid.uniforms.write( - device, encoder, belt, solid_uniform_offset, &[uniforms], - ); + ) } Mesh::Gradient { buffers, .. } => { gradient_vertex_offset += self.gradient.vertices.write( - device, encoder, belt, gradient_vertex_offset, @@ -461,7 +455,6 @@ impl Layer { ); gradient_uniform_offset += self.gradient.uniforms.write( - device, encoder, belt, gradient_uniform_offset, @@ -731,7 +724,7 @@ mod solid { &wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu.triangle.solid.pipeline_layout"), bind_group_layouts: &[&constants_layout], - push_constant_ranges: &[], + immediate_size: 0, }, ); @@ -784,7 +777,7 @@ mod solid { primitive: triangle::primitive_state(), depth_stencil: None, multisample: triangle::multisample_state(antialiasing), - multiview: None, + multiview_mask: None, cache: None, }, ); @@ -886,7 +879,7 @@ mod gradient { &wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu.triangle.gradient.pipeline_layout"), bind_group_layouts: &[&constants_layout], - push_constant_ranges: &[], + immediate_size: 0, }, ); @@ -949,7 +942,7 @@ mod gradient { primitive: triangle::primitive_state(), depth_stencil: None, multisample: triangle::multisample_state(antialiasing), - multiview: None, + multiview_mask: None, cache: None, }, ); diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs index 8ca49461..46118361 100644 --- a/wgpu/src/triangle/msaa.rs +++ b/wgpu/src/triangle/msaa.rs @@ -69,8 +69,8 @@ impl Pipeline { let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu::triangle::msaa pipeline layout"), - push_constant_ranges: &[], bind_group_layouts: &[&constant_layout, &texture_layout], + immediate_size: 0, }); let shader = @@ -116,7 +116,7 @@ impl Pipeline { mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, + multiview_mask: None, cache: None, }); @@ -177,6 +177,7 @@ impl Pipeline { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }) } } @@ -320,7 +321,6 @@ impl State { 0, NonZeroU64::new(std::mem::size_of::() as u64) .expect("non-empty ratio"), - device, ) .copy_from_slice(bytemuck::bytes_of(&ratio)); @@ -351,6 +351,7 @@ impl State { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); render_pass.set_pipeline(&pipeline.raw); diff --git a/wgpu/src/window.rs b/wgpu/src/window.rs index b9ec54bd..61be2187 100644 --- a/wgpu/src/window.rs +++ b/wgpu/src/window.rs @@ -1,13 +1,8 @@ //! Display rendering results on windows. pub mod compositor; -#[cfg(all( - unix, - feature = "cctk", - not(target_os = "macos"), - not(target_os = "redox") -))] +#[cfg(wayland_platform)] mod wayland; -#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))] +#[cfg(all(unix, feature = "x11", not(target_os = "macos"), not(target_os = "redox")))] mod x11; pub use compositor::Compositor; diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 833a642e..fef87b39 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -9,16 +9,10 @@ use crate::graphics::{self, Shell, Viewport}; use crate::settings::{self, Settings}; use crate::{Engine, Renderer}; -#[cfg(all( - unix, - feature = "cctk", - not(target_os = "macos"), - not(target_os = "redox") -))] +#[cfg(wayland_platform)] use super::wayland::get_wayland_device_ids; -#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))] +#[cfg(all(unix, feature = "x11", not(target_os = "macos"), not(target_os = "redox")))] use super::x11::get_x11_device_ids; -use std::future::Future; /// A window graphics backend for iced powered by `wgpu`. pub struct Compositor { @@ -65,15 +59,12 @@ impl Compositor { compatible_window: Option, shell: Shell, ) -> Result { - #[cfg(all( - unix, - feature = "cctk", - not(target_os = "macos"), - not(target_os = "redox") - ))] + #[cfg(wayland_platform)] let ids = compatible_window.as_ref().and_then(|window| { - get_wayland_device_ids(window) - .or_else(|| get_x11_device_ids(window)) + let w = get_wayland_device_ids(window); + #[cfg(feature = "x11")] + let w = w.or_else(|| get_x11_device_ids(window)); + w }); // HACK: @@ -82,12 +73,7 @@ impl Compositor { // 3. and the user didn't set an adapter name, // 4. and the user didn't request the high power pref // => don't load the nvidia icd, as it might power on the gpu in hybrid setups causing severe delays - #[cfg(all( - unix, - feature = "cctk", - not(target_os = "macos"), - not(target_os = "redox") - ))] + #[cfg(wayland_platform)] if !matches!(ids, Some((0x10de, _))) && std::env::var_os("__NV_PRIME_RENDER_OFFLOAD") .is_none_or(|var| var == "0") @@ -126,6 +112,7 @@ impl Compositor { // if log::max_level() >= log::LevelFilter::Info { // let available_adapters: Vec<_> = instance // .enumerate_adapters(settings.backends) + // .await // .iter() // .map(wgpu::Adapter::get_info) // .collect(); @@ -146,15 +133,11 @@ impl Compositor { let mut adapter = None; #[cfg_attr(not(unix), allow(dead_code))] if std::env::var_os("WGPU_ADAPTER_NAME").is_none() { - #[cfg(all( - unix, - feature = "cctk", - not(target_os = "macos"), - not(target_os = "redox") - ))] + #[cfg(wayland_platform)] if let Some((vendor_id, device_id)) = ids { adapter = instance .enumerate_adapters(settings.backends) + .await .into_iter() .filter(|adapter| { let info = adapter.get_info(); @@ -172,6 +155,7 @@ impl Compositor { } else if let Ok(name) = std::env::var("WGPU_ADAPTER_NAME") { adapter = instance .enumerate_adapters(settings.backends) + .await .into_iter() .filter(|adapter| { let info = adapter.get_info(); @@ -184,6 +168,8 @@ impl Compositor { true } }); + } else { + adapter = instance.request_adapter(&adapter_options).await.ok(); } let adapter = match adapter { @@ -286,6 +272,14 @@ impl Compositor { ..limits }); + // Request SHADER_F16 only if the adapter supports it (e.g., not available in WebGL2) + let required_features = + if adapter.features().contains(wgpu::Features::SHADER_F16) { + wgpu::Features::SHADER_F16 + } else { + wgpu::Features::empty() + }; + let mut errors = Vec::new(); for required_limits in limits { @@ -294,7 +288,7 @@ impl Compositor { label: Some( "iced_wgpu::window::compositor device descriptor", ), - required_features: wgpu::Features::empty(), + required_features, required_limits: required_limits.clone(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, @@ -352,6 +346,14 @@ pub fn present( ) -> Result<(), compositor::SurfaceError> { match surface.get_current_texture() { Ok(frame) => { + if frame.texture.width() != viewport.physical_width() + || frame.texture.height() != viewport.physical_height() + { + return Err(compositor::SurfaceError::Outdated); + } + + let suboptimal = frame.suboptimal; + let view = &frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); @@ -367,6 +369,10 @@ pub fn present( on_pre_present(); frame.present(); + if suboptimal { + return Err(compositor::SurfaceError::Outdated); + } + Ok(()) } Err(error) => match error { diff --git a/wgpu/src/window/x11.rs b/wgpu/src/window/x11.rs index 58da401a..a1508134 100644 --- a/wgpu/src/window/x11.rs +++ b/wgpu/src/window/x11.rs @@ -1,3 +1,5 @@ +#![allow(unexpected_cfgs)] + use std::{ fs, io::{BufRead, BufReader}, @@ -144,7 +146,9 @@ pub fn get_x11_device_ids(window: &W) -> Option<(u16, u16)> { { let stat = stat(Path::new("/dev/dri").join(device)).ok()?; - let dev = stat.st_rdev; + // Hack (u64 as u64) for E2K CPU architecture + #[allow(clippy::unnecessary_cast)] + let dev = stat.st_rdev as u64; return super::ids_from_dev(dev); } } @@ -165,7 +169,9 @@ pub fn get_x11_device_ids(window: &W) -> Option<(u16, u16)> { let dri3 = conn.dri3_open(root, x11rb::NONE).ok()?.reply().ok()?; let device_fd = dri3.device_fd; let stat = fstat(device_fd).ok()?; - let dev = stat.st_rdev; + // Hack (u64 as u64) for E2K CPU architecture + #[allow(clippy::unnecessary_cast)] + let dev = stat.st_rdev as u64; super::ids_from_dev(dev) } } diff --git a/widget/Cargo.toml b/widget/Cargo.toml index ab34819b..0726ae40 100644 --- a/widget/Cargo.toml +++ b/widget/Cargo.toml @@ -29,15 +29,13 @@ highlighter = ["dep:iced_highlighter"] advanced = [] crisp = [] a11y = ["iced_accessibility"] -cctk = ["iced_runtime/cctk", "dep:cctk"] +wayland = ["iced_runtime/wayland", "dep:cctk"] [dependencies] iced_renderer.workspace = true iced_runtime.workspace = true iced_accessibility.workspace = true iced_accessibility.optional = true -cctk.workspace = true -cctk.optional = true num-traits.workspace = true log.workspace = true rustc-hash.workspace = true @@ -57,3 +55,10 @@ pulldown-cmark.optional = true iced_highlighter.workspace = true iced_highlighter.optional = true + +[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies] +cctk.workspace = true +cctk.optional = true + +[build-dependencies] +build_helpers = { path = "../build_helpers" } diff --git a/widget/build.rs b/widget/build.rs new file mode 100644 index 00000000..db00a773 --- /dev/null +++ b/widget/build.rs @@ -0,0 +1,3 @@ +fn main() { + build_helpers::cfg_aliases_setup(); +} diff --git a/widget/src/button.rs b/widget/src/button.rs index f137221e..a99dd9e2 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -267,6 +267,7 @@ struct State { is_hovered: bool, is_pressed: bool, is_focused: bool, + needs_redraw: bool, } impl<'a, Message, Theme, Renderer> Widget @@ -368,6 +369,12 @@ where viewport, ); + let state = tree.state.downcast_mut::(); + if state.needs_redraw { + state.needs_redraw = false; + shell.request_redraw(); + } + if shell.is_event_captured() { return; } @@ -777,11 +784,11 @@ impl Default for Style { /// /// impl Catalog for MyTheme { /// type Class<'a> = ButtonClass; -/// +/// /// fn default<'a>() -> Self::Class<'a> { /// ButtonClass::default() /// } -/// +/// /// /// fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { /// let mut style = Style::default(); @@ -1000,10 +1007,12 @@ impl operation::Focusable for State { } fn focus(&mut self) { + self.needs_redraw |= !self.is_focused; self.is_focused = true; } fn unfocus(&mut self) { + self.needs_redraw |= self.is_focused; self.is_focused = false; } } diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 873a4541..6ac48b0e 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -505,6 +505,7 @@ where state.raw(), crate::text::Style { color: style.text_color, + ..crate::text::Style::default() }, viewport, ); diff --git a/widget/src/container.rs b/widget/src/container.rs index c9a300c9..a264007a 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -35,7 +35,7 @@ use crate::core::{ color, }; -use iced_runtime::{Action, Task, task}; +use iced_runtime::{Task, task}; /// A widget that aligns its contents inside of its boundaries. /// @@ -601,7 +601,7 @@ pub fn visible_bounds(id: Id) -> Task> { operate: &mut dyn FnMut(&mut dyn Operation>), ) { self.depth += 1; - self.traverse(operate); + operate(self); self.depth -= 1; } diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index baaee5c9..bb98d606 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -1,7 +1,6 @@ //! Keyed columns distribute content vertically while keeping continuity. //! Distribute content vertically. -use crate::core::event; use crate::core::layout; use crate::core::mouse; use crate::core::overlay; @@ -293,9 +292,12 @@ where .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), c_layout)| { - child - .as_widget_mut() - .operate(state, c_layout.with_virtual_offset(layout.virtual_offset()), renderer, operation); + child.as_widget_mut().operate( + state, + c_layout.with_virtual_offset(layout.virtual_offset()), + renderer, + operation, + ); }); }); } @@ -318,7 +320,13 @@ where .zip(layout.children()) { child.as_widget_mut().update( - tree, event, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, renderer, clipboard, shell, + tree, + event, + c_layout.with_virtual_offset(layout.virtual_offset()), + cursor, + renderer, + clipboard, + shell, viewport, ); } @@ -337,9 +345,13 @@ where .zip(&tree.children) .zip(layout.children()) .map(|((child, tree), c_layout)| { - child - .as_widget() - .mouse_interaction(tree, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, viewport, renderer) + child.as_widget().mouse_interaction( + tree, + c_layout.with_virtual_offset(layout.virtual_offset()), + cursor, + viewport, + renderer, + ) }) .max() .unwrap_or_default() diff --git a/widget/src/lib.rs b/widget/src/lib.rs index d9bdf1f3..526f3576 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -9,6 +9,8 @@ pub use iced_renderer::graphics; pub use core::widget::Id; +pub(crate) const DRAG_DEADBAND_DISTANCE: f32 = 10.0; + mod action; mod mouse_area; mod pin; diff --git a/widget/src/list.rs b/widget/src/list.rs index de9fe9b8..9f33244e 100644 --- a/widget/src/list.rs +++ b/widget/src/list.rs @@ -1,5 +1,5 @@ #![allow(missing_docs)] -use crate::core::event::{self, Event}; +use crate::core::event::Event; use crate::core::layout; use crate::core::mouse; use crate::core::overlay; @@ -363,24 +363,25 @@ where let state = tree.state.downcast_mut::(); let offset = layout.position() - Point::ORIGIN; - self.visible_elements + for (element, (index, layout, tree)) in self + .visible_elements .iter_mut() .zip(&mut state.visible_layouts) - .map(|(element, (index, layout, tree))| { - element.as_widget_mut().update( - tree, - event, - Layout::with_offset( - offset + Vector::new(0.0, self.spacing * *index as f32), - layout, - ), - cursor, - renderer, - clipboard, - shell, - viewport, - ) - }); + { + element.as_widget_mut().update( + tree, + event, + Layout::with_offset( + offset + Vector::new(0.0, self.spacing * *index as f32), + layout, + ), + cursor, + renderer, + clipboard, + shell, + viewport, + ); + } if let Event::Window(window::Event::RedrawRequested(_)) = event { match &mut state.task { diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index 3d355c52..4d808348 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -2,7 +2,6 @@ use iced_renderer::core::mouse::Click; -use crate::core::event; use crate::core::layout; use crate::core::mouse; use crate::core::overlay; diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index d43c69bd..9358c0ad 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -92,7 +92,8 @@ use crate::core::{ }; use log::trace; -const DRAG_DEADBAND_DISTANCE: f32 = 10.0; +use crate::DRAG_DEADBAND_DISTANCE; + const THICKNESS_RATIO: f32 = 25.0; /// A collection of panes distributed using either vertical or horizontal splits diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index a76010e7..4c41e3a6 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -6,7 +6,7 @@ use crate::core::renderer; use crate::core::widget::{self, Tree}; use crate::core::{ self, Clipboard, Element, Event, Layout, Point, Rectangle, Shell, Size, - Vector, event, + Vector, }; use crate::pane_grid::{Draggable, TitleBar}; diff --git a/widget/src/pin.rs b/widget/src/pin.rs index 8eb5906e..cef75944 100644 --- a/widget/src/pin.rs +++ b/widget/src/pin.rs @@ -60,6 +60,7 @@ where width: Length, height: Length, position: Point, + overflow: bool, } impl<'a, Message, Theme, Renderer> Pin<'a, Message, Theme, Renderer> @@ -75,6 +76,7 @@ where width: Length::Fill, height: Length::Fill, position: Point::ORIGIN, + overflow: false, } } @@ -107,6 +109,12 @@ where self.position.y = y.into().0; self } + + /// Sets whether the [`Pin`] can overflow its boundaries. + pub fn overflow(mut self, overflow: bool) -> Self { + self.overflow = overflow; + self + } } impl Widget @@ -145,8 +153,12 @@ where ) -> layout::Node { let limits = limits.width(self.width).height(self.height); - let available = - limits.max() - Size::new(self.position.x, self.position.y); + let available = + if self.overflow { + limits.max() + } else { + limits.max() - Size::new(self.position.x, self.position.y) + }; let node = self .content diff --git a/widget/src/radio.rs b/widget/src/radio.rs index 1aa97355..bdca259c 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -459,6 +459,7 @@ where state.raw(), crate::text::Style { color: style.text_color, + ..crate::text::Style::default() }, viewport, ); diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index e005a7b8..819af02d 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -27,7 +27,6 @@ use iced_runtime::core::widget::Id; #[cfg(feature = "a11y")] use std::borrow::Cow; -use crate::core::event; use crate::core::keyboard; use crate::core::layout; use crate::core::mouse; @@ -248,64 +247,19 @@ where self } - /// Sets whether the user should be allowed to auto-scroll the [`Scrollable`] - /// with the middle mouse button. + /// Adds padding at the ends of the [`Scrollbar`]s of the [`Scrollable`]. /// - /// By default, it is disabled. - pub fn auto_scroll(mut self, auto_scroll: bool) -> Self { - self.auto_scroll = auto_scroll; - self - } - - /// Sets the scrollbar width of the [`Scrollbar`]. - pub fn scrollbar_width(mut self, width: impl Into) -> Self { - let width = width.into().0.max(0.0); + /// The `padding` provided will be used as space at the top and bottom of a + /// vertical [`Scrollbar`], and at the left and right ends of a horizontal + /// [`Scrollbar`], when they are visible. + /// + /// Unlike [`Self::spacing`], the padding does not affect the layout of the + /// [`Scrollable`]. + pub fn padding(mut self, new_padding: impl Into) -> Self { + let padding = new_padding.into().0; match &mut self.direction { - Direction::Horizontal(scrollbar) - | Direction::Vertical(scrollbar) => { - scrollbar.width = width; - } - Direction::Both { - horizontal, - vertical, - } => { - horizontal.width = width; - vertical.width = width; - } - } - - self - } - - /// Sets the scroller width of the [`Scrollbar`]. - pub fn scroller_width(mut self, width: impl Into) -> Self { - let width = width.into().0.max(0.0); - - match &mut self.direction { - Direction::Horizontal(scrollbar) - | Direction::Vertical(scrollbar) => { - scrollbar.scroller_width = width; - } - Direction::Both { - horizontal, - vertical, - } => { - horizontal.scroller_width = width; - vertical.scroller_width = width; - } - } - - self - } - - /// Sets the padding at the start and end of the [`Scrollbar`]. - pub fn scrollbar_padding(mut self, padding: impl Into) -> Self { - let padding = padding.into().0.max(0.0); - - match &mut self.direction { - Direction::Horizontal(scrollbar) - | Direction::Vertical(scrollbar) => { + Direction::Horizontal(scrollbar) | Direction::Vertical(scrollbar) => { scrollbar.padding = padding; } Direction::Both { @@ -320,6 +274,15 @@ where self } + /// Sets whether the user should be allowed to auto-scroll the [`Scrollable`] + /// with the middle mouse button. + /// + /// By default, it is disabled. + pub fn auto_scroll(mut self, auto_scroll: bool) -> Self { + self.auto_scroll = auto_scroll; + self + } + /// Sets the style of this [`Scrollable`]. #[must_use] pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self @@ -504,9 +467,15 @@ impl Scrollbar { self } - /// Sets the padding at the start and end of the [`Scrollbar`]. + /// Sets the padding of the [`Scrollbar`]. + /// + /// The padding is added at the top and bottom of the scrollbar (or at the + /// left and right ends for a horizontal [`Scrollbar`]) when it is visible. + /// + /// Unlike [`Self::margin`] and [`Self::spacing`], the padding does not + /// affect the layout of the [`Scrollable`]. pub fn padding(mut self, padding: impl Into) -> Self { - self.padding = padding.into().0.max(0.0); + self.padding = padding.into().0; self } } @@ -676,13 +645,7 @@ where let translation = state.translation(self.direction, bounds, content_bounds); - operation.scrollable( - Some(&self.id), - bounds, - content_bounds, - translation, - state, - ); + operation.pre_operation(Some(&self.id)); operation.traverse(&mut |operation| { self.content.as_widget_mut().operate( @@ -696,6 +659,14 @@ where operation, ); }); + // XXX must be done after traversing to perform DFS + operation.scrollable( + Some(&self.id), + bounds, + content_bounds, + translation, + state, + ); } fn update( @@ -933,7 +904,7 @@ where let had_input_method = shell.input_method().is_enabled(); - let mut c_event = match event.clone() { + let c_event = match event.clone() { Event::Dnd(dnd::DndEvent::Offer( id, dnd::OfferEvent::Enter { @@ -961,6 +932,16 @@ where y: y + translation.y as f64, }, )), + Event::Touch(touch::Event::FingerLifted { + id, + position, + }) if matches!( + state.interaction, + Interaction::TouchScrolling(_) + ) => + { + Event::Touch(touch::Event::FingerLost { id, position }) + } e => e, }; self.content.as_widget_mut().update( @@ -988,6 +969,14 @@ where } }; + if matches!( + event, + Event::Mouse(mouse::Event::CursorMoved { .. }) + | Event::Touch(touch::Event::FingerPressed { .. }) + ) { + state.suppress_touch_hover = false; + } + if matches!( event, Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) @@ -996,10 +985,18 @@ where | touch::Event::FingerLost { .. } ) ) { + if matches!(state.interaction, Interaction::TouchScrolling(_)) { + state.suppress_touch_hover = true; + } state.interaction = Interaction::None; + state.touch_press_start = None; return; } + if let Event::Touch(touch::Event::FingerPressed { .. }) = event { + state.touch_press_start = cursor_over_scrollable; + } + if shell.is_event_captured() { return; } @@ -1093,14 +1090,31 @@ where Interaction::TouchScrolling(position); } touch::Event::FingerMoved { .. } => { - let Interaction::TouchScrolling( - scroll_box_touched_at, - ) = state.interaction + let Some(cursor_position) = cursor.position() else { return; }; - let Some(cursor_position) = cursor.position() + if !matches!( + state.interaction, + Interaction::TouchScrolling(_) + ) { + let Some(start) = state.touch_press_start + else { + return; + }; + if start.distance(cursor_position) + < crate::DRAG_DEADBAND_DISTANCE + { + return; + } + state.interaction = + Interaction::TouchScrolling(start); + } + + let Interaction::TouchScrolling( + scroll_box_touched_at, + ) = state.interaction else { return; }; @@ -1322,6 +1336,14 @@ where state.translation(self.direction, bounds, content_bounds); let cursor = match cursor_over_scrollable { + _ if state.suppress_touch_hover + || matches!( + state.interaction, + Interaction::TouchScrolling(_) + ) => + { + mouse::Cursor::Unavailable + } Some(cursor_position) if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) => { @@ -1497,6 +1519,14 @@ where state.translation(self.direction, bounds, content_bounds); let cursor = match cursor_over_scrollable { + _ if state.suppress_touch_hover + || matches!( + state.interaction, + Interaction::TouchScrolling(_) + ) => + { + cursor.levitate() + translation + } Some(cursor_position) if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) => { @@ -2022,6 +2052,8 @@ struct State { last_notified: Option, last_scrolled: Option, is_scrollbar_visible: bool, + touch_press_start: Option, + suppress_touch_hover: bool, } #[derive(Debug, Clone, Copy)] @@ -2047,6 +2079,8 @@ impl Default for State { last_notified: None, last_scrolled: None, is_scrollbar_visible: true, + touch_press_start: None, + suppress_touch_hover: false, } } } @@ -2349,13 +2383,16 @@ impl Scrollbars { let total_scrollbar_width = width.max(scroller_width) + 2.0 * margin; + // The padding is purely visual: it shrinks the top and bottom of the + // scrollbar without affecting the layout + let scrollbar_height = (bounds.height - x_scrollbar_height - 2.0 * padding).max(0.0); + // Total bounds of the scrollbar + margin + scroller width let total_scrollbar_bounds = Rectangle { x: bounds.x + bounds.width - total_scrollbar_width, y: bounds.y + padding, width: total_scrollbar_width, - height: (bounds.height - x_scrollbar_height - 2.0 * padding) - .max(0.0), + height: scrollbar_height, }; // Bounds of just the scrollbar @@ -2365,8 +2402,7 @@ impl Scrollbars { - width / 2.0, y: bounds.y + padding, width, - height: (bounds.height - x_scrollbar_height - 2.0 * padding) - .max(0.0), + height: scrollbar_height, }; let ratio = bounds.height / content_bounds.height; @@ -2423,12 +2459,15 @@ impl Scrollbars { let total_scrollbar_height = width.max(scroller_width) + 2.0 * margin; + // The padding is purely visual: it shrinks the left and right ends of + // the scrollbar without affecting the layout + let scrollbar_width = (bounds.width - scrollbar_y_width - 2.0 * padding).max(0.0); + // Total bounds of the scrollbar + margin + scroller width let total_scrollbar_bounds = Rectangle { x: bounds.x + padding, y: bounds.y + bounds.height - total_scrollbar_height, - width: (bounds.width - scrollbar_y_width - 2.0 * padding) - .max(0.0), + width: scrollbar_width, height: total_scrollbar_height, }; @@ -2438,8 +2477,7 @@ impl Scrollbars { y: bounds.y + bounds.height - total_scrollbar_height / 2.0 - width / 2.0, - width: (bounds.width - scrollbar_y_width - 2.0 * padding) - .max(0.0), + width: scrollbar_width, height: width, }; diff --git a/widget/src/slider.rs b/widget/src/slider.rs index ff32f417..de8ea65c 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -110,6 +110,8 @@ where height: f32, class: Theme::Class<'a>, status: Option, + handle_width: f32, + handle_height: f32, } impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme> @@ -119,7 +121,13 @@ where Theme: Catalog, { /// The default height of a [`Slider`]. - pub const DEFAULT_HEIGHT: f32 = 16.0; + pub const DEFAULT_HEIGHT: f32 = 26.0; + + /// The default max height of a [`Handle`]. + pub const DEFAULT_MAX_HANDLE_HEIGHT: f32 = 26.0; + + /// The default max width of a [`Handle`]. + pub const DEFAULT_MAX_HANDLE_WIDTH: f32 = 26.0; /// Creates a new [`Slider`]. /// @@ -165,6 +173,8 @@ where height: Self::DEFAULT_HEIGHT, class: Theme::default(), status: None, + handle_width: Self::DEFAULT_MAX_HANDLE_WIDTH, + handle_height: Self::DEFAULT_MAX_HANDLE_HEIGHT, } } @@ -207,6 +217,24 @@ where self } + /// Sets the max width of the [`Handle`]. + /// + /// Theme can be used to change the size and shape of handle + /// but width of handle defined in theme will not exceed this width + pub fn handle_width(mut self, width: impl Into) -> Self { + self.handle_width = width.into().0; + self + } + + /// Sets the max height of the [`Handle`]. + /// + /// Theme can be used to change the size and shape of handle + /// but height of handle defined in theme will not exceed this height + pub fn handle_height(mut self, height: impl Into) -> Self { + self.handle_height = height.into().0; + self + } + /// Sets the step size of the [`Slider`]. pub fn step(mut self, step: impl Into) -> Self { self.step = step.into(); @@ -316,15 +344,22 @@ where _renderer: &Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let state = tree.state.downcast_mut::(); let mut update = || { let current_value = self.value; + self.handle_width = self.handle_width.min(layout.bounds().width); + self.handle_height = self.handle_height.min(layout.bounds().height); + let locate = |cursor_position: Point| -> Option { - let bounds = layout.bounds(); + let bounds = Rectangle { + x: layout.bounds().x + self.handle_width / 2.0, + width: layout.bounds().width - self.handle_width, + ..layout.bounds() + }; if cursor_position.x <= bounds.x { Some(*self.range.start()) @@ -417,6 +452,8 @@ where Event::Mouse(mouse::Event::ButtonReleased( mouse::Button::Left, )) + | Event::Mouse(mouse::Event::CursorLeft) + | Event::Mouse(mouse::Event::CursorEntered) | Event::Touch(touch::Event::FingerLifted { .. }) | Event::Touch(touch::Event::FingerLost { .. }) => { if state.is_dragging { @@ -429,13 +466,20 @@ where Event::Mouse(mouse::Event::CursorMoved { .. }) | Event::Touch(touch::Event::FingerMoved { .. }) => { if state.is_dragging { - let _ = cursor - .land() - .position() - .and_then(locate) - .map(change); + let position = cursor.land().position(); + let outside = position.is_none_or(|position| { + !viewport.contains(position) + }); - shell.capture_event(); + if outside { + if let Some(on_release) = self.on_release.clone() { + shell.publish(on_release); + } + state.is_dragging = false; + } else { + let _ = position.and_then(locate).map(change); + shell.capture_event(); + } } } Event::Mouse(mouse::Event::WheelScrolled { delta }) @@ -509,54 +553,30 @@ where _cursor: mouse::Cursor, _viewport: &Rectangle, ) { - let bounds = layout.bounds(); + let bounds = Rectangle { + x: layout.bounds().x + self.handle_width / 2.0, + width: layout.bounds().width - self.handle_width, + ..layout.bounds() + }; let style = theme.style(&self.class, self.status.unwrap_or(Status::Active)); - let border_width = style - .handle - .border_width - .min(bounds.height / 2.0) - .min(bounds.width / 2.0); - - let (handle_width, handle_height, handle_border_radius) = + let (mut handle_width, mut handle_height, handle_border_radius) = match style.handle.shape { HandleShape::Circle { radius } => { - let radius = (radius) - .max(2.0 * border_width) - .min(bounds.height / 2.0) - .min(bounds.width / 2.0 + 2.0 * border_width); (radius * 2.0, radius * 2.0, Radius::from(radius)) } HandleShape::Rectangle { height, width, border_radius, - } => { - let width = (f32::from(width)).max(2.0 * border_width); - let height = (f32::from(height)).max(2.0 * border_width); - let mut border_radius: [f32; 4] = border_radius.into(); - for r in &mut border_radius { - *r = (*r) - .min(height / 2.0) - .min(width / 2.0) - .max(*r * (width + border_width * 2.0) / width); - } - - ( - width, - height, - Radius { - top_left: border_radius[0], - top_right: border_radius[1], - bottom_right: border_radius[2], - bottom_left: border_radius[3], - }, - ) - } + } => (f32::from(width), f32::from(height), border_radius), }; + handle_width = handle_width.min(self.handle_width); + handle_height = handle_height.min(self.handle_height); + let value = self.value.into() as f32; let (range_start, range_end) = { let (start, end) = self.range.clone().into_inner(); @@ -567,8 +587,7 @@ where let offset = if range_start >= range_end { 0.0 } else { - (bounds.width - handle_width) * (value - range_start) - / (range_end - range_start) + bounds.width * (value - range_start) / (range_end - range_start) }; let rail_y = bounds.y + bounds.height / 2.0; @@ -590,7 +609,8 @@ where x: bounds.x + offset, y: rail_y + 6.0, width: BREAKPOINT_WIDTH, - height: 8.0, + height: 8.0_f32 + .min(bounds.y + bounds.height - (rail_y + 6.0)), }, border: Border { radius: 0.0.into(), @@ -607,9 +627,9 @@ where renderer::Quad { bounds: Rectangle { x: bounds.x, - y: rail_y - style.rail.width / 2.0, - width: offset + handle_width / 2.0, - height: style.rail.width, + y: rail_y - (style.rail.width).min(bounds.height) / 2.0, + width: offset, + height: (style.rail.width).min(bounds.height), }, border: style.rail.border, ..renderer::Quad::default() @@ -622,10 +642,10 @@ where renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: bounds.x + offset + handle_width / 2.0, - y: rail_y - style.rail.width / 2.0, - width: bounds.width - offset - handle_width / 2.0, - height: style.rail.width, + x: bounds.x + offset, + y: rail_y - (style.rail.width).min(bounds.height) / 2.0, + width: bounds.width - offset, + height: (style.rail.width).min(bounds.height), }, border: style.rail.border, ..renderer::Quad::default() @@ -637,7 +657,7 @@ where renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: bounds.x + offset, + x: bounds.x + offset - handle_width / 2.0, y: rail_y - (handle_height / 2.0), width: handle_width, height: handle_height, diff --git a/widget/src/stack.rs b/widget/src/stack.rs index e85bf2af..cd30a886 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -1,6 +1,5 @@ //! Display content on top of other content. -use crate::core::event; use crate::core::layout; use crate::core::mouse; use crate::core::overlay; diff --git a/widget/src/text/rich.rs b/widget/src/text/rich.rs index cd368e83..ef44e2bf 100644 --- a/widget/src/text/rich.rs +++ b/widget/src/text/rich.rs @@ -179,7 +179,10 @@ where { let color = color.map(Into::into); - self.style(move |_theme| Style { color }) + self.style(move |_theme| Style { + color, + ..Style::default() + }) } /// Sets the default style class of the [`Rich`] text. diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index b93c96f3..0681989b 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -544,6 +544,9 @@ pub struct State { highlighter: RefCell, highlighter_settings: Highlighter::Settings, highlighter_format_address: usize, + context_menu_position: Option, + clipboard_has_text: bool, + pending_edit: Option, } #[derive(Debug, Clone)] @@ -579,6 +582,14 @@ impl State { pub fn is_focused(&self) -> bool { self.focus.is_some() } + + /// Clears focus, selection, and all interaction state. + pub fn clear_focus(&mut self) { + self.focus = None; + self.drag_click = None; + self.context_menu_position = None; + self.pending_edit = Some(Action::ClearSelection); + } } impl operation::Focusable @@ -593,7 +604,7 @@ impl operation::Focusable } fn unfocus(&mut self) { - self.focus = None; + self.clear_focus(); } } @@ -621,6 +632,9 @@ where )), highlighter_settings: self.highlighter_settings.clone(), highlighter_format_address: self.highlighter_format as usize, + context_menu_position: None, + clipboard_has_text: false, + pending_edit: None, }) } @@ -699,6 +713,60 @@ where shell: &mut Shell<'_, Message>, _viewport: &Rectangle, ) { + // Handle context menu and focus-loss on clicks outside. + { + let state = tree.state.downcast_mut::>(); + let text_bounds = layout.bounds(); + + if matches!( + event, + Event::Mouse(mouse::Event::ButtonPressed(_)) + | Event::Touch( + crate::core::touch::Event::FingerPressed { .. } + ) + ) && cursor.position_over(text_bounds).is_none() + { + state.clear_focus(); + } + + match event { + Event::Mouse(mouse::Event::ButtonPressed( + mouse::Button::Right, + )) => { + if let Some(pos) = cursor.position_over(text_bounds) { + if state.focus.is_none() { + state.focus = Some(Focus::now()); + } + state.context_menu_position = Some(pos); + state.clipboard_has_text = + widget::text::clipboard_has_text(clipboard); + shell.capture_event(); + return; + } + } + Event::Mouse(mouse::Event::ButtonPressed( + mouse::Button::Left, + )) => { + if state.context_menu_position.take().is_some() { + shell.capture_event(); + return; + } + } + _ => {} + } + } + + // Process pending edits from context menu + // publishes via on_edit so the app calls content.perform(). + { + let state = tree.state.downcast_mut::>(); + if let Some(action) = state.pending_edit.take() { + if let Some(on_edit) = self.on_edit.as_ref() { + shell.publish((on_edit)(action)); + } + } + } + let Some(on_edit) = self.on_edit.as_ref() else { return; }; @@ -1223,6 +1291,23 @@ impl Binding { return Some(binding); } + let alt_clipboard = match key.as_ref() { + keyboard::Key::Named(key::Named::Insert) if modifiers.shift() => { + Some(Self::Paste) + } + keyboard::Key::Named(key::Named::Insert) if modifiers.command() => { + Some(Self::Copy) + } + keyboard::Key::Named(key::Named::Delete) if modifiers.shift() => { + Some(Self::Cut) + } + _ => None, + }; + + if let Some(binding) = alt_clipboard { + return Some(binding); + } + #[cfg(target_os = "macos")] let modified_key = convert_macos_shortcut(&key, modifiers).unwrap_or(modified_key); @@ -1545,3 +1630,73 @@ pub(crate) fn convert_macos_shortcut( Some(keyboard::Key::Named(key)) } + +use crate::core::widget::text::HasSelectableText; + +impl HasSelectableText + for TextEditor<'_, Highlighter, Message, Theme, Renderer> +where + Highlighter: text::Highlighter, + Theme: Catalog, + Renderer: text::Renderer, +{ + fn selected_text(&self, _tree: &widget::Tree) -> Option { + self.content.selection() + } + + fn select_all(&self, tree: &mut widget::Tree) { + let state = tree.state.downcast_mut::>(); + state.pending_edit = Some(Action::SelectAll); + } + + fn is_editable(&self) -> bool { + self.on_edit.is_some() + } + + fn has_text(&self, _tree: &widget::Tree) -> bool { + !self.content.is_empty() + } + + fn clipboard_has_text(&self, tree: &widget::Tree) -> bool { + tree.state + .downcast_ref::>() + .clipboard_has_text + } + + fn is_focused(&self, tree: &widget::Tree) -> bool { + tree.state.downcast_ref::>().is_focused() + } + + fn context_menu_position(&self, tree: &widget::Tree) -> Option { + tree.state + .downcast_ref::>() + .context_menu_position + } + + fn set_context_menu_position( + &self, + tree: &mut widget::Tree, + pos: Option, + ) { + tree.state + .downcast_mut::>() + .context_menu_position = pos; + } + + fn delete_selection(&self, tree: &mut widget::Tree) -> Option { + let state = tree.state.downcast_mut::>(); + state.pending_edit = Some(Action::Edit(Edit::Delete)); + Some(String::new()) + } + + fn paste_text( + &self, + tree: &mut widget::Tree, + text: &str, + ) -> Option { + let state = tree.state.downcast_mut::>(); + state.pending_edit = + Some(Action::Edit(Edit::Paste(Arc::new(text.to_owned())))); + Some(String::new()) + } +} diff --git a/widget/src/themer.rs b/widget/src/themer.rs index 306c8580..76448ae1 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -1,6 +1,5 @@ use crate::container; -use crate::core::event; use crate::core::layout; use crate::core::mouse; use crate::core::overlay; diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index d398a79b..b4e66887 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -37,7 +37,6 @@ use std::borrow::Cow; use iced_runtime::core::border::Radius; use crate::core::alignment; -use crate::core::border; use crate::core::layout; use crate::core::mouse; use crate::core::renderer; @@ -483,6 +482,7 @@ where state.raw(), crate::text::Style { color: style.text_color, + ..crate::text::Style::default() }, viewport, ); @@ -509,7 +509,7 @@ where let space = style.handle_margin; - let toggler_background_bounds = Rectangle { + let _toggler_background_bounds = Rectangle { x: bounds.x, y: bounds.y, width: bounds.width, @@ -529,7 +529,7 @@ where style.background, ); - let padding = (style.padding_ratio * bounds.height).round(); + let _padding = (style.padding_ratio * bounds.height).round(); let toggler_foreground_bounds = Rectangle { x: bounds.x + if self.is_toggled { diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index a5260374..47a81ce0 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -255,7 +255,7 @@ where _renderer: &Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let state = tree.state.downcast_mut::(); let is_dragging = state.is_dragging; @@ -354,6 +354,8 @@ where } } Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Mouse(mouse::Event::CursorLeft) + | Event::Mouse(mouse::Event::CursorEntered) | Event::Touch(touch::Event::FingerLifted { .. }) | Event::Touch(touch::Event::FingerLost { .. }) => { if is_dragging { @@ -366,10 +368,20 @@ where Event::Mouse(mouse::Event::CursorMoved { .. }) | Event::Touch(touch::Event::FingerMoved { .. }) => { if is_dragging { - let _ = - cursor.land().position().and_then(locate).map(change); + let position = cursor.land().position(); + let outside = position.is_none_or(|position| { + !viewport.contains(position) + }); - shell.capture_event(); + if outside { + if let Some(on_release) = self.on_release.clone() { + shell.publish(on_release); + } + state.is_dragging = false; + } else { + let _ = position.and_then(locate).map(change); + shell.capture_event(); + } } } Event::Mouse(mouse::Event::WheelScrolled { delta }) diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 41e4a582..741528b5 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -14,7 +14,18 @@ keywords.workspace = true workspace = true [features] -wayland = ["winit/wayland", "wayland-csd-adwaita"] +wayland = [ + "winit/wayland", + "wayland-csd-adwaita", + "wayland-protocols", + "raw-window-handle", + "iced_runtime/wayland", + "wayland-backend", + "xkbcommon", + "xkbcommon-dl", + "xkeysym", + "dep:cctk", +] default = ["x11", "wayland-dlopen"] debug = ["iced_debug/enable"] sysinfo = ["dep:sysinfo"] @@ -26,17 +37,6 @@ program = [] wayland-dlopen = ["winit/wayland-dlopen"] wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] a11y = ["iced_accessibility", "iced_runtime/a11y"] -cctk = [ - "wayland", - "wayland-protocols", - "raw-window-handle", - "iced_runtime/cctk", - "wayland-backend", - "xkbcommon", - "xkbcommon-dl", - "xkeysym", - "dep:cctk", -] # Prevents multiple separate process instances. single-instance = [] @@ -64,7 +64,8 @@ iced_debug.workspace = true iced_program.workspace = true -[target.'cfg(target_os = "linux")'.dependencies] +[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies] + mundy.workspace = true mundy.optional = true @@ -89,3 +90,6 @@ winapi.workspace = true web-sys.workspace = true web-sys.features = ["Document", "Window", "HtmlCanvasElement"] wasm-bindgen-futures.workspace = true + +[build-dependencies] +build_helpers = { path = "../build_helpers" } diff --git a/winit/build.rs b/winit/build.rs new file mode 100644 index 00000000..db00a773 --- /dev/null +++ b/winit/build.rs @@ -0,0 +1,3 @@ +fn main() { + build_helpers::cfg_aliases_setup(); +} diff --git a/winit/src/application/drag_resize.rs b/winit/src/application/drag_resize.rs index 6d575b52..04979db7 100644 --- a/winit/src/application/drag_resize.rs +++ b/winit/src/application/drag_resize.rs @@ -1,3 +1,5 @@ +use crate::conversion; +use crate::core::mouse; use cursor_icon::CursorIcon; use winit::window::ResizeDirection; @@ -24,6 +26,9 @@ const DRAG_RESIZE_SUPPORTED: bool = true; const DRAG_RESIZE_SUPPORTED: bool = false; /// If supported by winit, returns a closure that implements cursor resize support. +/// +/// The closure takes the cursor last set by the UI, which it restores when +/// the pointer leaves the resize border. pub fn event_func( window: &dyn winit::window::Window, border_size: f64, @@ -32,6 +37,7 @@ pub fn event_func( dyn FnMut( &dyn winit::window::Window, &winit::event::WindowEvent, + mouse::Interaction, ) -> bool, >, > { @@ -41,7 +47,8 @@ pub fn event_func( Some(Box::new( move |window: &dyn winit::window::Window, - window_event: &winit::event::WindowEvent| + window_event: &winit::event::WindowEvent, + ui_interaction: mouse::Interaction| -> bool { // Keep track of border resize state and set cursor icon when in range match window_event { @@ -49,20 +56,30 @@ pub fn event_func( position, .. } => { - if !window.is_decorated() { - let location = cursor_resize_direction( + let location = if is_resizable(window) { + cursor_resize_direction( window.surface_size(), *position, border_size, - ); - if location != cursor_prev_resize_direction { - window.set_cursor( - resize_direction_cursor_icon(location) + ) + } else { + None + }; + if location != cursor_prev_resize_direction { + match location { + Some(direction) => window.set_cursor( + resize_direction_cursor_icon(direction) .into(), - ); - cursor_prev_resize_direction = location; - return true; + ), + None => restore_cursor(window, ui_interaction), } + cursor_prev_resize_direction = location; + return true; + } + } + winit::event::WindowEvent::PointerLeft { .. } => { + if cursor_prev_resize_direction.take().is_some() { + restore_cursor(window, ui_interaction); } } winit::event::WindowEvent::PointerButton { @@ -77,7 +94,9 @@ pub fn event_func( primary: true, .. } => { - if let Some(direction) = cursor_prev_resize_direction { + if let Some(direction) = cursor_prev_resize_direction + && is_resizable(window) + { let _res = window.drag_resize_window(direction); return true; } @@ -93,22 +112,37 @@ pub fn event_func( } } +/// Whether the window can currently be resized from its border. +fn is_resizable(window: &dyn winit::window::Window) -> bool { + !window.is_decorated() + && !window.is_maximized() + && window.fullscreen().is_none() +} + +/// Set the cursor back to the one the UI last set. +fn restore_cursor( + window: &dyn winit::window::Window, + ui_interaction: mouse::Interaction, +) { + // `None` means the UI hid the cursor, which a cursor icon doesn't undo. + if let Some(icon) = conversion::mouse_interaction(ui_interaction) { + window.set_cursor(icon.into()); + } +} + /// Get the cursor icon that corresponds to the resize direction. fn resize_direction_cursor_icon( - resize_direction: Option, + resize_direction: ResizeDirection, ) -> CursorIcon { match resize_direction { - Some(resize_direction) => match resize_direction { - ResizeDirection::East => CursorIcon::EResize, - ResizeDirection::North => CursorIcon::NResize, - ResizeDirection::NorthEast => CursorIcon::NeResize, - ResizeDirection::NorthWest => CursorIcon::NwResize, - ResizeDirection::South => CursorIcon::SResize, - ResizeDirection::SouthEast => CursorIcon::SeResize, - ResizeDirection::SouthWest => CursorIcon::SwResize, - ResizeDirection::West => CursorIcon::WResize, - }, - None => CursorIcon::Default, + ResizeDirection::East => CursorIcon::EResize, + ResizeDirection::North => CursorIcon::NResize, + ResizeDirection::NorthEast => CursorIcon::NeResize, + ResizeDirection::NorthWest => CursorIcon::NwResize, + ResizeDirection::South => CursorIcon::SResize, + ResizeDirection::SouthEast => CursorIcon::SeResize, + ResizeDirection::SouthWest => CursorIcon::SwResize, + ResizeDirection::West => CursorIcon::WResize, } } diff --git a/winit/src/clipboard.rs b/winit/src/clipboard.rs index d302ac36..2c9e4a32 100644 --- a/winit/src/clipboard.rs +++ b/winit/src/clipboard.rs @@ -92,7 +92,7 @@ impl Clipboard { }) .unwrap_or(State::Unavailable); - #[cfg(target_os = "linux")] + #[cfg(wayland_platform)] if let State::Connected { clipboard, .. } = &state { clipboard.init_dnd(Box::new(sender)); } diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index cc5676b5..e10c633d 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -23,6 +23,33 @@ use winit::event::Force; use winit::icon::IconProvider; use winit::keyboard::SmolStr; +/// Returns the activation token provided by the compositor, if any. +/// +/// The token is single-use, so it is only ever handed out to the first window. +#[cfg(target_os = "linux")] +fn take_activation_token() -> Option { + use std::sync::Mutex; + use std::sync::OnceLock; + use winit::platform::startup_notify::reset_activation_token_env; + + static TOKEN: OnceLock>> = + OnceLock::new(); + + TOKEN + .get_or_init(|| { + let token = std::env::var("XDG_ACTIVATION_TOKEN") + .ok() + .map(winit::window::ActivationToken::from_raw); + + reset_activation_token_env(); + + Mutex::new(token) + }) + .lock() + .ok() + .and_then(|mut token| token.take()) +} + /// Converts some [`window::Settings`] into some `WindowAttributes` from `winit`. pub fn window_attributes( settings: window::Settings, @@ -182,12 +209,19 @@ pub fn window_attributes( { use winit::platform::wayland::WindowAttributesWayland; - attributes = attributes.with_platform_attributes(Box::new( - WindowAttributesWayland::default().with_name( + let mut wayland_attributes = WindowAttributesWayland::default() + .with_name( &settings.platform_specific.application_id, &settings.platform_specific.application_id, - ), - )); + ); + + if let Some(token) = take_activation_token() { + wayland_attributes = + wayland_attributes.with_activation_token(token); + } + + attributes = attributes + .with_platform_attributes(Box::new(wayland_attributes)); } } @@ -235,6 +269,13 @@ pub fn window_event( scale_factor, ))) } + winit::event::PointerSource::TabletTool { .. } => { + let position = position.to_logical::(scale_factor); + + Some(Event::Mouse(mouse::Event::CursorMoved { + position: Point::new(position.x as f32, position.y as f32), + })) + } _ => None, }, // TODO how to handle this for Touch? Is it different than a press? Need to double check winit impl @@ -272,6 +313,24 @@ pub fn window_event( } })) } + WindowEvent::PointerButton { + button: ButtonSource::TabletTool { button, .. }, + state, + .. + } => { + let button: Option = button.into(); + + button.map(mouse_button).map(|button| { + Event::Mouse(match state { + winit::event::ElementState::Pressed => { + mouse::Event::ButtonPressed(button) + } + winit::event::ElementState::Released => { + mouse::Event::ButtonReleased(button) + } + }) + }) + } WindowEvent::PointerButton { button: ButtonSource::Touch { finger_id, force }, state, @@ -387,19 +446,19 @@ pub fn window_event( self::modifiers(new_modifiers.state()), ))) } - WindowEvent::Ime(event) => Some(Event::InputMethod(match event { - Ime::Enabled => input_method::Event::Opened, - Ime::Preedit(content, size) => input_method::Event::Preedit( + WindowEvent::Ime(event) => match event { + Ime::Enabled => Some(input_method::Event::Opened), + Ime::Preedit(content, size) => Some(input_method::Event::Preedit( content, size.map(|(start, end)| start..end), - ), - Ime::Commit(content) => input_method::Event::Commit(content), - Ime::Disabled => input_method::Event::Closed, - Ime::DeleteSurrounding { - before_bytes, - after_bytes, - } => todo!(), - })), + )), + Ime::Commit(content) => Some(input_method::Event::Commit(content)), + Ime::Disabled => Some(input_method::Event::Closed), + // Upstream iced depends on the winit version which doesn't support + // this kind of ime event. + Ime::DeleteSurrounding { .. } => None, + } + .map(Event::InputMethod), WindowEvent::Focused(focused) => Some(Event::Window(if focused { window::Event::Focused } else { @@ -424,7 +483,7 @@ pub fn window_event( WindowEvent::ScaleFactorChanged { scale_factor, .. } => { Some(Event::Window(window::Event::Rescaled(scale_factor as f32))) } - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] WindowEvent::SuggestedBounds(bounds) => { let size = bounds.map(|bounds| { let size = bounds.to_logical(scale_factor); @@ -437,7 +496,7 @@ pub fn window_event( ), ))) } - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] WindowEvent::WindowStateChanged => { use cctk::sctk::reexports::csd_frame::WindowState; use winit::platform::wayland::WindowExtWayland; @@ -1491,7 +1550,7 @@ pub fn winit_key_code( }) } -#[cfg(all(feature = "cctk", target_os = "linux"))] +#[cfg(wayland_platform)] fn winit_native_key_code( keycode: keyboard::key::NativeCode, ) -> winit::keyboard::NativeKeyCode { @@ -1515,7 +1574,7 @@ fn winit_native_key_code( } /// Reconstruct the raw keycode -#[cfg(all(feature = "cctk", target_os = "linux"))] +#[cfg(wayland_platform)] pub fn physical_to_scancode(physical: keyboard::key::Physical) -> Option { let Some(physical_key) = (match physical { keyboard::key::Physical::Code(code) => { diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 735d73aa..bf20100c 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -40,12 +40,6 @@ pub mod clipboard; pub mod conversion; pub mod platform_specific; -#[cfg(feature = "program")] -pub mod program; - -#[cfg(feature = "system")] -pub mod system; - mod error; mod proxy; mod window; @@ -53,7 +47,6 @@ mod window; pub use clipboard::Clipboard; pub use error::Error; pub use proxy::Proxy; -use winit::dpi::LogicalSize; use winit::dpi::PhysicalPosition; use winit::dpi::PhysicalSize; @@ -76,6 +69,8 @@ use crate::runtime::user_interface::{self, UserInterface}; use crate::runtime::{Action, Task}; use program::Program; +#[cfg(feature = "program")] +pub use program::Program; use window::WindowManager; use rustc_hash::FxHashMap; @@ -98,10 +93,10 @@ where let event_loop = EventLoop::new().expect("Create event loop"); - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] let is_wayland = winit::platform::wayland::EventLoopExtWayland::is_wayland(&event_loop); - #[cfg(not(all(feature = "cctk", target_os = "linux")))] + #[cfg(not(wayland_platform))] let is_wayland = false; // TODO this is new.. @@ -192,11 +187,15 @@ where receiver: mpsc::UnboundedReceiver, error: Option, system_theme: Option>, - control_sender: mpsc::UnboundedSender, - #[cfg(feature = "a11y")] - adapters: std::collections::HashMap, - + control_sender: mpsc::UnboundedSender, + + #[cfg(feature = "a11y")] + adapters: std::collections::HashMap< + window::Id, + (u64, iced_accessibility::accesskit_winit::Adapter), + >, + #[cfg(target_arch = "wasm32")] is_booted: std::rc::Rc>, #[cfg(target_arch = "wasm32")] @@ -215,6 +214,7 @@ where id: settings.id, sender: event_sender, receiver: control_receiver, + #[cfg(feature = "a11y")] control_sender: control_sender.clone(), error: None, system_theme: Some(system_theme_sender), @@ -263,7 +263,7 @@ where | winit::event::WindowEvent::Moved(_) ); - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] { if matches!(event, WindowEvent::RedrawRequested) { for id in @@ -501,9 +501,15 @@ where } }; } - let window: Arc = Arc::from(window); + let window: Arc< + dyn winit::window::Window + 'static, + > = Arc::from(window); #[cfg(feature = "a11y")] - self.init_adapter(event_loop, id, window.clone()); + self.init_adapter( + event_loop, + id, + window.clone(), + ); self.process_event( event_loop, @@ -572,11 +578,10 @@ where .expect("Send event"); } Control::Winit(id, e) => { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] { if matches!(e, WindowEvent::RedrawRequested) - { - + { for id in crate::subsurface_widget::subsurface_ids(id) { _ = self.sender .unbounded_send(Event::Winit( @@ -597,7 +602,7 @@ where } } } - + self.sender .start_send(Event::Winit(id, e)) .expect("Send event"); @@ -610,7 +615,7 @@ where #[cfg(feature = "a11y")] Control::InitAdapter(id, window) => { self.init_adapter(event_loop, id, window); - + self.process_event( event_loop, Some(Event::A11yAdapter(id)), @@ -618,8 +623,8 @@ where } #[cfg(feature = "a11y")] Control::Cleanup(id) => { - _ = self.adapters.remove(&id); - }, + _ = self.adapters.remove(&id); + } }, _ => { break; @@ -633,34 +638,35 @@ where } } - #[cfg(feature = "a11y")] - fn init_adapter(&mut self, event_loop: &(dyn winit::event_loop::ActiveEventLoop + 'static), id: core::window::Id, window: Arc) { + #[cfg(feature = "a11y")] + fn init_adapter( + &mut self, + event_loop: &(dyn winit::event_loop::ActiveEventLoop + 'static), + id: core::window::Id, + window: Arc, + ) { use crate::a11y::*; use iced_accessibility::accesskit::{ - ActivationHandler, Node, NodeId, Role, - Tree, TreeUpdate, + ActivationHandler, Node, NodeId, Role, Tree, TreeUpdate, }; use iced_accessibility::accesskit_winit::Adapter; - - let node_id = - iced_runtime::core::id::window_node_id(); - - let activation_handler = - WinitActivationHandler { - proxy: self.control_sender.clone(), - title: String::new(), - }; - + + let node_id = iced_runtime::core::id::window_node_id(); + + let activation_handler = WinitActivationHandler { + proxy: self.control_sender.clone(), + title: String::new(), + }; + let action_handler = WinitActionHandler { id, proxy: self.control_sender.clone(), }; - - let deactivation_handler = - WinitDeactivationHandler { - proxy: self.control_sender.clone(), - }; - + + let deactivation_handler = WinitDeactivationHandler { + proxy: self.control_sender.clone(), + }; + _ = self.adapters.insert( id, ( @@ -674,8 +680,6 @@ where ), ), ); - - } } @@ -770,7 +774,7 @@ async fn run_instance

( let mut platform_specific_handler = crate::platform_specific::PlatformSpecific::default(); - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] if is_wayland { platform_specific_handler = platform_specific_handler.with_wayland( control_sender.clone(), @@ -838,6 +842,30 @@ async fn run_instance

( log::info!("System theme: {system_theme:?}"); + macro_rules! run_action { + ($action:expr) => { + run_action( + $action, + &program, + &mut runtime, + &mut compositor, + &mut events, + &mut messages, + &mut clipboard, + &mut control_sender, + &mut user_interfaces, + &mut window_manager, + &mut ui_caches, + &mut is_window_opening, + &mut system_theme, + &mut platform_specific_handler, + &mut dnd_surface, + &mut dnd_surface_id, + is_daemon, + ) + }; + } + 'next_event: loop { // Empty the queue if possible let event = if let Ok(event) = event_receiver.try_recv() { @@ -857,35 +885,36 @@ async fn run_instance

( exit_on_close_request, make_visible, on_open, - resize_border + resize_border, } => { - - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] platform_specific_handler.send_wayland( platform_specific::Action::TrackWindow(window.clone(), id), ); - match create_compositor::

( - window.clone(), - CreateCompositor { - proxy: &proxy, - runtime: &mut runtime, - display_handle: &display_handle, - graphics_settings: &graphics_settings, - default_fonts: &default_fonts, - }, - ) - .await - { - Ok(comp) => { - compositor = Some(comp); - } - Err(error) => { - control_sender - .start_send(Control::Crash( - Error::GraphicsCreationFailed(error), - )) - .expect("Send control message"); - continue; + if compositor.is_none() { + match create_compositor::

( + window.clone(), + CreateCompositor { + proxy: &proxy, + runtime: &mut runtime, + display_handle: &display_handle, + graphics_settings: &graphics_settings, + default_fonts: &default_fonts, + }, + ) + .await + { + Ok(comp) => { + compositor = Some(comp); + } + Err(error) => { + control_sender + .start_send(Control::Crash( + Error::GraphicsCreationFailed(error), + )) + .expect("Send control message"); + continue; + } } } @@ -913,7 +942,7 @@ async fn run_instance

( compositor, exit_on_close_request, system_theme, - resize_border + resize_border, ); window.raw.set_theme(conversion::window_theme( @@ -999,22 +1028,7 @@ async fn run_instance

( } } Event::UserEvent(action) => { - let exited = run_action( - action, - &program, - &mut runtime, - &mut compositor, - &mut events, - &mut messages, - &mut clipboard, - &mut control_sender, - &mut user_interfaces, - &mut window_manager, - &mut ui_caches, - &mut is_window_opening, - &mut system_theme, - &mut platform_specific_handler, - ); + let exited = run_action!(action); if exited { runtime.track(None.into_iter()); } @@ -1025,7 +1039,7 @@ async fn run_instance

( continue; }; - let Some((id, mut window)) = + let Some((id, mut window)) = window_manager.get_mut_alias(window_id) else { continue; @@ -1040,12 +1054,12 @@ async fn run_instance

( )) .expect("Send redraw event"); continue; - } + } // XX must force update to corner radius before the surface is committed. - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] if (window.surface_version != window.state.surface_version() - || window.logical_size() != window.state.logical_size() - ) && !crate::subsurface_widget::is_subsurface(window_id) + || window.logical_size() != window.state.logical_size()) + && !crate::subsurface_widget::is_subsurface(window_id) { platform_specific_handler.send_wayland( platform_specific::Action::ResizeWindow(id), @@ -1156,22 +1170,7 @@ async fn run_instance

( continue; } - _ = run_action( - action, - &program, - &mut runtime, - &mut compositor, - &mut events, - &mut messages, - &mut clipboard, - &mut control_sender, - &mut user_interfaces, - &mut window_manager, - &mut ui_caches, - &mut is_window_opening, - &mut system_theme, - &mut platform_specific_handler, - ); + _ = run_action!(action); } for (window_id, window) in window_manager.iter_mut() { @@ -1326,13 +1325,26 @@ async fn run_instance

( continue; }; // Initiates a drag resize window state when found. - if let Some(func) = - window.drag_resize_window_func.as_mut() - { - if func(window.raw.as_ref(), &event) { + if let Some(func) = window.drag_resize_window_func.as_mut() { + if func( + window.raw.as_ref(), + &event, + window.mouse_interaction, + ) { continue; } } + // to avoid race with the reported modifier state from the popup of + // this window, don't empty the modifiers, on focus loss + #[cfg(wayland_platform)] + if let winit::event::WindowEvent::ModifiersChanged(modifiers) = + &event + && modifiers.state().is_empty() + && platform_specific_handler.has_popup(id) + { + continue; + } + match event { winit::event::WindowEvent::SurfaceResized(_) => { window.raw.request_redraw(); @@ -1354,22 +1366,9 @@ async fn run_instance

( if matches!(event, winit::event::WindowEvent::CloseRequested) && window.exit_on_close_request { - _ = run_action( - Action::Window(runtime::window::Action::Close(id)), - &program, - &mut runtime, - &mut compositor, - &mut events, - &mut messages, - &mut clipboard, - &mut control_sender, - &mut user_interfaces, - &mut window_manager, - &mut ui_caches, - &mut is_window_opening, - &mut system_theme, - &mut platform_specific_handler, - ); + _ = run_action!(Action::Window( + runtime::window::Action::Close(id) + )); } else { window.state.update(&program, window.raw.as_ref(), &event); @@ -1411,8 +1410,7 @@ async fn run_instance

( true } }); - let no_window_events = window_events.is_empty(); - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] window_events.push(core::Event::PlatformSpecific( core::event::PlatformSpecific::Wayland( core::event::wayland::Event::RequestResize, @@ -1429,8 +1427,7 @@ async fn run_instance

( &mut clipboard, &mut messages, ); - let mut needs_redraw = - !no_window_events || !messages.is_empty(); + let mut needs_redraw = !messages.is_empty(); if let Some(requested_size) = clipboard.requested_logical_size.lock().unwrap().take() { @@ -1486,8 +1483,17 @@ async fn run_instance

( mouse_interaction, .. } => { + let mouse_changed = + window.mouse_interaction != mouse_interaction; + window.update_mouse(mouse_interaction); + if mouse_changed { + window.request_redraw( + core::window::RedrawRequest::NextFrame, + ); + } + #[cfg(not(feature = "unconditional-rendering"))] window.request_redraw(_redraw_request); } @@ -1547,22 +1553,7 @@ async fn run_instance

( )); for action in actions { - let exited = run_action( - action, - &program, - &mut runtime, - &mut compositor, - &mut events, - &mut messages, - &mut clipboard, - &mut control_sender, - &mut user_interfaces, - &mut window_manager, - &mut ui_caches, - &mut is_window_opening, - &mut system_theme, - &mut platform_specific_handler, - ); + let exited = run_action!(action); if exited { runtime.track(None.into_iter()); } @@ -1772,7 +1763,6 @@ async fn run_instance

( }; // search windows for widget with operation - if result.is_none() { log::warn!( "start_dnd: widget {:?} not found; drag will fail", @@ -2106,6 +2096,11 @@ fn run_action<'a, P, C>( is_window_opening: &mut bool, system_theme: &mut theme::Mode, platform_specific: &mut crate::platform_specific::PlatformSpecific, + dnd_surface: &mut Option< + Arc>, + >, + dnd_surface_id: &mut Option, + is_daemon: bool, ) -> bool where P: Program, @@ -2148,7 +2143,7 @@ where on_open: channel, }) .expect("Send control action"); - + *is_window_opening = true; } window::Action::Close(id) => { @@ -2159,7 +2154,7 @@ where _ = control_sender.start_send(Control::Cleanup(id)).ok(); } let proxy = clipboard.proxy(); - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] platform_specific .send_wayland(platform_specific::Action::RemoveWindow(id)); if let Some(window) = window_manager.remove(id) { @@ -2190,7 +2185,7 @@ where )); } - if window_manager.is_empty() { + if window_manager.is_empty() && !is_daemon { *compositor = None; } } @@ -2543,6 +2538,8 @@ where while let Some(mut operation) = current_operation.take() { for (id, ui) in interfaces.iter_mut() { if let Some(window) = window_manager.get_mut(*id) { + operation.as_mut().set_window_id(*id); + ui.operate(&window.renderer, operation.as_mut()); } } @@ -2620,6 +2617,10 @@ where } iced_runtime::dnd::DndAction::EndDnd => { clipboard.end_dnd(); + *dnd_surface = None; + if let Some(id) = dnd_surface_id.take() { + platform_specific.retain_subsurfaces(|other| other != id); + } } iced_runtime::dnd::DndAction::PeekDnd(m, channel) => { let data = clipboard.peek_dnd(m); @@ -2657,10 +2658,10 @@ where .and_then(|window| theme::Base::palette(window.state.theme())) }); - cached_user_interfaces - .drain() - .filter_map(|(id, cache)| { - let window = window_manager.get_mut(id)?; + window_manager + .iter_mut() + .filter_map(|(id, window)| { + let cache = cached_user_interfaces.remove(&id)?; Some(( id, @@ -2733,7 +2734,5 @@ fn system_information( graphics_backend: graphics.backend, } } -#[cfg(feature = "program")] -pub use program::Program; pub use platform_specific::*; diff --git a/winit/src/platform_specific/mod.rs b/winit/src/platform_specific/mod.rs index 9b74ee9c..8695a892 100644 --- a/winit/src/platform_specific/mod.rs +++ b/winit/src/platform_specific/mod.rs @@ -1,24 +1,20 @@ //! Wayland specific shell //! -use std::{borrow::Cow, collections::HashMap, sync::Arc}; +use std::collections::HashMap; -#[cfg(all(feature = "cctk", target_os = "linux"))] -use cctk::sctk::reexports::client::Connection; -use iced_graphics::{Compositor, compositor}; +use iced_graphics::compositor; use iced_runtime::{ core::{Vector, window}, - platform_specific, user_interface, + user_interface, }; use winit::raw_window_handle::HasWindowHandle; -#[cfg(all(feature = "cctk", target_os = "linux"))] +#[cfg(wayland_platform)] pub mod wayland; -#[cfg(all(feature = "cctk", target_os = "linux"))] +#[cfg(wayland_platform)] pub use wayland::*; -#[cfg(all(feature = "cctk", target_os = "linux"))] -use wayland_backend::client::Backend; use crate::{CreateCompositor, Program, WindowManager}; @@ -35,7 +31,7 @@ pub type UserInterfaces<'a, P> = HashMap< #[derive(Debug)] pub enum Event { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] Wayland(sctk_event::SctkEvent), } @@ -61,7 +57,7 @@ impl SurfaceIdWrapper { #[derive(Debug, Default)] pub struct PlatformSpecific { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] wayland: WaylandSpecific, } @@ -71,25 +67,30 @@ impl PlatformSpecific { action: iced_runtime::platform_specific::Action, ) { match action { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] iced_runtime::platform_specific::Action::Wayland(a) => { self.send_wayland(wayland::Action::Action(a)); } } } + #[cfg(wayland_platform)] + pub(crate) fn has_popup(&self, toplevel: window::Id) -> bool { + self.wayland.has_popup(toplevel) + } + pub(crate) fn retain_subsurfaces bool>( &mut self, keep: F, ) { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] { self.wayland.retain_subsurfaces(keep); } } pub(crate) fn clear_subsurface_list(&mut self) { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] { self.wayland.clear_subsurface_list(); } @@ -100,7 +101,7 @@ impl PlatformSpecific { id: window::Id, window: &dyn HasWindowHandle, ) { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] { use cctk::sctk::reexports::client::{ Proxy, protocol::wl_surface::WlSurface, @@ -151,11 +152,14 @@ impl PlatformSpecific { pub(crate) fn create_surface( &mut self, ) -> Option> { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] { - return self.wayland.create_surface(); + self.wayland.create_surface() + } + #[cfg(not(wayland_platform))] + { + None } - None } pub(crate) fn update_surface_shm( @@ -167,7 +171,7 @@ impl PlatformSpecific { data: &[u8], offset: Vector, ) { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] { return self.wayland.update_surface_shm( surface, width, height, scale, data, offset, @@ -195,7 +199,7 @@ pub(crate) async fn handle_event<'a, 'b, P>( P: Program, { match e { - #[cfg(all(feature = "cctk", target_os = "linux"))] + #[cfg(wayland_platform)] Event::Wayland(e) => { platform_specific .wayland diff --git a/winit/src/platform_specific/wayland/commands/activation.rs b/winit/src/platform_specific/wayland/commands/activation.rs index fa051031..442ee08c 100644 --- a/winit/src/platform_specific/wayland/commands/activation.rs +++ b/winit/src/platform_specific/wayland/commands/activation.rs @@ -1,8 +1,8 @@ use crate::core::window::Id as SurfaceId; use iced_runtime::{ - self, + self, Action, Task, platform_specific::{self, wayland}, - task, Action, Task, + task, }; pub fn request_token( diff --git a/winit/src/platform_specific/wayland/commands/blur.rs b/winit/src/platform_specific/wayland/commands/blur.rs new file mode 100644 index 00000000..e9a73646 --- /dev/null +++ b/winit/src/platform_specific/wayland/commands/blur.rs @@ -0,0 +1,17 @@ +use iced_futures::core::window; +use iced_runtime::{ + self, Action, Task, + platform_specific::{self, wayland}, + task, +}; + +pub fn blur( + id: window::Id, + blur: Option>, +) -> Task<()> { + task::oneshot(|_| { + Action::PlatformSpecific(platform_specific::Action::Wayland( + wayland::Action::BlurSurface(id, blur), + )) + }) +} diff --git a/winit/src/platform_specific/wayland/commands/corner_radius.rs b/winit/src/platform_specific/wayland/commands/corner_radius.rs index c5ba6016..4cc122dd 100644 --- a/winit/src/platform_specific/wayland/commands/corner_radius.rs +++ b/winit/src/platform_specific/wayland/commands/corner_radius.rs @@ -1,11 +1,11 @@ use iced_futures::core::window; use iced_runtime::{ - self, + self, Action, Task, platform_specific::{ self, wayland::{self, CornerRadius}, }, - task, Action, Task, + task, }; pub fn corner_radius( diff --git a/winit/src/platform_specific/wayland/commands/keyboard_shortcuts_inhibit.rs b/winit/src/platform_specific/wayland/commands/keyboard_shortcuts_inhibit.rs index 783b62c6..afc2e27b 100644 --- a/winit/src/platform_specific/wayland/commands/keyboard_shortcuts_inhibit.rs +++ b/winit/src/platform_specific/wayland/commands/keyboard_shortcuts_inhibit.rs @@ -1,7 +1,7 @@ use iced_runtime::{ - self, + self, Action, Task, platform_specific::{self, wayland}, - task, Action, Task, + task, }; pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> { diff --git a/winit/src/platform_specific/wayland/commands/layer_surface.rs b/winit/src/platform_specific/wayland/commands/layer_surface.rs index 839db95c..e7bbb3b5 100644 --- a/winit/src/platform_specific/wayland/commands/layer_surface.rs +++ b/winit/src/platform_specific/wayland/commands/layer_surface.rs @@ -1,8 +1,9 @@ //! Interact with the window of your application. +use crate::core::Rectangle; use crate::core::window::Id as SurfaceId; use iced_runtime::{ - self, + self, Action, Task, platform_specific::{ self, wayland::{ @@ -10,7 +11,7 @@ use iced_runtime::{ layer_surface::{IcedMargin, SctkLayerSurfaceSettings}, }, }, - task, Action, Task, + task, }; pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer}; @@ -114,3 +115,37 @@ pub fn set_layer(id: SurfaceId, layer: Layer) -> Task { )), )) } + +pub fn set_padding( + id: SurfaceId, + padding: IcedMargin, +) -> Task { + task::effect(Action::PlatformSpecific( + platform_specific::Action::Wayland(wayland::Action::LayerSurface( + wayland::layer_surface::Action::Padding { id, padding }, + )), + )) +} + +/// +pub fn set_input_zone( + id: SurfaceId, + zone: Option>, +) -> Task { + task::effect(Action::PlatformSpecific( + platform_specific::Action::Wayland(wayland::Action::LayerSurface( + wayland::layer_surface::Action::InputZone { id, zone }, + )), + )) +} + +pub fn set_show_on_lock( + id: SurfaceId, + value: bool, +) -> Task { + task::effect(Action::PlatformSpecific( + platform_specific::Action::Wayland(wayland::Action::LayerSurface( + wayland::layer_surface::Action::ShowOnLock { id, value }, + )), + )) +} diff --git a/winit/src/platform_specific/wayland/commands/mod.rs b/winit/src/platform_specific/wayland/commands/mod.rs index 191dc3a5..cd6832c7 100644 --- a/winit/src/platform_specific/wayland/commands/mod.rs +++ b/winit/src/platform_specific/wayland/commands/mod.rs @@ -1,6 +1,7 @@ //! Interact with the wayland objects of your application. pub mod activation; +pub mod blur; pub mod corner_radius; pub mod keyboard_shortcuts_inhibit; pub mod layer_surface; diff --git a/winit/src/platform_specific/wayland/commands/overlap_notify.rs b/winit/src/platform_specific/wayland/commands/overlap_notify.rs index 860838c3..417cfb24 100644 --- a/winit/src/platform_specific/wayland/commands/overlap_notify.rs +++ b/winit/src/platform_specific/wayland/commands/overlap_notify.rs @@ -1,7 +1,8 @@ use iced_futures::core::window::Id; use iced_runtime::{ + Action, Task, platform_specific::{self, wayland}, - task, Action, Task, + task, }; /// Request subscription for overlap notification events on the surface diff --git a/winit/src/platform_specific/wayland/commands/popup.rs b/winit/src/platform_specific/wayland/commands/popup.rs index f70d97f4..c702f4f1 100644 --- a/winit/src/platform_specific/wayland/commands/popup.rs +++ b/winit/src/platform_specific/wayland/commands/popup.rs @@ -1,12 +1,15 @@ //! Interact with the popups of your application. use crate::core::window::Id as SurfaceId; use iced_runtime::{ - self, + self, Action, Task, platform_specific::{ self, - wayland::{self, popup::SctkPopupSettings}, + wayland::{ + self, + popup::{SctkPopupSettings, SctkPositioner}, + }, }, - task, Action, Task, + task, }; /// @@ -32,6 +35,18 @@ pub fn set_size( )) } +/// +pub fn reposition( + id: SurfaceId, + positioner: SctkPositioner, +) -> Task { + task::effect(Action::PlatformSpecific( + platform_specific::Action::Wayland(wayland::Action::Popup( + wayland::popup::Action::Reposition { id, positioner }, + )), + )) +} + /// pub fn destroy_popup(id: SurfaceId) -> Task { task::effect(Action::PlatformSpecific( diff --git a/winit/src/platform_specific/wayland/commands/session_lock.rs b/winit/src/platform_specific/wayland/commands/session_lock.rs index 7f8748f7..a530c12e 100644 --- a/winit/src/platform_specific/wayland/commands/session_lock.rs +++ b/winit/src/platform_specific/wayland/commands/session_lock.rs @@ -1,9 +1,9 @@ use crate::core::window::Id as SurfaceId; use cctk::sctk::reexports::client::protocol::wl_output::WlOutput; use iced_runtime::{ - self, + self, Action, Task, platform_specific::{self, wayland}, - task, Action, Task, + task, }; pub fn lock() -> Task { diff --git a/winit/src/platform_specific/wayland/commands/subsurface.rs b/winit/src/platform_specific/wayland/commands/subsurface.rs index a76e3b4e..20c3cdb7 100644 --- a/winit/src/platform_specific/wayland/commands/subsurface.rs +++ b/winit/src/platform_specific/wayland/commands/subsurface.rs @@ -1,12 +1,12 @@ use crate::core::window::Id as SurfaceId; pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer}; use iced_runtime::{ - self, + self, Action, Task, platform_specific::{ self, wayland::{self, subsurface::SctkSubsurfaceSettings}, }, - task, Action, Task, + task, }; pub fn get_subsurface( diff --git a/winit/src/platform_specific/wayland/event_loop/mod.rs b/winit/src/platform_specific/wayland/event_loop/mod.rs index 976595a7..01bd7514 100644 --- a/winit/src/platform_specific/wayland/event_loop/mod.rs +++ b/winit/src/platform_specific/wayland/event_loop/mod.rs @@ -1,4 +1,5 @@ pub mod control_flow; +pub mod popup; pub mod proxy; pub mod state; @@ -6,6 +7,7 @@ pub mod state; use crate::platform_specific::SurfaceIdWrapper; use crate::{ Control, + event_loop::popup::PopupManager, futures::futures::channel::mpsc, handlers::{ ext_background_effect, overlap::OverlapNotifyV1, @@ -33,7 +35,7 @@ use cctk::{ reexports::{ calloop::{self, EventLoop}, client::{ - ConnectError, Connection, Proxy, globals::registry_queue_init, + Connection, Proxy, globals::registry_queue_init, }, }, registry::RegistryState, @@ -46,8 +48,6 @@ use cctk::{ }; use raw_window_handle::HasDisplayHandle; use state::{FrameStatus, SctkWindow, send_event}; -#[cfg(feature = "a11y")] -use std::sync::{Arc, Mutex}; use std::{ collections::{HashMap, HashSet}, fmt::Debug, @@ -71,8 +71,8 @@ pub struct SctkEventLoop { pub(crate) state: SctkState, } +#[derive(Debug)] pub enum Error { - Connect(ConnectError), Calloop(calloop::Error), Global(GlobalError), NoDisplayHandle, @@ -129,7 +129,7 @@ impl SctkEventLoop { crate::platform_specific::Action::ResizeWindow(id) => { if let Some((_, v)) = state.windows.iter() .find(|w| w.id == id) - .map(|w| w.corner_radius.as_ref()) + .map(|w| state.corner_radii.get(&id)) .unwrap_or_default() { _ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, *v)); } @@ -138,7 +138,7 @@ impl SctkEventLoop { window, id, ) => { - state.windows.push(SctkWindow { window, id, corner_radius: Default::default() }); + state.windows.push(SctkWindow { window, id }); if let Some(v) = state.pending_corner_radius.remove(&id) { _ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, Some(v))); } @@ -179,13 +179,41 @@ impl SctkEventLoop { } } crate::platform_specific::Action::SetCursor( + surface, icon, ) => { - if let Some(seat) = state.seats.get_mut(0) { + state.record_cursor(surface.clone(), |r| { + r.icon = Some(icon); + r.hidden = false; + }); + if let Some(seat) = state + .seats + .get_mut(0) + .filter(|s| s.is_over(&surface)) + { seat.icon = Some(icon); + seat.hidden = false; seat.set_cursor(&state.connection, icon); } } + crate::platform_specific::Action::SetCursorVisible( + surface, + visible, + ) => { + state.record_cursor(surface.clone(), |r| { + r.hidden = !visible; + }); + if let Some(seat) = state + .seats + .get_mut(0) + .filter(|s| s.is_over(&surface)) + { + seat.set_cursor_visible( + &state.connection, + visible, + ); + } + } crate::platform_specific::Action::RequestRedraw( id, ) => { @@ -350,7 +378,7 @@ impl SctkEventLoop { ), corner_radius_manager: registry_state.bind_one::( &qh, - 1..=1, + 1..=2, (), ).ok(), toplevel_manager: ToplevelManagerState::try_new( @@ -364,6 +392,7 @@ impl SctkEventLoop { ).ok(), text_input_manager: TextInputManager::try_new(®istry_state, &qh), ext_background_effect_manager: ext_background_effect::ExtBackgroundEffectManager::new(&globals, &qh).ok(), + cosmic_session_lock_layer_manager: registry_state.bind_one(&qh, 1..=1, ()).ok(), registry_state, queue_handle: qh, @@ -377,13 +406,16 @@ impl SctkEventLoop { seats: Vec::new(), windows: Vec::new(), blur_surfaces: HashMap::new(), + corner_radii: HashMap::new(), layer_surfaces: Vec::new(), - popups: Vec::new(), + popmgr: PopupManager::default(), lock_surfaces: Vec::new(), subsurfaces: Vec::new(), touch_points: HashMap::new(), + kbd_leave_to_own_popup: HashSet::new(), sctk_events: Vec::new(), frame_status: HashMap::new(), + cursor_requests: HashMap::new(), fractional_scaling_manager, viewporter_state, compositor_updates: Default::default(), @@ -402,6 +434,7 @@ impl SctkEventLoop { preedit: None, pending_delete: None, pending_commit: None, + pending_blur: HashMap::new(), }, _features: Default::default(), }; @@ -488,6 +521,23 @@ impl SctkEventLoop { let had_events = !state.state.sctk_events.is_empty(); let mut wake_up = had_events; + for e in state.state.sctk_events.drain(..) { + if let SctkEvent::Winit(id, e) = e { + _ = state + .state + .events_sender + .unbounded_send(Control::Winit(id, e)); + } else { + _ = + state + .state + .events_sender + .unbounded_send(Control::PlatformSpecific( + crate::platform_specific::Event::Wayland(e), + )); + } + } + for s in state .state @@ -497,8 +547,8 @@ impl SctkEventLoop { .chain( state .state - .popups - .iter() + .popmgr + .popups() .map(|s| s.popup.wl_surface()), ) .chain( @@ -532,22 +582,6 @@ impl SctkEventLoop { ); } - for e in state.state.sctk_events.drain(..) { - if let SctkEvent::Winit(id, e) = e { - _ = state - .state - .events_sender - .unbounded_send(Control::Winit(id, e)); - } else { - _ = - state - .state - .events_sender - .unbounded_send(Control::PlatformSpecific( - crate::platform_specific::Event::Wayland(e), - )); - } - } if wake_up { state.state.proxy.wake_up(); } diff --git a/winit/src/platform_specific/wayland/event_loop/popup.rs b/winit/src/platform_specific/wayland/event_loop/popup.rs new file mode 100644 index 00000000..109e9af6 --- /dev/null +++ b/winit/src/platform_specific/wayland/event_loop/popup.rs @@ -0,0 +1,189 @@ +use std::sync::Mutex; +use std::sync::Arc; +use cctk::sctk::shell::xdg::popup::PopupConfigure; +use cctk::sctk::shell::xdg::popup::Popup; +use iced_runtime::{platform_specific, core::window::Id}; +use wayland_client::protocol::wl_surface::WlSurface; +use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; + +use crate::event_loop::state::Common; +use crate::event_loop::state::SctkPopupData; + +#[derive(Debug, Default)] +pub struct PopupManager { + chains: Vec>, +} + +impl PopupManager { + pub(crate) fn popups(&self) -> impl Iterator { + self.chains.iter().map(|c| c.iter()).flatten() + } + + pub(crate) fn chain_for_popup_mut( + &mut self, + id: &WlSurface, + ) -> Option<(&mut Vec, usize)> { + let pos = self.chains.iter().enumerate().find_map(move |(pos, c)| { + if let Some(pop_pos) = + c.into_iter().position(|p| p.popup.wl_surface() == id) + { + Some((pos, pop_pos)) + } else { + None + } + }); + + pos.map(|pos| (&mut self.chains[pos.0], pos.1)) + } + + pub(crate) fn chain_for_popup( + &self, + id: &WlSurface, + ) -> Option<(&Vec, usize)> { + let pos = self.chains.iter().enumerate().find_map(move |(pos, c)| { + if let Some(pop_pos) = + c.into_iter().position(|p| p.popup.wl_surface() == id) + { + Some((pos, pop_pos)) + } else { + None + } + }); + + pos.map(|pos| (&self.chains[pos.0], pos.1)) + } + + pub(crate) fn popup(&self, id: &WlSurface) -> Option<&SctkPopup> { + self.chains.iter().find_map(move |c| { + c.into_iter().find(|p| p.popup.wl_surface() == id) + }) + } + + pub(crate) fn popup_mut( + &mut self, + id: &WlSurface, + ) -> Option<&mut SctkPopup> { + self.chains.iter_mut().find_map(move |c| { + c.into_iter().find(|p| p.popup.wl_surface() == id) + }) + } + + pub(crate) fn popup_id(&self, id: Id) -> Option<&SctkPopup> { + self.chains + .iter() + .find_map(move |c| c.into_iter().find(|p| p.data.id == id)) + } + + pub(crate) fn popup_id_mut(&mut self, id: Id) -> Option<&mut SctkPopup> { + self.chains + .iter_mut() + .find_map(move |c| c.into_iter().find(|p| p.data.id == id)) + } + + pub(crate) fn active_grab(&self) -> Option<&SctkPopup> { + self.chains + .iter() + .find_map(|c| c.into_iter().rev().find(|p| p.data.grab)) + } + + pub(crate) fn root_grab(&self) -> Option<&SctkPopup> { + self.chains + .iter() + .find_map(|c| c.into_iter().find(|p| p.data.grab)) + } + + pub(crate) fn push(&mut self, popup: SctkPopup) { + if let Some((chain, _pos)) = + self.chain_for_popup_mut(popup.data.parent.wl_surface()) + { + // TODO should we return an error if a popup is attempted to be added to a non-leaf popup of a chain? + chain.push(popup); + } else { + if let Some(empty) = self.chains.iter_mut().find(|c| c.is_empty()) { + empty.push(popup); + } else { + self.chains.push(vec![popup]); + } + } + } + + pub(crate) fn remove( + &mut self, + popup: &WlSurface, + ) -> Option> { + // must perform cleanup so that the popups are dropped in the correct order + let ret = if let Some((chain, mut pos)) = + self.chain_for_popup_mut(popup) + { + // TODO should we return an error if a popup is attempted to be added to a non-leaf popup of a chain? + while let Some(p) = pos.checked_sub(1).and_then(|p| chain.get(p)) { + if p.close_with_children { + pos -= 1; + } else { + break; + } + } + + Some(chain.drain(pos..).rev()) + } else { + None + }; + + return ret; + } + + pub(crate) fn remove_ignore_children( + &mut self, + popup: &WlSurface, + ) -> Option> { + // must perform cleanup so that the popups are dropped in the correct order + let ret = if let Some((chain, pos)) = self.chain_for_popup_mut(popup) { + Some(chain.drain(pos..).rev()) + } else { + None + }; + + return ret; + } +} + +#[derive(Debug)] +pub struct SctkPopup { + pub(crate) popup: Popup, + pub(crate) last_configure: Option, + pub(crate) _pending_requests: + Vec, + pub(crate) data: SctkPopupData, + pub(crate) common: Arc>, + pub(crate) wp_fractional_scale: Option, + pub(crate) close_with_children: bool, +} + +impl SctkPopup { + pub(crate) fn set_size(&mut self, w: u32, h: u32, token: u32) { + let guard = self.common.lock().unwrap(); + if guard.size.width == w && guard.size.height == h { + return; + } + drop(guard); + // update geometry + self.popup + .xdg_surface() + .set_window_geometry(0, 0, w as i32, h as i32); + self.update_viewport(w, h); + // update positioner + self.data.positioner.set_size(w as i32, h as i32); + self.popup.reposition(&self.data.positioner, token); + } + + pub(crate) fn update_viewport(&mut self, w: u32, h: u32) { + let common = self.common.lock().unwrap(); + if common.size.width == w && common.size.height == h { + return; + } + if let Some(viewport) = common.wp_viewport.as_ref() { + // Set inner size without the borders. + viewport.set_destination(w as i32, h as i32); + } + } +} diff --git a/winit/src/platform_specific/wayland/event_loop/proxy.rs b/winit/src/platform_specific/wayland/event_loop/proxy.rs index bdbc44a4..58229dc1 100644 --- a/winit/src/platform_specific/wayland/event_loop/proxy.rs +++ b/winit/src/platform_specific/wayland/event_loop/proxy.rs @@ -1,8 +1,8 @@ use cctk::sctk::reexports::calloop; use iced_futures::futures::{ + Sink, channel::mpsc, task::{Context, Poll}, - Sink, }; use std::pin::Pin; diff --git a/winit/src/platform_specific/wayland/event_loop/state.rs b/winit/src/platform_specific/wayland/event_loop/state.rs index f99e48ba..7b1f65c0 100644 --- a/winit/src/platform_specific/wayland/event_loop/state.rs +++ b/winit/src/platform_specific/wayland/event_loop/state.rs @@ -1,9 +1,11 @@ use crate::{ Control, + event_loop::popup::{self, PopupManager}, handlers::{ activation::IcedRequestData, ext_background_effect, overlap::{OverlapNotificationV1, OverlapNotifyV1}, + shell::corner_radius::CornerRadiusWrapper, text_input::{Preedit, TextInputManager}, }, platform_specific::{ @@ -30,7 +32,6 @@ use std::{ convert::Infallible, fmt::Debug, sync::{Arc, Mutex, atomic::AtomicU32}, - thread::panicking, time::Duration, }; use wayland_backend::client::ObjectId; @@ -41,17 +42,14 @@ use winit::{ use cctk::{ cosmic_protocols::{ - corner_radius::v1::client::{ - cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, - cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, - }, + corner_radius::v1::client::cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1, + session_lock_layer::v1::client::cosmic_session_lock_layer_manager_v1::CosmicSessionLockLayerManagerV1, }, sctk::{ activation::{ActivationState, RequestData}, compositor::CompositorState, error::GlobalError, - globals::GlobalData, output::OutputState, reexports::{ calloop::{LoopHandle, timer::TimeoutAction}, @@ -67,11 +65,12 @@ use cctk::{ wl_touch::WlTouch, }, }, + protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, }, registry::RegistryState, seat::{ SeatState, - keyboard::KeyEvent, + keyboard::{KeyEvent, Modifiers}, pointer::{CursorIcon, PointerData, ThemedPointer}, touch::TouchData, }, @@ -85,10 +84,7 @@ use cctk::{ Anchor, KeyboardInteractivity, Layer, LayerShell, LayerSurface, LayerSurfaceConfigure, SurfaceKind, }, - xdg::{ - XdgPositioner, XdgShell, - popup::{Popup, PopupConfigure}, - }, + xdg::{XdgPositioner, XdgShell, popup::Popup}, }, shm::{Shm, multi::MultiPool}, }, @@ -97,7 +93,7 @@ use cctk::{ }; use iced_runtime::{ core::{self, Point, touch}, - keyboard::Modifiers, + keyboard::key::Named::Settings, platform_specific::{ self, wayland::{ @@ -109,7 +105,10 @@ use iced_runtime::{ }, }; use wayland_protocols::{ - ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1, + ext::background_effect::v1::client::{ + ext_background_effect_manager_v1, + ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1, + }, wp::{ fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1, keyboard_shortcuts_inhibit::zv1::client::{ @@ -124,6 +123,13 @@ use wayland_protocols::{ pub static TOKEN_CTR: AtomicU32 = AtomicU32::new(0); +/// The requested cursor for a surface +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub(crate) struct CursorRequest { + pub(crate) icon: Option, + pub(crate) hidden: bool, +} + #[derive(Debug)] pub(crate) struct SctkSeat { pub(crate) seat: WlSeat, @@ -140,6 +146,8 @@ pub(crate) struct SctkSeat { pub(crate) active_icon: Option, // Cursor icon set by application pub(crate) icon: Option, + // Application asked for cursor to hide + pub(crate) hidden: bool, } impl SctkSeat { @@ -149,6 +157,35 @@ impl SctkSeat { self.active_icon = Some(icon); } } + + /// Is the pointer currently over `surface`. + pub(crate) fn is_over(&self, surface: &ObjectId) -> bool { + self.ptr_focus.as_ref().is_some_and(|s| &s.id() == surface) + } + + /// Set cursor visibility over this surfaces. + pub(crate) fn set_cursor_visible( + &mut self, + conn: &Connection, + visible: bool, + ) { + let Some(ptr) = self.ptr.as_ref() else { + return; + }; + + self.hidden = !visible; + + if visible { + let icon = self.icon.unwrap_or(CursorIcon::Default); + _ = ptr.set_cursor(conn, icon); + self.active_icon = Some(icon); + } else { + // Ignoring the error + // It only fails when the pointer is not on our surface (due to serial error) + _ = ptr.hide_cursor(); + self.active_icon = None; + } + } } #[derive(Debug, Clone)] @@ -264,47 +301,6 @@ impl From> for Common { } } -#[derive(Debug)] -pub struct SctkPopup { - pub(crate) popup: Popup, - pub(crate) last_configure: Option, - pub(crate) _pending_requests: - Vec, - pub(crate) data: SctkPopupData, - pub(crate) common: Arc>, - pub(crate) wp_fractional_scale: Option, - pub(crate) close_with_children: bool, -} - -impl SctkPopup { - pub(crate) fn set_size(&mut self, w: u32, h: u32, token: u32) { - let guard = self.common.lock().unwrap(); - if guard.size.width == w && guard.size.height == h { - return; - } - drop(guard); - // update geometry - self.popup - .xdg_surface() - .set_window_geometry(0, 0, w as i32, h as i32); - self.update_viewport(w, h); - // update positioner - self.data.positioner.set_size(w as i32, h as i32); - self.popup.reposition(&self.data.positioner, token); - } - - pub(crate) fn update_viewport(&mut self, w: u32, h: u32) { - let common = self.common.lock().unwrap(); - if common.size.width == w && common.size.height == h { - return; - } - if let Some(viewport) = common.wp_viewport.as_ref() { - // Set inner size without the borders. - viewport.set_destination(w as i32, h as i32); - } - } -} - #[derive(Debug)] pub struct SctkLockSurface { pub(crate) id: core::window::Id, @@ -338,27 +334,18 @@ pub struct SctkSubsurface { pub struct SctkPopupData { pub(crate) id: core::window::Id, pub(crate) parent: PopupParent, + pub(crate) parent_window: core::window::Id, pub(crate) toplevel: WlSurface, pub(crate) positioner: Arc, pub(crate) grab: bool, } -#[derive(Debug)] -pub struct MyCosmicCornerRadiusToplevelV1(CosmicCornerRadiusToplevelV1); - -impl Drop for MyCosmicCornerRadiusToplevelV1 { - fn drop(&mut self) { - self.0.destroy(); - } -} - #[derive(Debug, Clone)] -pub struct SctkCornerRadius(Arc); +pub struct SctkCornerRadius(Arc); pub struct SctkWindow { pub(crate) window: Arc, pub(crate) id: core::window::Id, - pub(crate) corner_radius: Option<(SctkCornerRadius, Option)>, } impl SctkWindow { @@ -442,11 +429,15 @@ pub struct SctkState { /// `WindowUpdate` or buffer on the associated with it `WindowHandle`. pub(crate) windows: Vec, pub(crate) layer_surfaces: Vec, - pub(crate) popups: Vec, + pub(crate) popmgr: PopupManager, pub(crate) subsurfaces: Vec, pub(crate) lock_surfaces: Vec, - pub(crate) blur_surfaces: HashMap>, + pub(crate) blur_surfaces: HashMap, + pub(crate) corner_radii: HashMap)>, pub(crate) touch_points: HashMap, + /// List of layer surfaces whose keyboard leave was ignored because focus moved + /// to one of their own grabbing popups. Their next enter is swallowed too. + pub(crate) kbd_leave_to_own_popup: HashSet, /// Window updates, which are coming from SCTK or the compositor, which require /// calling back to the sctk's downstream. They are handled right in the event loop, @@ -481,6 +472,7 @@ pub struct SctkState { pub(crate) session_lock_state: SessionLockState, pub(crate) session_lock: Option, pub(crate) id_map: HashMap, + pub(crate) cursor_requests: HashMap, pub(crate) to_commit: HashMap, pub(crate) destroyed: HashSet, pub(crate) pending_popup: Option<(SctkPopupSettings, usize)>, @@ -489,6 +481,7 @@ pub struct SctkState { pub(crate) toplevel_manager: Option, pub(crate) subsurface_state: Option, pub(crate) ext_background_effect_manager: Option, + pub(crate) cosmic_session_lock_layer_manager: Option, pub(crate) activation_token_ctr: u32, pub(crate) token_senders: HashMap>>, @@ -499,6 +492,7 @@ pub struct SctkState { pub(crate) corner_radius_manager: Option, pub(crate) pending_corner_radius: HashMap, + pub(crate) pending_blur: HashMap>, pub(crate) text_input_manager: Option, pub(crate) text_input: Option>, @@ -510,6 +504,10 @@ pub struct SctkState { /// An error that occurred while running an application. #[derive(Debug, thiserror::Error)] pub enum PopupCreationError { + /// The requested ID already exists. + #[error("The requested ID already exists")] + AlreadyExists, + /// Positioner creation failed #[error("Positioner creation failed")] PositionerCreationFailed(GlobalError), @@ -530,6 +528,10 @@ pub enum PopupCreationError { /// An error that occurred while running an application. #[derive(Debug, thiserror::Error)] pub enum LayerSurfaceCreationError { + /// The requested ID already exists. + #[error("The requested ID already exists")] + AlreadyExists, + /// Layer shell is not supported by the compositor #[error("Layer shell is not supported by the compositor")] LayerShellNotSupported, @@ -546,6 +548,10 @@ pub enum LayerSurfaceCreationError { /// An error that occurred while running an application. #[derive(Debug, thiserror::Error)] pub enum SubsurfaceCreationError { + /// The requested ID already exists. + #[error("The requested ID already exists")] + AlreadyExists, + /// Subsurface creation failed #[error("Subsurface creation failed")] CreationFailed(GlobalError), @@ -570,6 +576,17 @@ pub(crate) fn receive_frame( } impl SctkState { + /// What a surface wants the pointer over it to be. + pub(crate) fn record_cursor( + &mut self, + surface: ObjectId, + amend: impl FnOnce(&mut CursorRequest), + ) { + self.cursor_requests + .retain(|id, _| id == &surface || self.id_map.contains_key(id)); + amend(self.cursor_requests.entry(surface).or_default()); + } + pub fn request_redraw(&mut self, surface: &WlSurface) { let e = self .frame_status @@ -606,11 +623,7 @@ impl SctkState { } } - if let Some(popup) = self - .popups - .iter_mut() - .find(|p| p.popup.wl_surface() == surface) - { + if let Some(popup) = self.popmgr.popup(surface) { id = Some(popup.data.id); if legacy && popup.wp_fractional_scale.is_some() { return; @@ -671,6 +684,33 @@ impl SctkState { // TODO winit sets cursor size after handling the change for the window, so maybe that should be done as well. } + /// Popups can only be attached to xdg or layer surfaces. So for subsurfaces, we return the + /// parent, and adjust the offset. + pub(crate) fn subsurface_popup_parent( + &self, + id: core::window::Id, + ) -> Option<(WlSurface, (i32, i32))> { + let mut sub = self.subsurfaces.iter().find(|s| s.id == id)?; + let mut offset = (0, 0); + // bounded by the number of subsurfaces so a corrupt chain cannot spin forever + for _ in 0..=self.subsurfaces.len() { + if let Some(b) = sub.instance.bounds { + offset.0 += b.x as i32; + offset.1 += b.y as i32; + } + let parent = &sub.instance.parent; + match self + .subsurfaces + .iter() + .find(|s| s.instance.wl_surface == *parent) + { + Some(s) => sub = s, + None => return Some((parent.clone(), offset)), + } + } + None + } + pub fn get_popup( &mut self, settings: SctkPopupSettings, @@ -684,6 +724,18 @@ impl SctkState { ), PopupCreationError, > { + if self.layer_surfaces.iter().any(|s| s.id == settings.id) + || self.windows.iter().any(|w| w.id == settings.id) + || self.popmgr.popup_id(settings.id).is_some() + || self.subsurfaces.iter().any(|s| s.id == settings.id) + { + log::warn!( + "Popup surface with id {:?} already exists", + settings.id + ); + return Err(PopupCreationError::AlreadyExists); + } + let (parent, toplevel) = if let Some(parent) = self.layer_surfaces.iter().find(|l| l.id == settings.parent) { @@ -698,16 +750,32 @@ impl SctkState { PopupParent::Window(parent.wl_surface(&self.connection)), parent.wl_surface(&self.connection), ) - } else if let Some(i) = self - .popups - .iter() - .position(|p| p.data.id == settings.parent) - { - let parent = &self.popups[i]; + } else if let Some(parent) = self.popmgr.popup_id(settings.parent) { ( PopupParent::Popup(parent.popup.wl_surface().clone()), parent.data.toplevel.clone(), ) + } else if let Some((surface, _)) = + self.subsurface_popup_parent(settings.parent) + { + if self + .layer_surfaces + .iter() + .any(|l| *l.surface.wl_surface() == surface) + { + (PopupParent::LayerSurface(surface.clone()), surface) + } else if self + .windows + .iter() + .any(|w| w.wl_surface(&self.connection) == surface) + { + (PopupParent::Window(surface.clone()), surface) + } else if let Some(p) = self.popmgr.popup(&surface) { + (PopupParent::Popup(surface), p.data.toplevel.clone()) + } else { + // e.g. a session lock surface, which cannot parent xdg popups + return Err(PopupCreationError::ParentMissing); + } } else { return Err(PopupCreationError::ParentMissing); }; @@ -741,6 +809,7 @@ impl SctkState { if positioner.version() >= 3 && settings.positioner.reactive { positioner.set_reactive(); } + positioner.set_size(size.0 as i32, size.1 as i32); let grab = settings.grab; @@ -805,10 +874,9 @@ impl SctkState { ) } PopupParent::Popup(parent) => { - let Some(parent_xdg) = self.popups.iter().find_map(|p| { - (p.popup.wl_surface() == parent) - .then(|| p.popup.xdg_surface()) - }) else { + let Some(parent_xdg) = + self.popmgr.popup(parent).map(|p| p.popup.xdg_surface()) + else { return Err(PopupCreationError::ParentMissing); }; @@ -825,6 +893,7 @@ impl SctkState { ) } }; + let toplevel = toplevel.clone(); popup.xdg_surface().set_window_geometry( 0, @@ -857,7 +926,15 @@ impl SctkState { } _ = wl_surface.frame(&self.queue_handle, wl_surface.clone()); - wl_surface.commit(); + + if let Some(old) = self.blur_surfaces.remove(&settings.id) { + old.destroy(); + } + _ = self.corner_radii.remove(&settings.id); + + if let Some(blur) = self.pending_blur.remove(&settings.id) { + self.apply_blur(settings.id, Some(blur), &wl_surface); + } let wp_viewport = self.viewporter_state.as_ref().map(|state| { let viewport = @@ -874,11 +951,12 @@ impl SctkState { let common = Arc::new(Mutex::new(common)); let positioner = Arc::new(positioner); - self.popups.push(SctkPopup { + self.popmgr.push(popup::SctkPopup { popup: popup.clone(), data: SctkPopupData { id: settings.id, parent: parent.clone(), + parent_window: settings.parent, toplevel: toplevel.clone(), positioner: positioner.clone(), grab: settings.grab, @@ -890,6 +968,13 @@ impl SctkState { close_with_children: settings.close_with_children, }); + if let Some(corners) = self.pending_corner_radius.remove(&settings.id) { + self.handle_action(Action::RoundedCorners( + settings.id, + Some(corners), + )); + } + wl_surface.commit(); Ok(( settings.id, parent.wl_surface().clone(), @@ -924,6 +1009,15 @@ impl SctkState { IcedOutput::Output(output) => Some(output), }; + if self.layer_surfaces.iter().any(|s| s.id == id) + || self.windows.iter().any(|w| w.id == id) + || self.popmgr.popup_id(id).is_some() + || self.subsurfaces.iter().any(|s| s.id == id) + { + log::warn!("Layer surface with id {id:?} already exists"); + return Err(LayerSurfaceCreationError::AlreadyExists); + } + let layer_shell = self .layer_shell .as_ref() @@ -976,7 +1070,15 @@ impl SctkState { layer_surface.set_input_region(Some(®ion)); region.destroy(); } - layer_surface.commit(); + + if let Some(old) = self.blur_surfaces.remove(&id) { + old.destroy(); + } + _ = self.corner_radii.remove(&id); + + if let Some(blur) = self.pending_blur.remove(&id) { + self.apply_blur(id, Some(blur), &wl_surface); + } let wp_viewport = self.viewporter_state.as_ref().map(|state| { state.get_viewport(layer_surface.wl_surface(), &self.queue_handle) @@ -1010,6 +1112,10 @@ impl SctkState { wp_fractional_scale, common: common.clone(), }); + if let Some(corners) = self.pending_corner_radius.remove(&id) { + _ = self.handle_action(Action::RoundedCorners(id, Some(corners))); + } + layer_surface.commit(); Ok((id, CommonSurface::Layer(layer_surface), common)) } pub fn get_lock_surface( @@ -1017,6 +1123,14 @@ impl SctkState { id: core::window::Id, output: &WlOutput, ) -> Option<(CommonSurface, Arc>)> { + if self.layer_surfaces.iter().any(|s| s.id == id) + || self.windows.iter().any(|w| w.id == id) + || self.popmgr.popup_id(id).is_some() + || self.subsurfaces.iter().any(|s| s.id == id) + { + log::warn!("Lock surface with id {id:?} already exists"); + return None; + } if let Some(lock) = self.session_lock.as_ref() { let wl_surface = self.compositor_state.create_surface(&self.queue_handle); @@ -1090,14 +1204,17 @@ impl SctkState { } }, platform_specific::wayland::layer_surface::Action::Destroy(id) => { + if let Some(id) = self.layer_surfaces.iter().find_map(|l| if l.id == id {self.popmgr.popups().find_map(|p| (p.data.parent.wl_surface() == l.surface.wl_surface()).then(|| p.data.id))} else {None}).clone() { + _ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy { id })); + } if let Some(i) = self.layer_surfaces.iter().position(|l| l.id == id) { let l = self.layer_surfaces.remove(i); + _ = self.kbd_leave_to_own_popup.remove(&l.surface.wl_surface().id()); if let Some(blurred) = self.blur_surfaces.remove(&l.id) { - for s in blurred { - s.destroy(); - } + blurred.destroy(); } + _ = self.corner_radii.remove(&id); let (removed, remaining): (Vec<_>, Vec<_>) = self .subsurfaces @@ -1120,6 +1237,7 @@ impl SctkState { if let Some(destroyed) = self.id_map.remove(&l.surface.wl_surface().id()) { _ = self.destroyed.insert(destroyed); } + send_event(&self.events_sender, &self.proxy, SctkEvent::LayerSurfaceEvent { variant: LayerSurfaceEventVariant::Done, id: l.surface.wl_surface().clone(), @@ -1194,205 +1312,229 @@ impl SctkState { } }, - }, - Action::Popup(action) => match action { - platform_specific::wayland::popup::Action::Popup { popup: settings } => { - // first check existing popup - if let Some(existing) = self.popups.iter().position(|p| p.data.id == settings.id - && ( - self.popups.iter().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent) - || self.windows.iter().any(|w| w.id == settings.parent && *p.data.parent.wl_surface() == w.wl_surface(&self.connection)) - || self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface())) - ) { - let existing = &mut self.popups[existing]; - let size = if settings.positioner.size.is_none() { - log::info!("No configured popup size"); - (1, 1) - } else { - settings.positioner.size.unwrap() - }; - let Ok(positioner) = XdgPositioner::new(&self.xdg_shell_state) - .map_err(PopupCreationError::PositionerCreationFailed) else { - log::error!("Failed to create popup positioner"); - return Ok(()); - }; - positioner.set_anchor(settings.positioner.anchor); - positioner.set_anchor_rect( - settings.positioner.anchor_rect.x, - settings.positioner.anchor_rect.y, - settings.positioner.anchor_rect.width, - settings.positioner.anchor_rect.height, - ); - if let Ok(constraint_adjustment) = - settings.positioner.constraint_adjustment.try_into() - { - positioner.set_constraint_adjustment(constraint_adjustment); - } - positioner.set_gravity(settings.positioner.gravity); - positioner.set_offset( - settings.positioner.offset.0, - settings.positioner.offset.1, - ); - if settings.positioner.reactive { - positioner.set_reactive(); - } - positioner.set_size(size.0 as i32, size.1 as i32); - existing.data.positioner = Arc::new(positioner); - existing.set_size(size.0, size.1, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); - _ = send_event(&self.events_sender, &self.proxy, - SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: existing.data.parent.wl_surface().clone(), parent_id: existing.data.parent.wl_surface().clone(), id: existing.popup.wl_surface().clone() }); - return Ok(()); - } - let mut parent_mismatch = !self.popups.is_empty(); - for p in &self.popups { - parent_mismatch = p.data.id != settings.parent; - } - if !self.destroyed.is_empty() || parent_mismatch { - if parent_mismatch { - let mut found = false; - for p in std::mem::take(&mut self.popups).into_iter().rev() { - let id = p.data.id; - self.popups.insert(0, p); - - found |= id == settings.parent; - if !found { - _ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy{id})); + platform_specific::wayland::layer_surface::Action::Padding { id, padding } => { + // corner radius padding + if let Some((protocol_object, _)) = self.corner_radii.get_mut(&id) { + if let CornerRadiusWrapper::Wlr(protocol_object) = protocol_object.0.as_ref() { + protocol_object.set_padding(padding.top, padding.right, padding.bottom, padding.left); + } + } + }, + platform_specific::wayland::layer_surface::Action::ShowOnLock { id, value } => { + if let Some(layer_surface) = self.layer_surfaces.iter_mut().find(|l| l.id == id) { + if let SurfaceKind::Wlr(wlr) = layer_surface.surface.kind() { + if let Some(manager) = &self.cosmic_session_lock_layer_manager { + if value { + manager.set_show_on_lock(wlr); + } else { + manager.unset_show_on_lock(wlr); + } + _ = self.to_commit.insert(id, layer_surface.surface.wl_surface().clone()); + } } - } } - if self.pending_popup.replace((settings, 0)).is_none() { - - let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30)); - let queue_handle = self.queue_handle.clone(); - _ = self.loop_handle.insert_source(timer, move |_, _, state| { - let Some((mut popup, attempt)) = state.pending_popup.take() else { - return TimeoutAction::Drop; + }, + Action::Popup(action) => { + match action { + platform_specific::wayland::popup::Action::Popup { popup: mut settings } => { + // anchor rects from widgets inside a subsurface are in that subsurface's coordinates + if let Some((_, (dx, dy))) = self.subsurface_popup_parent(settings.parent) { + settings.positioner.anchor_rect.x += dx; + settings.positioner.anchor_rect.y += dy; + } + // first check existing popup + // if an existing popup with the same id exists, we just update the positioner. + if let Some((chain, existing)) = self.popmgr.popup_id(settings.id).and_then(|p| + (p.data.grab == settings.grab && self.popmgr.popups().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent) + || self.windows.iter().any(|w| w.id == settings.parent && *p.data.parent.wl_surface() == w.wl_surface(&self.connection)) + || self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface()) + || (p.data.parent_window == settings.parent && self.subsurfaces.iter().any(|s| s.id == settings.parent))).then(|| + { + p.popup.wl_surface().clone() + })).and_then(|s| self.popmgr.chain_for_popup_mut(&s)) + { + let existing = &mut chain[existing]; + let size = if let Some(size) = settings.positioner.size { + size + } else { + let guard = existing.common.lock().unwrap(); + (guard.size.width, guard.size.height) + }; + let Ok(positioner) = XdgPositioner::new(&self.xdg_shell_state) + .map_err(PopupCreationError::PositionerCreationFailed) else { + log::error!("Failed to create popup positioner"); + return Ok(()); }; + positioner.set_anchor(settings.positioner.anchor); + positioner.set_anchor_rect( + settings.positioner.anchor_rect.x, + settings.positioner.anchor_rect.y, + settings.positioner.anchor_rect.width, + settings.positioner.anchor_rect.height, + ); + if let Ok(constraint_adjustment) = + settings.positioner.constraint_adjustment.try_into() + { + positioner.set_constraint_adjustment(constraint_adjustment); + } + positioner.set_gravity(settings.positioner.gravity); + positioner.set_offset( + settings.positioner.offset.0, + settings.positioner.offset.1, + ); + if settings.positioner.reactive { + positioner.set_reactive(); + } + positioner.set_size(size.0 as i32, size.1 as i32); + existing.data.positioner = Arc::new(positioner); + existing.set_size(size.0, size.1, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); + _ = send_event(&self.events_sender, &self.proxy, + SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: existing.data.parent.wl_surface().clone(), parent_id: existing.data.parent.wl_surface().clone(), id: existing.popup.wl_surface().clone(), parent_window: existing.data.parent_window }); + return Ok(()); + } else if !self.destroyed.is_empty() || self.popmgr.popup_id(settings.id).is_some() || self.popmgr.active_grab().is_some() + { + // must clean up popup chains so that there is just one grab chain... + let to_destroy = if settings.grab { + if let Some((grab_chain, p_i)) = self.popmgr.active_grab().and_then(|g| self.popmgr.chain_for_popup(g.popup.wl_surface())) { - if !state.destroyed.is_empty() || state.popups.last().is_some_and(|p| { - state.id_map.get(&p.popup.wl_surface().id()).map_or(true, |p| *p != popup.parent) - }) { - if attempt < 5 { - state.pending_popup = Some((popup, attempt+1)); - TimeoutAction::ToDuration(Duration::from_millis(30)) + if let Some(pos) = grab_chain.iter().position(|p| p.data.id == settings.parent) { + // destroy children of parent + grab_chain.get(pos + 1).map(|g| g.data.id) } else { - TimeoutAction::Drop + // destroy whole chain... + grab_chain.get(0).map(|g| g.data.id) } } else { - match state.get_popup(popup) { - Ok((id, parent_id, toplevel_id, surface, common)) => { - let wl_surface = surface.wl_surface().clone(); - receive_frame(&mut state.frame_status, &wl_surface); - send_event(&state.events_sender, &state.proxy, - SctkEvent::PopupEvent { - variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(queue_handle.clone(), surface, id, common, state.connection.display()), - toplevel_id, parent_id, id: wl_surface }); - } - Err(err) => { - log::error!("Failed to create popup. {err:?}"); - } - }; - TimeoutAction::Drop + None } - }); - } - // log::error!("Invalid popup Id {:?}", popup.id); - } else { - self.pending_popup = None; - match self.get_popup(settings) { - Ok((id, parent_id, toplevel_id, surface, common)) => { - let wl_surface = surface.wl_surface().clone(); - send_event(&self.events_sender, &self.proxy, - SctkEvent::PopupEvent { - variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(self.queue_handle.clone(), surface, id, common, self.connection.display()), - toplevel_id, parent_id, id: wl_surface }); + } else { + None + }; + if let Some(to_destroy) = to_destroy { + _ = self.destroy_popup(to_destroy, true); } - Err(err) => { - log::error!("Failed to create popup. {err:?}"); + + if self.popmgr.popup_id(settings.id).is_some() { + let _ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy { id: settings.id })); + } + if self.pending_popup.replace((settings, 0)).is_none() { + let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30)); + let queue_handle = self.queue_handle.clone(); + _ = self.loop_handle.insert_source(timer, move |_, _, state| { + let Some((settings, attempt)) = state.pending_popup.take() else { + return TimeoutAction::Drop; + }; + // check if the mismatch still exists... + if !state.destroyed.is_empty() || state.popmgr.popup_id(settings.id).is_some_and(|p| + state.popmgr.popups().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent)) + { + if attempt < 5 { + state.pending_popup = Some((settings, attempt+1)); + TimeoutAction::ToDuration(Duration::from_millis(30)) + } + else { + TimeoutAction::Drop + } + } else { + let parent_window = settings.parent; + match state.get_popup(settings) { + Ok((id, parent_id, toplevel_id, surface, common)) => { + let wl_surface = surface.wl_surface().clone(); + receive_frame(&mut state.frame_status, &wl_surface); + send_event(&state.events_sender, &state.proxy, + SctkEvent::PopupEvent { + variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(queue_handle.clone(), surface, id, common, state.connection.display()), + toplevel_id, parent_id, id: wl_surface, parent_window }); + } + Err(err) => { + log::error!("Failed to create popup. {err:?}"); + } + }; + TimeoutAction::Drop + } + }); + } + } else { + let parent_window = settings.parent; + match self.get_popup(settings) { + Ok((id, parent_id, toplevel_id, surface, common)) => { + let wl_surface = surface.wl_surface().clone(); + send_event(&self.events_sender, &self.proxy, + SctkEvent::PopupEvent { + variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created( + self.queue_handle.clone(), surface, id, common, self.connection.display() + ), + toplevel_id, parent_id, id: wl_surface, parent_window } + ); + } + Err(err) => { + log::error!("Failed to create popup. {err:?}"); + } } } - } - }, - // XXX popup destruction must be done carefully - // first destroy the uppermost popup, then work down to the requested popup - platform_specific::wayland::popup::Action::Destroy { id } => { - let sctk_popup = match self - .popups - .iter() - .position(|s| s.data.id == id) - { - Some(p) => self.popups.remove(p), - None => { - log::info!("No popup to destroy"); - return Ok(()); - }, - }; - let mut to_destroy = vec![sctk_popup]; - // TODO optionally destroy parents if they request to be destroyed with children - while let Some(popup_to_destroy_last) = to_destroy.last().and_then(|popup| self - .popups - .iter() - .position(|p| popup.data.parent.wl_surface() == p.popup.wl_surface() && p.close_with_children)) { - let popup_to_destroy_last = self.popups.remove(popup_to_destroy_last); - to_destroy.push(popup_to_destroy_last); - } - to_destroy.reverse(); - - while let Some(popup_to_destroy_first) = to_destroy.last().and_then(|popup| self - .popups - .iter() - .position(|p| p.data.parent.wl_surface() == popup.popup.wl_surface())) { - let popup_to_destroy_first = self.popups.remove(popup_to_destroy_first); - to_destroy.push(popup_to_destroy_first); - } - for popup in to_destroy.into_iter().rev() { - if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id()) { - _ = self.destroyed.insert(id); + }, + // XXX popup destruction must be done carefully + // first destroy the uppermost popup, then work down to the requested popup + platform_specific::wayland::popup::Action::Destroy { id } => { + if let Some(value) = self.destroy_popup(id, false) { + return value; } - - if let Some(blurred) = self.blur_surfaces.remove(&id) { - for s in blurred { - s.destroy(); - } - } - - let (removed, remaining): (Vec<_>, Vec<_>) = self - .subsurfaces - .drain(..) - .partition(|s| { - s.instance.parent == *popup.popup.wl_surface() - }); - - self.subsurfaces = remaining; - for s in removed + }, + platform_specific::wayland::popup::Action::Size { id, width, height } => { + if let Some(sctk_popup) = self + .popmgr.popup_id_mut(id) { - crate::subsurface_widget::remove_iced_subsurface( - &s.instance.wl_surface, - ); - send_event(&self.events_sender, &self.proxy, - SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(s.instance) ) - ); + // update geometry + // update positioner + sctk_popup.set_size(width, height, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); + let surface = sctk_popup.popup.wl_surface().clone(); + _ = send_event(&self.events_sender, &self.proxy, + SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(width, height), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface, parent_window: sctk_popup.data.parent_window }); } - _ = send_event(&self.events_sender, &self.proxy, - SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Done, toplevel_id: popup.data.toplevel.clone(), parent_id: popup.data.parent.wl_surface().clone(), id: popup.popup.wl_surface().clone() }); - } - }, - platform_specific::wayland::popup::Action::Size { id, width, height } => { - if let Some(sctk_popup) = self - .popups - .iter_mut() - .find(|s| s.data.id == id) - { - // update geometry - // update positioner - sctk_popup.set_size(width, height, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); - let surface = sctk_popup.popup.wl_surface().clone(); - _ = send_event(&self.events_sender, &self.proxy, - SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(width, height), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface }); - } - }, + }, + platform_specific::wayland::popup::Action::Reposition { id, positioner } => { + if let Some(sctk_popup) = self + .popmgr.popup_id_mut(id) + { + sctk_popup.data.positioner.set_anchor(positioner.anchor); + sctk_popup.data.positioner.set_anchor_rect( + positioner.anchor_rect.x, + positioner.anchor_rect.y, + positioner.anchor_rect.width, + positioner.anchor_rect.height, + ); + if let Ok(constraint_adjustment) = + positioner.constraint_adjustment.try_into() + { + sctk_popup.data.positioner.set_constraint_adjustment(constraint_adjustment); + } + sctk_popup.data.positioner.set_gravity(positioner.gravity); + sctk_popup.data.positioner.set_offset( + positioner.offset.0, + positioner.offset.1, + ); + if positioner.reactive { + sctk_popup.data.positioner.set_reactive(); + } + let guard =sctk_popup.common.lock().unwrap(); + let w = guard.size.width; + let h = guard.size.height; + drop(guard); + let size = positioner.size.unwrap_or((w, h)); + sctk_popup.popup + .xdg_surface() + .set_window_geometry(0, 0, w as i32, h as i32); + sctk_popup.update_viewport(w, h); + // update positioner + sctk_popup.data.positioner.set_size(w as i32, h as i32); + sctk_popup.popup.reposition(&sctk_popup.data.positioner, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); let surface = sctk_popup.popup.wl_surface().clone(); + _ = send_event(&self.events_sender, &self.proxy, + SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface, parent_window: sctk_popup.data.parent_window }); + } + }, + } }, Action::Activation(activation_event) => match activation_event { platform_specific::wayland::activation::Action::RequestToken { app_id, window, channel } => { @@ -1469,7 +1611,7 @@ impl SctkState { // TODO how to handle this when there's no lock? if let Some((surface, _)) = self.get_lock_surface(id, &output) { let wl_surface = surface.wl_surface(); - + receive_frame(&mut self.frame_status, &wl_surface); } } @@ -1481,10 +1623,10 @@ impl SctkState { { let surface = self.lock_surfaces.remove(i); if let Some(blurred) = self.blur_surfaces.remove(&surface.id) { - for s in blurred { - s.destroy(); - } + blurred.destroy(); } + _ = self.corner_radii.remove(&id); + let (removed, remaining): (Vec<_>, Vec<_>) = self .subsurfaces .drain(..) @@ -1524,7 +1666,7 @@ impl SctkState { }; let notification = overlap_notify_state.notify.notify_on_overlap(wlr, &self.queue_handle, OverlapNotificationV1 { surface: layer_surface.surface.wl_surface().clone() }); _ = self.overlap_notifications.insert(my_id, notification); - } else { + } else if !enabled { _ = self.overlap_notifications.remove(&my_id); } } else { @@ -1566,10 +1708,10 @@ impl SctkState { } for (destroyed, parent, id) in destroyed { if let Some(blurred) = self.blur_surfaces.remove(&id) { - for s in blurred { - s.destroy(); - } + blurred.destroy(); } + _ = self.corner_radii.remove(&id); + if let Some((wl_surface, f)) = self.seats.iter_mut().find(|f| { f.kbd_focus.as_ref().is_some_and(|f| *f == destroyed) }).and_then(|f| Some((parent, &mut f.kbd_focus))) { @@ -1578,9 +1720,13 @@ impl SctkState { } }, subsurface::Action::Reposition { id, x, y } => { - if let Some(subsurface) = self.subsurfaces.iter().find(|s| s.id == id) { + if let Some(subsurface) = self.subsurfaces.iter_mut().find(|s| s.id == id) { subsurface.instance.wl_subsurface.set_position(x, y); subsurface.instance.wl_surface.commit(); + if let Some(b) = subsurface.instance.bounds.as_mut() { + b.x = x as f32; + b.y = y as f32; + } } }, }, @@ -1597,8 +1743,25 @@ impl SctkState { } Action::RoundedCorners(id, v) => { if let Some(manager) = self.corner_radius_manager.as_ref() { - if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) { - let geo_size: LogicalSize = w.window.surface_size().cast::().to_logical(w.window.scale_factor()); + enum Surface { + Xdg(XdgSurface, Option), + Wlr(ZwlrLayerSurfaceV1), + } + let s = if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) { + Some((Surface::Xdg(w.xdg_surface(&self.connection), Some(w.xdg_toplevel(&self.connection))), w.window.surface_size().cast::().to_logical(w.window.scale_factor()))) + } else if let Some(p) = self.popmgr.popup_id_mut(id) { + let guard = p.common.lock().unwrap(); + Some((Surface::Xdg(p.popup.xdg_surface().clone(), None), guard.size.cast::())) + } else if let Some(l) = self.layer_surfaces.iter_mut().find(|l| l.id == id) { + let guard = l.common.lock().unwrap(); + match l.surface.kind() { + SurfaceKind::Wlr(l) => Some((Surface::Wlr(l.clone()), guard.size.cast::())), + _ => None + } + } else { + None + }; + if let Some((s, geo_size)) = s { let half_min_dim = (geo_size.width as u32).min(geo_size.height as u32) / 2; if let Some(radii) = v { @@ -1608,37 +1771,73 @@ impl SctkState { bottom_right: radii.bottom_right.min(half_min_dim), bottom_left: radii.bottom_left.min(half_min_dim), }; - if let Some((protocol_object, corner_radii)) = w.corner_radius.as_mut() { + if let Some((protocol_object, corner_radii)) = self.corner_radii.get_mut(&id) { if *corner_radii != Some(adjusted_radii) { - protocol_object.0.0.set_radius( + match protocol_object.0.as_ref() { + CornerRadiusWrapper::Xdg(protocol_object) => protocol_object.set_radius( + adjusted_radii.top_left, + adjusted_radii.top_right, + adjusted_radii.bottom_right, + adjusted_radii.bottom_left, + ), + CornerRadiusWrapper::Wlr(protocol_object) => protocol_object.set_radius( + adjusted_radii.top_left, + adjusted_radii.top_right, + adjusted_radii.bottom_right, + adjusted_radii.bottom_left, + ) + }; + *corner_radii = Some(adjusted_radii.clone()); + } + } else { + + let protocol_object = match s { + Surface::Xdg(s, w) => { + if manager.version() == 1 { + if let Some(w) = w { + CornerRadiusWrapper::Xdg(manager.get_corner_radius(&w, &self.queue_handle, ())) + } else { + log::error!("Corner radius is not supported for popups on xdg shell v1"); + return Ok(()); + } + } else { + CornerRadiusWrapper::Xdg(manager.get_corner_radius_surface(&s, &self.queue_handle, ()))} + } + Surface::Wlr(l) => { + if manager.version() == 1 { + return Ok(()); + } + CornerRadiusWrapper::Wlr(manager.get_corner_radius_layer(&l, &self.queue_handle, ()))} + }; + match &protocol_object { + CornerRadiusWrapper::Xdg(protocol_object) => protocol_object.set_radius( adjusted_radii.top_left, adjusted_radii.top_right, adjusted_radii.bottom_right, adjusted_radii.bottom_left, - ); - *corner_radii = Some(adjusted_radii.clone()); - } - } else { - let toplevel = w.xdg_toplevel(&self.connection); - - let protocol_object = manager.get_corner_radius(&toplevel, &self.queue_handle, ()); - - protocol_object.set_radius( - adjusted_radii.top_left, - adjusted_radii.top_right, - adjusted_radii.bottom_right, - adjusted_radii.bottom_left, - ); - w.corner_radius = Some((SctkCornerRadius(Arc::new(MyCosmicCornerRadiusToplevelV1( protocol_object))), Some(adjusted_radii.clone()))); + ), + CornerRadiusWrapper::Wlr(protocol_object) => protocol_object.set_radius( + adjusted_radii.top_left, + adjusted_radii.top_right, + adjusted_radii.bottom_right, + adjusted_radii.bottom_left, + ) + }; + _ = self.corner_radii.insert(id, (SctkCornerRadius(Arc::new(protocol_object)), Some(adjusted_radii.clone()))); } } else { - if let Some(old) = w.corner_radius.as_mut() { - old.0.0.as_ref().0.unset_radius(); + if let Some(old) = self.corner_radii.get_mut(&id) { + match old.0.0.as_ref() { + CornerRadiusWrapper::Xdg(protocol_object) => protocol_object.unset_radius(), + CornerRadiusWrapper::Wlr(protocol_object) => { + protocol_object.unset_radius() + } + }; old.1 = None; } } } else { - if let Some(v) = v{ + if let Some(v) = v { _ = self.pending_corner_radius.insert(id, v); } else { _ = self.pending_corner_radius.remove(&id); @@ -1653,8 +1852,8 @@ impl SctkState { bg_effect_mgr.enqueue(id, rectangles.clone()); return Ok(()); } - - let s = if let Some(s) = self.popups.iter().find(|s| s.data.id == id) { + + let s = if let Some(s) = self.popmgr.popup_id(id) { s.popup.wl_surface() } else if let Some(s) = self.layer_surfaces.iter().find(|s| s.id == id) { s.surface.wl_surface() @@ -1663,84 +1862,149 @@ impl SctkState { } else if let Some(subsurface) = self.subsurfaces.iter().find(|s| s.id == id) { &subsurface.instance.wl_surface } else { - log::error!("Failed to find surface for blur action"); + log::info!("Failed to find surface for blur action"); + if let Some(rectangles) = rectangles { + _ = self.pending_blur.insert(id, rectangles.clone()); + } return Ok(()); - }; - let existing_blur = self.blur_surfaces.entry(id); - match (existing_blur, rectangles) { - (Entry::Occupied(occupied_entry), None) => { - let blur_surfaces = occupied_entry.remove(); - for region in blur_surfaces { - region.destroy(); - } - }, - (Entry::Occupied(mut occupied_entry), Some(rectangles)) => { - let blur_surfaces = occupied_entry.get_mut(); - let regions = rectangles.into_iter().map(|rect| { - let region = self - .compositor_state - .wl_compositor() - .create_region(&self.queue_handle, ()); - region.add( - rect.x.round() as i32, - rect.y.round() as i32, - rect.width.round() as i32, - rect.height.round() as i32, - ); - region - }).collect::>(); - if regions.len() > blur_surfaces.len() { - // add extra blur surfaces if needed - for _ in blur_surfaces.len()..regions.len() { - let Some(extra_region) = self.ext_background_effect_manager.as_mut().map(|mgr| mgr.blur(s, &self.queue_handle)) else { - log::error!("Failed to create blur effect for surface"); - return Ok(()); - }; - blur_surfaces.push(extra_region); - } - } else if regions.len() < blur_surfaces.len() { - for surface in blur_surfaces.iter().skip(regions.len()) { - surface.destroy(); - } - } - - // update existing blur surfaces - for (blur_surface, region) in blur_surfaces.iter().zip(regions.into_iter()) { - blur_surface.set_blur_region(Some(®ion)); - } - }, - (Entry::Vacant(..), None) => { - // nothing to remove - }, - (Entry::Vacant(vacant_entry), Some(rectangles)) => { - if self.ext_background_effect_manager.is_none() { - log::error!("Blur effect is not supported."); - return Ok(()); - } - let blur_surfaces = rectangles.into_iter().map(|rect| { - let region = self - .compositor_state - .wl_compositor() - .create_region(&self.queue_handle, ()); - region.add( - rect.x.round() as i32, - rect.y.round() as i32, - rect.width.round() as i32, - rect.height.round() as i32, - ); - let blur_manager = self.ext_background_effect_manager.as_mut().unwrap(); - let blur_surface = blur_manager.blur(s, &self.queue_handle); - blur_surface.set_blur_region(Some(®ion)); - blur_surface - }).collect::>(); - _ = vacant_entry.insert(blur_surfaces); - }, - } + }.clone(); + self.apply_blur(id, rectangles, &s) }, }; Ok(()) } + fn destroy_popup( + &mut self, + id: core::window::Id, + ignore_children: bool, + ) -> Option> { + let Some(to_destroy) = self.popmgr.popup_id(id) else { + if self.pending_popup.take().is_none_or(|p_id| p_id.0.id != id) { + log::warn!( + "Destroyed popup does not exist. {id:?}, existing: {:?}", + self.popmgr.popups().map(|p| p.data.id).collect::>() + ); + } + return Some(Ok(())); + }; + let s = to_destroy.popup.wl_surface().clone(); + let d: Option> = if ignore_children { + self.popmgr.remove(&s).map(|v| v.collect()) + } else { + self.popmgr.remove_ignore_children(&s).map(|v| v.collect()) + }; + let Some(to_destroy) = d else { + return Some(Ok(())); + }; + + for popup in to_destroy { + if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id()) + { + _ = self.destroyed.insert(id); + } + + if let Some(blurred) = self.blur_surfaces.remove(&id) { + blurred.destroy(); + } + _ = self.corner_radii.remove(&id); + + let (removed, remaining): (Vec<_>, Vec<_>) = self + .subsurfaces + .drain(..) + .partition(|s| s.instance.parent == *popup.popup.wl_surface()); + + self.subsurfaces = remaining; + for s in removed { + crate::subsurface_widget::remove_iced_subsurface( + &s.instance.wl_surface, + ); + send_event( + &self.events_sender, + &self.proxy, + SctkEvent::SubsurfaceEvent( + crate::sctk_event::SubsurfaceEventVariant::Destroyed( + s.instance, + ), + ), + ); + } + _ = send_event( + &self.events_sender, + &self.proxy, + SctkEvent::PopupEvent { + variant: crate::sctk_event::PopupEventVariant::Done, + parent_window: popup.data.parent_window, + toplevel_id: popup.data.toplevel.clone(), + parent_id: popup.data.parent.wl_surface().clone(), + id: popup.popup.wl_surface().clone(), + }, + ); + } + None + } + + fn apply_blur( + &mut self, + id: core::window::Id, + rectangles: Option>, + s: &WlSurface, + ) { + let existing_blur = self.blur_surfaces.entry(id); + + match (existing_blur, rectangles) { + (Entry::Occupied(occupied_entry), None) => { + let blur_surface = occupied_entry.remove(); + blur_surface.destroy(); + } + (Entry::Occupied(mut occupied_entry), Some(rectangles)) => { + let blur_surface = occupied_entry.get_mut(); + let region = self + .compositor_state + .wl_compositor() + .create_region(&self.queue_handle, ()); + for rect in rectangles.into_iter() { + region.add( + rect.x.round() as i32, + rect.y.round() as i32, + rect.width.round() as i32, + rect.height.round() as i32, + ); + } + + // update existing blur surfaces + blur_surface.set_blur_region(Some(®ion)); + } + (Entry::Vacant(..), None) => { + // nothing to remove + } + (Entry::Vacant(vacant_entry), Some(rectangles)) => { + if self.ext_background_effect_manager.is_none() { + log::error!("Blur effect is not supported."); + return; + } + let region = self + .compositor_state + .wl_compositor() + .create_region(&self.queue_handle, ()); + for rect in rectangles { + region.add( + rect.x.round() as i32, + rect.y.round() as i32, + rect.width.round() as i32, + rect.height.round() as i32, + ); + } + + let blur_manager = + self.ext_background_effect_manager.as_mut().unwrap(); + let blur_surface = blur_manager.blur(s, &self.queue_handle); + blur_surface.set_blur_region(Some(®ion)); + _ = vacant_entry.insert(blur_surface); + } + } + } + pub fn get_subsurface( &mut self, settings: SctkSubsurfaceSettings, @@ -1754,9 +2018,17 @@ impl SctkState { ), SubsurfaceCreationError, > { - let Some(subsurface_state) = self.subsurface_state.as_ref() else { + let Some(subsurface_state) = self.subsurface_state.clone() else { return Err(SubsurfaceCreationError::Unsupported); }; + if self.layer_surfaces.iter().any(|s| s.id == settings.id) + || self.windows.iter().any(|w| w.id == settings.id) + || self.popmgr.popup_id(settings.id).is_some() + || self.subsurfaces.iter().any(|s| s.id == settings.id) + { + log::warn!("Subsurface with id {:?} already exists", settings.id); + return Err(SubsurfaceCreationError::AlreadyExists); + } let size = settings.size.unwrap_or(Size::new(1., 1.)); let half_w = size.width / 2.; @@ -1806,12 +2078,7 @@ impl SctkState { self.windows.iter().find(|w| w.id == settings.parent) { PopupParent::Window(parent.wl_surface(&self.connection)) - } else if let Some(i) = self - .popups - .iter() - .position(|p| p.data.id == settings.parent) - { - let parent = &self.popups[i]; + } else if let Some(parent) = self.popmgr.popup_id(settings.parent) { PopupParent::Popup(parent.popup.wl_surface().clone()) } else if let Some(i) = self .lock_surfaces @@ -1862,7 +2129,14 @@ impl SctkState { region.destroy(); } - wl_surface.commit(); + if let Some(old) = self.blur_surfaces.remove(&settings.id) { + old.destroy(); + } + _ = self.corner_radii.remove(&settings.id); + + if let Some(blur) = self.pending_blur.remove(&settings.id) { + _ = self.apply_blur(settings.id, Some(blur), &wl_surface); + } let wp_viewport = subsurface_state.wp_viewporter.get_viewport( &wl_surface, @@ -1938,7 +2212,11 @@ impl SctkState { // XXX subsurfaces need to be sorted by z in descending order self.subsurfaces .sort_by(|a, b| b.instance.z.cmp(&a.instance.z)); + if let Some(corners) = self.pending_corner_radius.remove(&id) { + self.handle_action(Action::RoundedCorners(id, Some(corners))); + } + wl_surface.commit(); Ok(( id, parent.wl_surface().clone(), @@ -1965,3 +2243,4 @@ pub(crate) fn send_event( delegate_noop!(SctkState: ignore WlSubsurface); delegate_noop!(SctkState: ignore WlRegion); +delegate_noop!(SctkState: CosmicSessionLockLayerManagerV1); diff --git a/winit/src/platform_specific/wayland/handlers/ext_background_effect.rs b/winit/src/platform_specific/wayland/handlers/ext_background_effect.rs index 03a80da9..2d0df67a 100644 --- a/winit/src/platform_specific/wayland/handlers/ext_background_effect.rs +++ b/winit/src/platform_specific/wayland/handlers/ext_background_effect.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use cctk::sctk; +use iced_futures::futures::SinkExt; use iced_runtime::core::Rectangle; use iced_runtime::platform_specific::wayland::Action; use sctk::globals::GlobalData; @@ -11,7 +12,7 @@ use wayland_protocols::ext::background_effect::v1::client::ext_background_effect use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1; use crate::event_loop::state::SctkState; -use crate::window; +use crate::{platform_specific, window}; #[derive(Debug, Clone)] pub struct ExtBackgroundEffectManager { @@ -70,8 +71,15 @@ impl Dispatch state.ext_background_effect_manager.as_mut() { bg_effect_mgr.capabilities = capability; - queued_actions = - bg_effect_mgr.queued_blur_actions.drain().collect(); + if capability.contains(Capability::Blur) { + queued_actions = bg_effect_mgr + .queued_blur_actions + .drain() + .collect(); + state + .sctk_events + .push(crate::sctk_event::SctkEvent::Blur); + } } for (id, rects) in queued_actions { _ = state.handle_action(Action::BlurSurface(id, rects)); diff --git a/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs b/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs index 5ff7fb4b..e3ad792a 100644 --- a/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs +++ b/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs @@ -1,12 +1,52 @@ use crate::platform_specific::wayland::{ - event_loop::state::SctkState, + event_loop::state::{PopupParent, SctkState}, sctk_event::{KeyboardEventVariant, SctkEvent}, }; use cctk::sctk::{ delegate_keyboard, - seat::keyboard::{KeyboardHandler, Keysym}, + seat::keyboard::{KeyboardHandler, Keysym, Modifiers}, }; -use cctk::sctk::{reexports::client::Proxy, seat::keyboard::RawModifiers}; +use cctk::sctk::{ + reexports::client::Proxy, + seat::keyboard::RawModifiers, + shell::{WaylandSurface, wlr_layer::KeyboardInteractivity}, +}; + +fn modifiers_from_keysyms( + keysyms: &[Keysym], + previous: Modifiers, +) -> Modifiers { + let mut modifiers = Modifiers { + caps_lock: previous.caps_lock, + num_lock: previous.num_lock, + ..Modifiers::default() + }; + + for keysym in keysyms { + match keysym.raw() { + xkeysym::key::Shift_L | xkeysym::key::Shift_R => { + modifiers.shift = true + } + xkeysym::key::Control_L | xkeysym::key::Control_R => { + modifiers.ctrl = true + } + xkeysym::key::Alt_L | xkeysym::key::Alt_R => modifiers.alt = true, + xkeysym::key::Super_L + | xkeysym::key::Super_R + | xkeysym::key::Hyper_L + | xkeysym::key::Hyper_R + | xkeysym::key::Meta_L + | xkeysym::key::Meta_R => modifiers.logo = true, + xkeysym::key::Caps_Lock | xkeysym::key::Shift_Lock => { + modifiers.caps_lock = true + } + xkeysym::key::Num_Lock => modifiers.num_lock = true, + _ => {} + } + } + + modifiers +} impl KeyboardHandler for SctkState { fn enter( @@ -17,9 +57,9 @@ impl KeyboardHandler for SctkState { surface: &cctk::sctk::reexports::client::protocol::wl_surface::WlSurface, _serial: u32, _raw: &[u32], - _keysyms: &[Keysym], + keysyms: &[Keysym], ) { - let (i, mut is_active, seat) = { + let (i, mut is_active, seat, modifiers) = { let (i, is_active, my_seat) = match self.seats.iter_mut().enumerate().find_map(|(i, s)| { if s.kbd.as_ref() == Some(keyboard) { @@ -41,9 +81,11 @@ impl KeyboardHandler for SctkState { surface }; _ = my_seat.kbd_focus.replace(surface.clone()); + let modifiers = modifiers_from_keysyms(keysyms, my_seat._modifiers); + my_seat._modifiers = modifiers; let seat = my_seat.seat.clone(); - (i, is_active, seat) + (i, is_active, seat, modifiers) }; if !is_active && self.seats[0].kbd_focus.is_none() { @@ -52,6 +94,11 @@ impl KeyboardHandler for SctkState { } self.request_redraw(&surface); + // Focus is coming back from our own grabbing popup + if self.kbd_leave_to_own_popup.remove(&surface.id()) { + return; + } + let surfaces = self.subsurfaces.iter().filter_map(|s| { (s.instance.parent == *surface).then(|| &s.instance.wl_surface) }); @@ -73,6 +120,12 @@ impl KeyboardHandler for SctkState { seat_id: seat.clone(), surface: surface.clone(), }); + self.sctk_events.push(SctkEvent::KeyboardEvent { + variant: KeyboardEventVariant::Modifiers(modifiers), + kbd_id: keyboard.clone(), + seat_id: seat.clone(), + surface: surface.clone(), + }); } } } @@ -103,6 +156,32 @@ impl KeyboardHandler for SctkState { _ = my_seat.kbd_focus.take(); (is_active, seat, kbd) }; + + // A grabbing popup parented to this layer surface, or opened from one of + // its subsurfaces that holds focus, takes keyboard focus. + // Only swallow the leave when the layer surface has exclusive keyboard interactivity. + let focus_moved_to_own_popup = self.popmgr.popups().any(|p| { + p.data.grab + && match &p.data.parent { + PopupParent::LayerSurface(s) => { + (s == surface + || self.subsurfaces.iter().any(|sub| { + sub.id == p.data.parent_window + && sub.instance.wl_surface == *surface + })) + && self.layer_surfaces.iter().any(|l| { + l.surface.wl_surface() == s + && l.keyboard_interactivity + == KeyboardInteractivity::Exclusive + }) + } + _ => false, + } + }); + if focus_moved_to_own_popup { + _ = self.kbd_leave_to_own_popup.insert(surface.id()); + return; + } let surfaces = self.subsurfaces.iter().filter_map(|s| { (s.instance.parent == *surface).then(|| &s.instance.wl_surface) }); @@ -126,6 +205,7 @@ impl KeyboardHandler for SctkState { if self.windows.iter().any(|w| w.window.id() == id) { continue; } + let modifiers = s._modifiers; self.sctk_events.push(SctkEvent::Winit( id, winit::event::WindowEvent::Focused(true), @@ -137,6 +217,12 @@ impl KeyboardHandler for SctkState { kbd_id: s.kbd.clone().unwrap(), seat_id: s.seat.clone(), surface: surface.clone(), + }); + self.sctk_events.push(SctkEvent::KeyboardEvent { + variant: KeyboardEventVariant::Modifiers(modifiers), + kbd_id: s.kbd.clone().unwrap(), + seat_id: s.seat.clone(), + surface: surface.clone(), }) } } @@ -248,6 +334,7 @@ impl KeyboardHandler for SctkState { }; let seat_id = my_seat.seat.clone(); let kbd_id = keyboard.clone(); + my_seat._modifiers = modifiers; if is_active { if let Some(surface) = my_seat.kbd_focus.clone() { @@ -266,6 +353,31 @@ impl KeyboardHandler for SctkState { surface: surface.clone(), }); } + // A popup holds the seat's keyboard on behalf of its toplevel. Report + // the modifiers to the toplevel as well. + let toplevel = self.popmgr.popups().find_map(|p| { + (p.popup.wl_surface() == &surface) + .then(|| p.data.toplevel.clone()) + }); + if let Some(toplevel) = toplevel { + let id = self + .windows + .iter() + .find(|w| w.wl_surface(&self.connection) == toplevel) + .map(|w| w.id) + .or_else(|| { + self.layer_surfaces + .iter() + .find(|l| *l.surface.wl_surface() == toplevel) + .map(|l| l.id) + }); + if let Some(toplevel) = id { + self.sctk_events.push(SctkEvent::PopupModifiers { + toplevel, + modifiers, + }); + } + } } } } diff --git a/winit/src/platform_specific/wayland/handlers/seat/pointer.rs b/winit/src/platform_specific/wayland/handlers/seat/pointer.rs index 8c962a4d..ac0bd4a9 100644 --- a/winit/src/platform_specific/wayland/handlers/seat/pointer.rs +++ b/winit/src/platform_specific/wayland/handlers/seat/pointer.rs @@ -1,8 +1,5 @@ -use crate::{ - event_loop::state::FrameStatus, - platform_specific::wayland::{ - event_loop::state::SctkState, sctk_event::SctkEvent, - }, +use crate::platform_specific::wayland::{ + event_loop::state::SctkState, sctk_event::SctkEvent, }; use cctk::sctk::{ delegate_pointer, @@ -34,7 +31,7 @@ impl PointerHandler for SctkState { // track events, but only forward for the active seat for e in events { - if my_seat.active_icon != my_seat.icon { + if !my_seat.hidden && my_seat.active_icon != my_seat.icon { // Restore cursor that was set by appliction, or default my_seat.set_cursor( conn, @@ -50,14 +47,6 @@ impl PointerHandler for SctkState { continue; } - let entry = self - .frame_status - .entry(e.surface.id()) - .or_insert(FrameStatus::RequestedRedraw); - if matches!(entry, FrameStatus::Received) { - *entry = FrameStatus::Ready; - } - self.sctk_events.push(SctkEvent::PointerEvent { variant: PointerEvent { surface: e.surface.clone(), @@ -71,10 +60,19 @@ impl PointerHandler for SctkState { match e.kind { PointerEventKind::Enter { .. } => { _ = my_seat.ptr_focus.replace(e.surface.clone()); + if let Some(want) = + self.cursor_requests.get(&e.surface.id()) + { + my_seat.icon = want.icon; + my_seat.set_cursor_visible(conn, !want.hidden); + } } PointerEventKind::Leave { .. } => { _ = my_seat.ptr_focus.take(); _ = my_seat.active_icon = None; + // The next surface the pointer lands on decides what it + // looks like there + my_seat.hidden = false; } PointerEventKind::Press { time, diff --git a/winit/src/platform_specific/wayland/handlers/seat/seat.rs b/winit/src/platform_specific/wayland/handlers/seat/seat.rs index 2901ce8e..ec2b187c 100644 --- a/winit/src/platform_specific/wayland/handlers/seat/seat.rs +++ b/winit/src/platform_specific/wayland/handlers/seat/seat.rs @@ -4,10 +4,9 @@ use crate::platform_specific::wayland::{ }; use cctk::sctk::{ delegate_seat, - reexports::client::{protocol::wl_keyboard::WlKeyboard, Proxy}, - seat::{pointer::ThemeSpec, SeatHandler}, + reexports::client::{Proxy, protocol::wl_keyboard::WlKeyboard}, + seat::{SeatHandler, keyboard::Modifiers, pointer::ThemeSpec}, }; -use iced_runtime::keyboard::Modifiers; use std::sync::Arc; impl SeatHandler for SctkState { @@ -39,6 +38,7 @@ impl SeatHandler for SctkState { last_touch_down: None, icon: None, active_icon: None, + hidden: false, }); } @@ -66,6 +66,7 @@ impl SeatHandler for SctkState { last_touch_down: None, icon: None, active_icon: None, + hidden: false, }); self.seats.last_mut().unwrap() } diff --git a/winit/src/platform_specific/wayland/handlers/seat/touch.rs b/winit/src/platform_specific/wayland/handlers/seat/touch.rs index f726fd93..a0b1cce0 100644 --- a/winit/src/platform_specific/wayland/handlers/seat/touch.rs +++ b/winit/src/platform_specific/wayland/handlers/seat/touch.rs @@ -9,12 +9,12 @@ use crate::{ use cctk::sctk::{ delegate_touch, reexports::client::{ - protocol::{wl_surface::WlSurface, wl_touch::WlTouch}, Connection, Proxy, QueueHandle, + protocol::{wl_surface::WlSurface, wl_touch::WlTouch}, }, seat::touch::TouchHandler, }; -use iced_runtime::core::{touch, Point}; +use iced_runtime::core::{Point, touch}; impl TouchHandler for SctkState { fn down( diff --git a/winit/src/platform_specific/wayland/handlers/session_lock.rs b/winit/src/platform_specific/wayland/handlers/session_lock.rs index d0eee5ef..98eb5586 100644 --- a/winit/src/platform_specific/wayland/handlers/session_lock.rs +++ b/winit/src/platform_specific/wayland/handlers/session_lock.rs @@ -1,6 +1,9 @@ use crate::{ event_loop::state::CommonSurface, - platform_specific::wayland::{handlers::SctkState, sctk_event::SctkEvent}, + platform_specific::wayland::{ + event_loop::state::receive_frame, handlers::SctkState, + sctk_event::SctkEvent, + }, }; use cctk::sctk::{ delegate_session_lock, @@ -67,6 +70,10 @@ impl SessionLockHandler for SctkState { configure, first, }); + + let wl_surface = session_lock_surface.wl_surface().clone(); + receive_frame(&mut self.frame_status, &wl_surface); + self.request_redraw(&wl_surface); } } diff --git a/winit/src/platform_specific/wayland/handlers/shell/corner_radius.rs b/winit/src/platform_specific/wayland/handlers/shell/corner_radius.rs index bd7d2409..c4bdda88 100644 --- a/winit/src/platform_specific/wayland/handlers/shell/corner_radius.rs +++ b/winit/src/platform_specific/wayland/handlers/shell/corner_radius.rs @@ -1,17 +1,29 @@ -use cctk::{sctk, cosmic_protocols::{ - corner_radius::v1::client::{ +use cctk::{ + cosmic_protocols::corner_radius::v1::client::{ + cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, }, - overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1, -}}; -use sctk::reexports::{ - client::{Connection, Dispatch, Proxy}, - + sctk, }; +use sctk::reexports::client::{Connection, Dispatch, Proxy}; use crate::event_loop::state::SctkState; -use crate::platform_specific::wayland::SctkEvent; + +#[derive(Debug)] +pub enum CornerRadiusWrapper { + Xdg(CosmicCornerRadiusToplevelV1), + Wlr(CosmicCornerRadiusLayerV1), +} + +impl Drop for CornerRadiusWrapper { + fn drop(&mut self) { + match self { + Self::Xdg(c) => c.destroy(), + Self::Wlr(c) => c.destroy(), + }; + } +} impl Dispatch for SctkState { fn event( @@ -21,15 +33,11 @@ impl Dispatch for SctkState { _data: &(), _conn: &Connection, _qhandle: &sctk::reexports::client::QueueHandle, - ) {} + ) { + } } -impl - Dispatch< - CosmicCornerRadiusToplevelV1, - (), - > for SctkState -{ +impl Dispatch for SctkState { fn event( state: &mut Self, _proxy: &CosmicCornerRadiusToplevelV1, @@ -39,7 +47,22 @@ impl _qhandle: &sctk::reexports::client::QueueHandle, ) { match event { - _ => unimplemented!() + _ => unimplemented!(), + } + } +} + +impl Dispatch for SctkState { + fn event( + state: &mut Self, + _proxy: &CosmicCornerRadiusLayerV1, + event: ::Event, + _data: &(), + _conn: &Connection, + _qhandle: &sctk::reexports::client::QueueHandle, + ) { + match event { + _ => unimplemented!(), } } } diff --git a/winit/src/platform_specific/wayland/handlers/shell/layer.rs b/winit/src/platform_specific/wayland/handlers/shell/layer.rs index 65e5724b..5e6bad57 100644 --- a/winit/src/platform_specific/wayland/handlers/shell/layer.rs +++ b/winit/src/platform_specific/wayland/handlers/shell/layer.rs @@ -1,13 +1,13 @@ use crate::platform_specific::wayland::{ - event_loop::state::{receive_frame, SctkState}, + event_loop::state::{SctkState, receive_frame}, sctk_event::{LayerSurfaceEventVariant, SctkEvent}, }; use cctk::sctk::{ delegate_layer, reexports::client::Proxy, shell::{ - wlr_layer::{Anchor, KeyboardInteractivity, LayerShellHandler}, WaylandSurface, + wlr_layer::{Anchor, KeyboardInteractivity, LayerShellHandler}, }, }; use std::fmt::Debug; @@ -27,6 +27,15 @@ impl LayerShellHandler for SctkState { None => return, }; + if let Some(id) = self.id_map.remove(&layer.surface.wl_surface().id()) { + if let Some(blurred) = self.blur_surfaces.remove(&id) { + blurred.destroy(); + } + _ = self.corner_radii.remove(&id); + + _ = self.destroyed.insert(id); + } + self.sctk_events.push(SctkEvent::LayerSurfaceEvent { variant: LayerSurfaceEventVariant::Done, id: layer.surface.wl_surface().clone(), diff --git a/winit/src/platform_specific/wayland/handlers/shell/mod.rs b/winit/src/platform_specific/wayland/handlers/shell/mod.rs index af8b5961..c4117eb7 100644 --- a/winit/src/platform_specific/wayland/handlers/shell/mod.rs +++ b/winit/src/platform_specific/wayland/handlers/shell/mod.rs @@ -1,4 +1,4 @@ +pub mod corner_radius; pub mod layer; pub mod xdg_popup; pub mod xdg_window; -pub mod corner_radius; diff --git a/winit/src/platform_specific/wayland/handlers/shell/xdg_popup.rs b/winit/src/platform_specific/wayland/handlers/shell/xdg_popup.rs index 55bf2b87..4d11aac8 100644 --- a/winit/src/platform_specific/wayland/handlers/shell/xdg_popup.rs +++ b/winit/src/platform_specific/wayland/handlers/shell/xdg_popup.rs @@ -1,6 +1,6 @@ use crate::event_loop::state::receive_frame; use crate::platform_specific::wayland::{ - event_loop::state::{self, PopupParent, SctkState}, + event_loop::state::{PopupParent, SctkState}, sctk_event::{PopupEventVariant, SctkEvent}, }; use cctk::sctk::{ @@ -18,11 +18,11 @@ impl PopupHandler for SctkState { configure: cctk::sctk::shell::xdg::popup::PopupConfigure, ) { self.request_redraw(popup.wl_surface()); - let sctk_popup = match self.popups.iter_mut().find(|s| { - s.popup.wl_surface().clone() == popup.wl_surface().clone() - }) { + let sctk_popup = match self.popmgr.popup_mut(popup.wl_surface()) { Some(p) => p, - None => return, + None => { + return; + } }; let first = sctk_popup.last_configure.is_none(); _ = sctk_popup.last_configure.replace(configure.clone()); @@ -38,6 +38,7 @@ impl PopupHandler for SctkState { first, ), id: popup.wl_surface().clone(), + parent_window: sctk_popup.data.parent_window, toplevel_id: sctk_popup.data.toplevel.clone(), parent_id: match &sctk_popup.data.parent { PopupParent::LayerSurface(s) => s.clone(), @@ -53,42 +54,24 @@ impl PopupHandler for SctkState { _qh: &cctk::sctk::reexports::client::QueueHandle, popup: &cctk::sctk::shell::xdg::popup::Popup, ) { - let sctk_popup = match self.popups.iter().position(|s| { - s.popup.wl_surface().clone() == popup.wl_surface().clone() - }) { - Some(p) => self.popups.remove(p), - None => return, + let Some(to_destroy) = self.popmgr.remove(popup.wl_surface()) else { + return; }; - let mut to_destroy = vec![sctk_popup]; - while let Some(popup_to_destroy) = to_destroy.last() { - match popup_to_destroy.data.parent.clone() { - state::PopupParent::LayerSurface(_) - | state::PopupParent::Window(_) => { - break; - } - state::PopupParent::Popup(popup_to_destroy_first) => { - let Some(popup_to_destroy_first) = - self.popups.iter().position(|p| { - p.popup.wl_surface() == &popup_to_destroy_first - }) - else { - log::warn!("could not find popup to destroy first."); - return; - }; - let popup_to_destroy_first = - self.popups.remove(popup_to_destroy_first); - to_destroy.push(popup_to_destroy_first); - } - } - } - for popup in to_destroy.into_iter().rev() { + + for popup in to_destroy { if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id()) { + if let Some(blurred) = self.blur_surfaces.remove(&id) { + blurred.destroy(); + } + _ = self.corner_radii.remove(&id); + _ = self.destroyed.insert(id); } self.sctk_events.push(SctkEvent::PopupEvent { variant: PopupEventVariant::Done, + parent_window: popup.data.parent_window, toplevel_id: popup.data.toplevel.clone(), parent_id: popup.data.parent.wl_surface().clone(), id: popup.popup.wl_surface().clone(), diff --git a/winit/src/platform_specific/wayland/handlers/wp_viewporter.rs b/winit/src/platform_specific/wayland/handlers/wp_viewporter.rs index 5cfe28d7..7bbd66d1 100644 --- a/winit/src/platform_specific/wayland/handlers/wp_viewporter.rs +++ b/winit/src/platform_specific/wayland/handlers/wp_viewporter.rs @@ -1,10 +1,10 @@ //! Handling of the wp-viewporter. +use cctk::sctk::reexports::client::Dispatch; use cctk::sctk::reexports::client::globals::{BindError, GlobalList}; use cctk::sctk::reexports::client::protocol::wl_surface::WlSurface; -use cctk::sctk::reexports::client::Dispatch; use cctk::sctk::reexports::client::{ - delegate_dispatch, Connection, Proxy, QueueHandle, + Connection, Proxy, QueueHandle, delegate_dispatch, }; use cctk::sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport; use cctk::sctk::reexports::protocols::wp::viewporter::client::wp_viewporter::WpViewporter; diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs index 9b3a7ec0..5b41e1c5 100644 --- a/winit/src/platform_specific/wayland/mod.rs +++ b/winit/src/platform_specific/wayland/mod.rs @@ -20,7 +20,8 @@ use iced_graphics::{Compositor, compositor}; use iced_runtime::core::{Vector, window}; use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle}; use raw_window_handle::{HasRawDisplayHandle, RawWindowHandle}; -use sctk_event::SctkEvent; +use sctk_event::{PopupEventVariant, SctkEvent}; +use std::sync::OnceLock; use std::{collections::HashMap, sync::Arc}; use subsurface_widget::{SubsurfaceInstance, SubsurfaceState}; use wayland_backend::client::ObjectId; @@ -31,7 +32,8 @@ use winit::window::ImePurpose; pub(crate) enum Action { Action(iced_runtime::platform_specific::wayland::Action), - SetCursor(CursorIcon), + SetCursor(ObjectId, CursorIcon), + SetCursorVisible(ObjectId, bool), RequestRedraw(ObjectId), TrackWindow(Arc, window::Id), ResizeWindow(window::Id), @@ -47,9 +49,14 @@ impl std::fmt::Debug for Action { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Action(arg0) => f.debug_tuple("Action").field(arg0).finish(), - Self::SetCursor(arg0) => { - f.debug_tuple("SetCursor").field(arg0).finish() + Self::SetCursor(arg0, arg1) => { + f.debug_tuple("SetCursor").field(arg0).field(arg1).finish() } + Self::SetCursorVisible(arg0, arg1) => f + .debug_tuple("SetCursorVisible") + .field(arg0) + .field(arg1) + .finish(), Self::RequestRedraw(arg0) => { f.debug_tuple("RequestRedraw").field(arg0).finish() } @@ -96,6 +103,7 @@ pub(crate) struct WaylandSpecific { surface_ids: HashMap, subsurface_state: Option, surface_subsurfaces: HashMap>, + popup_toplevels: HashMap, } impl PlatformSpecific { @@ -128,7 +136,6 @@ impl PlatformSpecific { }; self.wayland.display_handle = Some(display); self.wayland.proxy = Some(raw); - // TODO remove this self.wayland.sender = crate::platform_specific::event_loop::SctkEventLoop::new( self.wayland.winit_event_sender.clone().unwrap(), @@ -167,6 +174,11 @@ impl WaylandSpecific { self.conn.as_ref() } + /// Whether a popup opened from `toplevel` is alive. + pub(crate) fn has_popup(&self, toplevel: window::Id) -> bool { + self.popup_toplevels.values().any(|id| *id == toplevel) + } + pub(crate) async fn handle_event<'a, 'b, P>( &mut self, e: SctkEvent, @@ -195,8 +207,33 @@ impl WaylandSpecific { modifiers, subsurface_state, surface_subsurfaces, + popup_toplevels, } = self; + if let SctkEvent::PopupEvent { + variant, + parent_id, + parent_window, + id, + .. + } = &e + { + match variant { + PopupEventVariant::Created(..) => { + // A nested popup's parent is itself a popup so inhreit its toplevel + let toplevel = popup_toplevels + .get(&parent_id.id()) + .copied() + .unwrap_or(*parent_window); + _ = popup_toplevels.insert(id.id(), toplevel); + } + PopupEventVariant::Done => { + _ = popup_toplevels.remove(&id.id()); + } + _ => {} + } + } + match e { sctk_event => { let Some(sender) = sender.as_ref() else { diff --git a/winit/src/platform_specific/wayland/sctk_event.rs b/winit/src/platform_specific/wayland/sctk_event.rs index 8845b0c9..b96f1765 100755 --- a/winit/src/platform_specific/wayland/sctk_event.rs +++ b/winit/src/platform_specific/wayland/sctk_event.rs @@ -68,10 +68,8 @@ use cctk::{ xdg::{popup::PopupConfigure, window::WindowConfigure}, }, }, - wayland_client::protocol::wl_subsurface::WlSubsurface, }; use std::{ - any::Any, collections::HashMap, num::NonZeroU32, sync::{Arc, Mutex}, @@ -98,9 +96,7 @@ use super::{ #[derive(Debug, Clone)] pub enum SctkEvent { - // - // Input events - // + Blur, SeatEvent { variant: SeatEventVariant, id: WlSeat, @@ -122,11 +118,6 @@ pub enum SctkEvent { seat_id: WlSeat, surface: WlSurface, }, - // TODO data device & touch - - // - // Surface Events - // WindowEvent { variant: WindowEventVariant, id: WlSurface, @@ -164,13 +155,12 @@ pub enum SctkEvent { parent_id: WlSurface, /// the id of this popup id: WlSurface, + /// Window id of the parent surface, as passed in the popup settings. Needed to route + /// `Done` to the parent's widgets. + parent_window: SurfaceId, }, SubsurfaceEvent(SubsurfaceEventVariant), - - // - // output events - // NewOutput { id: WlOutput, info: Option, @@ -180,9 +170,7 @@ pub enum SctkEvent { info: OutputInfo, }, RemovedOutput(WlOutput), - // - // compositor events - // + ScaleFactorChanged { factor: f64, id: WlOutput, @@ -210,6 +198,10 @@ pub enum SctkEvent { SessionUnlocked, SurfaceScaleFactorChanged(f64, WlSurface, window::Id), Winit(WindowId, WindowEvent), + PopupModifiers { + toplevel: window::Id, + modifiers: cctk::sctk::seat::keyboard::Modifiers, + }, Subcompositor(SubsurfaceState), ShortcutsInhibited(bool), } @@ -635,7 +627,12 @@ impl SctkEvent { keyboard::Event::KeyPressed { key: key.clone(), location: location, - text: ke.utf8.map(|s| s.into()), + // sctk reports keys without a text representation (e.g. arrows, Home) as + // Some("") but winit reports None. Widgets are written against winit's contract. + text: ke + .utf8 + .filter(|s| !s.is_empty()) + .map(|s| s.into()), modifiers: modifiers_to_native(*modifiers), physical_key, repeat: false, @@ -661,7 +658,9 @@ impl SctkEvent { keyboard::Event::KeyPressed { key: key.clone(), location: location, - text: utf8.map(|s| s.into()), + text: utf8 + .filter(|s| !s.is_empty()) + .map(|s| s.into()), modifiers: modifiers_to_native(*modifiers), physical_key, repeat: true, @@ -935,6 +934,7 @@ impl SctkEvent { SctkEvent::PopupEvent { variant, id: surface, + parent_window, .. } => { match variant { @@ -973,6 +973,8 @@ impl SctkEvent { ) }) { + // tell the parent's widgets so menus can reset when the compositor dismisses them. + events.push((Some(parent_window), e.1.clone())); events.push(e) } } @@ -1409,6 +1411,19 @@ impl SctkEvent { ), )), SctkEvent::Winit(_, _) => {} + SctkEvent::PopupModifiers { + toplevel, + modifiers, + } => { + events.push(( + Some(toplevel), + iced_runtime::core::Event::Keyboard( + keyboard::Event::ModifiersChanged(modifiers_to_native( + modifiers, + )), + ), + )); + } SctkEvent::SurfaceScaleFactorChanged(scale, _, id) => { if let Some(w) = window_manager.get_mut(id) { w.state.update_scale_factor(scale); @@ -1738,6 +1753,12 @@ impl SctkEvent { ), ), )), + SctkEvent::Blur => events.push(( + None, + iced_runtime::core::Event::PlatformSpecific( + PlatformSpecific::Wayland(wayland::Event::BlurEnabled), + ), + )), } } } diff --git a/winit/src/platform_specific/wayland/subsurface_widget.rs b/winit/src/platform_specific/wayland/subsurface_widget.rs index dbecc52d..c3173fa9 100644 --- a/winit/src/platform_specific/wayland/subsurface_widget.rs +++ b/winit/src/platform_specific/wayland/subsurface_widget.rs @@ -409,7 +409,10 @@ impl SubsurfaceState { .unwrap(); canvas[0..width as usize * height as usize * 4].copy_from_slice(data); surface.damage_buffer(0, 0, width as i32, height as i32); - buffer.attach_to(&surface); + if let Err(err) = buffer.attach_to(&surface) { + log::warn!("failed to attach shm buffer to subsurface: {err}"); + return; + } surface.offset(offset.x as i32, offset.y as i32); wp_viewport.set_destination( (width as f64 / scale) as i32, diff --git a/winit/src/platform_specific/wayland/winit_window.rs b/winit/src/platform_specific/wayland/winit_window.rs index b33b7f9e..2751b31c 100644 --- a/winit/src/platform_specific/wayland/winit_window.rs +++ b/winit/src/platform_specific/wayland/winit_window.rs @@ -83,7 +83,10 @@ impl winit::window::Window for SctkWinitWindow { fn set_cursor(&self, cursor: winit_core::cursor::Cursor) { match cursor { winit_core::cursor::Cursor::Icon(icon) => { - _ = self.tx.send(Action::SetCursor(icon)); + _ = self.tx.send(Action::SetCursor( + self.surface.wl_surface().id(), + icon, + )); } winit_core::cursor::Cursor::Custom(_) => { // TODO @@ -92,7 +95,10 @@ impl winit::window::Window for SctkWinitWindow { } fn set_cursor_visible(&self, visible: bool) { - // TODO + _ = self.tx.send(Action::SetCursorVisible( + self.surface.wl_surface().id(), + visible, + )); } fn surface_size(&self) -> winit::dpi::PhysicalSize { diff --git a/winit/src/window.rs b/winit/src/window.rs index 87d35a5a..453d59c4 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -191,6 +191,7 @@ where dyn FnMut( &dyn winit::window::Window, &winit::event::WindowEvent, + mouse::Interaction, ) -> bool, >, >, @@ -291,11 +292,11 @@ where pub fn update_mouse(&mut self, interaction: mouse::Interaction) { if interaction != self.mouse_interaction { if let Some(icon) = conversion::mouse_interaction(interaction) { - self.raw.set_cursor(icon.into()); - if self.mouse_interaction == mouse::Interaction::Hidden { self.raw.set_cursor_visible(true); } + + self.raw.set_cursor(icon.into()); } else { self.raw.set_cursor_visible(false); } @@ -335,19 +336,9 @@ where } if self.ime_state != Some((cursor, purpose)) { - // Specify only the bottom-left position of the cursor on Linux - // because fcitx5 doesn't work well with cursor areas of - // the top-left position and height. - // The candidate window hides the composing text (a.k.a. preedit). - let (cursor_y, cursor_height) = - if cfg!(not(any(target_os = "windows", target_os = "macos"))) { - (cursor.y + cursor.height, 0.0) - } else { - (cursor.y, cursor.height) - }; self.raw.set_ime_cursor_area( - LogicalPosition::new(cursor.x, cursor_y).into(), - LogicalSize::new(cursor.width, cursor_height).into(), + LogicalPosition::new(cursor.x, cursor.y).into(), + LogicalSize::new(cursor.width, cursor.height).into(), ); self.raw.set_ime_purpose(conversion::ime_purpose(purpose)); diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index 98fdf9ee..1e16fc51 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -97,6 +97,7 @@ where self.ready = ready; } + #[cfg(feature = "a11y")] pub(crate) fn set_a11y_ready(&mut self, ready: bool) { self.a11y_ready = ready; }