From a9d4216eb0faf6e765df17fd183705065ddfa02e Mon Sep 17 00:00:00 2001 From: Arthur Brussee Date: Wed, 31 Jul 2024 07:43:16 +0000 Subject: [PATCH] Upgrade winit to 0.30.2 (#4849) * Closes https://github.com/emilk/egui/issues/1918 * Closes https://github.com/emilk/egui/issues/4437 * Closes https://github.com/emilk/egui/issues/4709 * [x] I have followed the instructions in the PR template Hiya, I need new winit for a specific fix for a android_native_actvity. There are already two PRs, but both don't seem to have a lot of movement, or are entirely complete: https://github.com/emilk/egui/pull/4466 Seems to have gone stale & is missing some bits. https://github.com/emilk/egui/pull/4702 Also seems stale (if less so), and is missing a refactor to run_on_demand. I also *think* the accesskit integration has a mistake and can't be enabled. I've marked them as a co-author on this as I started from this branch. (I think! Haven't done that on git before...). Sorry for the wall of text but just dumping some details / thoughts here: - There's an issue with creating child windows in winit 0.30.1 and up on macOS. The multiple_viewports, "create immediate viewport" example crashes on anything later 0.30.1, with a stack overflow in unsafe code. I've create [a winit issue](https://github.com/rust-windowing/winit/issues/3800), it *might* already be fixed in 0.31.0 but I can't test as 0.31 will likely require another refactoring. For now I have just pinned things to 0.30.0 exatly. - Winit has deprecated run_on_demand, instead requiring the ApplicationHandler interface. In 0.31.0 run_on_demand is removed. I've refactored both the integration and the WinitApp trait to follow this pattern. I've left user_events a bit more opaque, as it seems 0.31.0 is doing a rework of UserEvents too. - I've used the new lazy init approach for access kit from this branch https://github.com/mwcampbell/egui/tree/accesskit-new-lazy-init and marked Matt as co-author, thanks Matt! - There was very similair but not quite the same code for run_and_return and run_and_exit. I've merged them, but looking at the github issues graveyard it seems vey finnicky. I *hope* this is more robust than before but it's a bit scary. - when receiving new_events this also used to check the redraw timing dictionary. That doesn't seem necesarry so left this out, but that is a slight behaviour change? - I have reeneabled serial_windows on macOS. I wondered whether it was fixed after this PR and does seem to be! However, even before this PR it seems to work, so maybe winit has sorted things out before that... Windows also works fine now without the extra hack. - I've done a very basic test of AccessKit on Windows and screen reader seems ok but I'm really not knowleadgable enough to say whether it's all good or not. - I've tested cargo tests & all examples on Windows & macOS, and ran a basic Android app. Still, testing native platforms is wel... hard so if anyone can test linux / iOs / older mac versions / windows 10 would probably be a good idea! - For consistencys sake I've made all event like functions in WinitApp return a `Result`. There's quite a bit of Ok-wrapping now, maybe too annoying? Not sure. Thank you for having a look! * [x] macOS * [x] Windows * [x] Wayland (thanks [SiebenCorgie](https://github.com/SiebenCorgie)) * [x] X11 (thanks [crumblingstatue](https://github.com/crumblingstatue)!, [SiebenCorgie](https://github.com/SiebenCorgie)) * [x] Fix "follow system theme" not working on initial startup (winit issue, pinning to 0.30.2 for now). * [x] Fix `request_repaint_after` --------- Co-authored-by: mwcampbell Co-authored-by: j-axa Co-authored-by: DataTriny Co-authored-by: Emil Ernerfeldt --- Cargo.lock | 669 +++++++++++------- Cargo.toml | 9 +- crates/eframe/Cargo.toml | 13 +- crates/eframe/src/epi.rs | 2 +- crates/eframe/src/native/epi_integration.rs | 32 +- .../eframe/src/native/event_loop_context.rs | 54 ++ crates/eframe/src/native/glow_integration.rs | 263 ++++--- crates/eframe/src/native/mod.rs | 1 + crates/eframe/src/native/run.rs | 517 ++++++-------- crates/eframe/src/native/wgpu_integration.rs | 272 ++++--- crates/eframe/src/native/winit_integration.rs | 71 +- crates/egui-wgpu/Cargo.toml | 4 +- crates/egui-winit/Cargo.toml | 10 +- crates/egui-winit/src/lib.rs | 150 ++-- crates/egui-winit/src/window_settings.rs | 16 +- crates/egui/Cargo.toml | 2 +- crates/egui/src/context.rs | 37 +- crates/egui/src/response.rs | 14 +- .../text_selection/label_text_selection.rs | 2 +- crates/egui/tests/accesskit.rs | 2 +- crates/egui_glow/Cargo.toml | 14 +- crates/egui_glow/examples/pure_glow.rs | 222 ++++-- crates/egui_glow/src/winit.rs | 4 +- deny.toml | 8 +- examples/serial_windows/src/main.rs | 11 - 25 files changed, 1275 insertions(+), 1124 deletions(-) create mode 100644 crates/eframe/src/native/event_loop_context.rs diff --git a/Cargo.lock b/Cargo.lock index 34cdf3a2cd8..d47e187e9ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,76 +20,94 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.12.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8410747ed85a17c4a1e9ed3f5a74d3e7bdcc876cf9a18ff40ae21d645997b2" +checksum = "e4700bdc115b306d6c43381c344dc307f03b7f0460c304e4892c309930322bd7" dependencies = [ "enumn", "serde", ] +[[package]] +name = "accesskit_atspi_common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1de72dc7093910a1284cef784b6b143bab0a34d67f6178e4fc3aaaf29a09f8b" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "thiserror", + "zvariant", +] + [[package]] name = "accesskit_consumer" -version = "0.16.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6" +checksum = "fe3a07a32ab5837ad83db3230ac490c8504c2cd5b90ac8c00db6535f6ed65d0b" dependencies = [ "accesskit", + "immutable-chunkmap", ] [[package]] name = "accesskit_macos" -version = "0.10.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7" +checksum = "a189d159c153ae0fce5f9eefdcfec4a27885f453ce5ef0ccf078f72a73c39d34" dependencies = [ "accesskit", "accesskit_consumer", - "objc2 0.3.0-beta.3.patch-leaks.3", + "objc2", + "objc2-app-kit", + "objc2-foundation", "once_cell", ] [[package]] name = "accesskit_unix" -version = "0.6.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8c9b4467d77cacfbc93cee9aa8e7822f6d527c774efdca5f8b3a5280c34847" +checksum = "b76c448cfd96d16131a9ad3ab786d06951eb341cdac1db908978ab010245a19d" dependencies = [ "accesskit", - "accesskit_consumer", + "accesskit_atspi_common", "async-channel", - "async-once-cell", + "async-executor", + "async-task", "atspi", - "futures-lite", - "once_cell", + "futures-lite 1.13.0", + "futures-util", "serde", "zbus", ] [[package]] name = "accesskit_windows" -version = "0.15.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b" +checksum = "682d8c4fb425606f97408e7577793f32e96310b646fa77662eb4216293eddc7f" dependencies = [ "accesskit", "accesskit_consumer", - "once_cell", "paste", "static_assertions", - "windows 0.48.0", + "windows 0.54.0", ] [[package]] name = "accesskit_winit" -version = "0.16.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67" +checksum = "9afbd6d598b7c035639ad2b664aa0edc94c93dc1fc3ebb4b40d8a95fcd43ffac" dependencies = [ "accesskit", "accesskit_macos", "accesskit_unix", "accesskit_windows", + "raw-window-handle 0.6.2", "winit", ] @@ -139,9 +157,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "android-activity" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", "bitflags 2.6.0", @@ -153,7 +171,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "thiserror", ] @@ -205,7 +223,7 @@ checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89" dependencies = [ "clipboard-win", "log", - "objc2 0.5.1", + "objc2", "objc2-app-kit", "objc2-foundation", "parking_lot", @@ -251,13 +269,14 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.9.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 2.5.3", + "event-listener-strategy", "futures-core", + "pin-project-lite", ] [[package]] @@ -270,7 +289,7 @@ dependencies = [ "async-task", "concurrent-queue", "fastrand 2.0.1", - "futures-lite", + "futures-lite 1.13.0", "slab", ] @@ -283,7 +302,7 @@ dependencies = [ "async-lock", "autocfg", "blocking", - "futures-lite", + "futures-lite 1.13.0", ] [[package]] @@ -296,7 +315,7 @@ dependencies = [ "autocfg", "cfg-if", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "log", "parking", "polling 2.8.0", @@ -315,12 +334,6 @@ dependencies = [ "event-listener 2.5.3", ] -[[package]] -name = "async-once-cell" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9338790e78aa95a416786ec8389546c4b6a1dfc3dc36071ed9518a9413a542eb" - [[package]] name = "async-process" version = "1.8.0" @@ -333,7 +346,7 @@ dependencies = [ "blocking", "cfg-if", "event-listener 3.0.0", - "futures-lite", + "futures-lite 1.13.0", "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -436,7 +449,7 @@ checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939" dependencies = [ "atspi-common", "atspi-proxies", - "futures-lite", + "futures-lite 1.13.0", "zbus", ] @@ -547,67 +560,26 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-sys" -version = "0.1.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" -dependencies = [ - "objc-sys 0.2.0-beta.2", -] - -[[package]] -name = "block-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dd7cf50912cddc06dc5ea7c08c5e81c1b2c842a70d19def1848d54c586fed92" -dependencies = [ - "objc-sys 0.3.3", -] - [[package]] name = "block2" -version = "0.2.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" -dependencies = [ - "block-sys 0.1.0-beta.1", - "objc2-encode 2.0.0-pre.2", -] - -[[package]] -name = "block2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" -dependencies = [ - "block-sys 0.2.0", - "objc2 0.4.1", -] - -[[package]] -name = "block2" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ff7d91d3c1d568065b06c899777d1e48dcf76103a672a0adbc238a7f247f1e" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2 0.5.1", + "objc2", ] [[package]] name = "blocking" -version = "1.4.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c4ef1f913d78636d78d538eec1f18de81e481f44b1be0a81060090530846e1" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ "async-channel", - "async-lock", "async-task", - "fastrand 2.0.1", "futures-io", - "futures-lite", + "futures-lite 2.3.0", "piper", - "tracing", ] [[package]] @@ -660,9 +632,9 @@ dependencies = [ [[package]] name = "calloop" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf" +checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ "bitflags 2.6.0", "log", @@ -728,6 +700,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "cgl" version = "0.3.2" @@ -883,9 +861,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -1169,6 +1147,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + [[package]] name = "ecolor" version = "0.28.1" @@ -1199,15 +1183,14 @@ dependencies = [ "image", "js-sys", "log", - "objc2 0.5.1", + "objc2", "objc2-app-kit", "objc2-foundation", "parking_lot", "percent-encoding", "pollster", "puffin", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.2", "ron", "serde", "static_assertions", @@ -1265,10 +1248,12 @@ dependencies = [ "document-features", "egui", "log", + "nix", "puffin", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.2", "serde", "smithay-clipboard", + "wayland-cursor", "web-time", "webbrowser", "winit", @@ -1347,7 +1332,6 @@ dependencies = [ "log", "memoffset 0.9.0", "puffin", - "raw-window-handle 0.5.2", "wasm-bindgen", "web-sys", "winit", @@ -1518,6 +1502,27 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + [[package]] name = "fancy-regex" version = "0.11.0" @@ -1646,6 +1651,27 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "futures-sink" version = "0.3.28" @@ -1666,6 +1692,7 @@ checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -1799,69 +1826,61 @@ dependencies = [ [[package]] name = "glutin" -version = "0.31.3" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" +checksum = "2491aa3090f682ddd920b184491844440fdd14379c7eef8f5bc10ef7fb3242fd" dependencies = [ "bitflags 2.6.0", - "cfg_aliases", + "cfg_aliases 0.2.1", "cgl", "core-foundation", "dispatch", "glutin_egl_sys", "glutin_glx_sys", - "glutin_wgl_sys 0.5.0", - "icrate", + "glutin_wgl_sys", "libloading", - "objc2 0.4.1", + "objc2", + "objc2-app-kit", + "objc2-foundation", "once_cell", - "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", "wayland-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "x11-dl", ] [[package]] name = "glutin-winit" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" +checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" dependencies = [ - "cfg_aliases", + "cfg_aliases 0.2.1", "glutin", - "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", "winit", ] [[package]] name = "glutin_egl_sys" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" +checksum = "cae99fff4d2850dbe6fb8c1fa8e4fead5525bab715beaacfccf3fb994e01c827" dependencies = [ "gl_generator", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "glutin_glx_sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" +checksum = "9c2b2d3918e76e18e08796b55eb64e8fe6ec67d5a6b2e2a7e2edce224ad24c63" dependencies = [ "gl_generator", "x11-dl", ] -[[package]] -name = "glutin_wgl_sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" -dependencies = [ - "gl_generator", -] - [[package]] name = "glutin_wgl_sys" version = "0.6.0" @@ -2059,7 +2078,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.51.1", ] [[package]] @@ -2071,17 +2090,6 @@ dependencies = [ "cc", ] -[[package]] -name = "icrate" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" -dependencies = [ - "block2 0.3.0", - "dispatch", - "objc2 0.4.1", -] - [[package]] name = "idna" version = "0.4.0" @@ -2124,6 +2132,15 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" +[[package]] +name = "immutable-chunkmap" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4419f022e55cc63d5bbd6b44b71e1d226b9c9480a47824c706e9d54e5c40c5eb" +dependencies = [ + "arrayvec", +] + [[package]] name = "indexmap" version = "2.1.0" @@ -2256,9 +2273,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.150" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" @@ -2445,7 +2462,7 @@ dependencies = [ "arrayvec", "bit-set 0.6.0", "bitflags 2.6.0", - "cfg_aliases", + "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", "indexmap", @@ -2459,17 +2476,16 @@ dependencies = [ [[package]] name = "ndk" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ "bitflags 2.6.0", "jni-sys", "log", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.2", "thiserror", ] @@ -2488,6 +2504,15 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + [[package]] name = "nix" version = "0.26.4" @@ -2568,99 +2593,205 @@ dependencies = [ [[package]] name = "objc-sys" -version = "0.2.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" - -[[package]] -name = "objc-sys" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da284c198fb9b7b0603f8635185e85fbd5b64ee154b1ed406d489077de2d6d60" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" [[package]] name = "objc2" -version = "0.3.0-beta.3.patch-leaks.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ - "block2 0.2.0-alpha.6", - "objc-sys 0.2.0-beta.2", - "objc2-encode 2.0.0-pre.2", + "objc-sys", + "objc2-encode", ] [[package]] -name = "objc2" -version = "0.4.1" +name = "objc2-app-kit" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "objc-sys 0.3.3", - "objc2-encode 3.0.0", + "bitflags 2.6.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", ] [[package]] -name = "objc2" -version = "0.5.1" +name = "objc2-cloud-kit" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b25e1034d0e636cd84707ccdaa9f81243d399196b8a773946dcffec0401659" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "objc-sys 0.3.3", - "objc2-encode 4.0.1", + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", ] [[package]] -name = "objc2-app-kit" -version = "0.2.0" +name = "objc2-contacts" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb79768a710a9a1798848179edb186d1af7e8a8679f369e4b8d201dd2a034047" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2 0.5.0", - "objc2 0.5.1", - "objc2-core-data", + "block2", + "objc2", "objc2-foundation", ] [[package]] name = "objc2-core-data" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e092bc42eaf30a08844e6a076938c60751225ec81431ab89f5d1ccd9f958d6c" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "block2 0.5.0", - "objc2 0.5.1", + "bitflags 2.6.0", + "block2", + "objc2", "objc2-foundation", ] [[package]] -name = "objc2-encode" -version = "2.0.0-pre.2" +name = "objc2-core-image" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "objc-sys 0.2.0-beta.2", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", ] [[package]] -name = "objc2-encode" -version = "3.0.0" +name = "objc2-core-location" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] [[package]] name = "objc2-encode" -version = "4.0.1" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88658da63e4cc2c8adb1262902cd6af51094df0488b760d6fd27194269c0950a" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" [[package]] name = "objc2-foundation" -version = "0.2.0" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfaefe14254871ea16c7d88968c0ff14ba554712a20d76421eec52f0a7fb8904" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "block2 0.5.0", - "objc2 0.5.1", + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", ] [[package]] @@ -2786,6 +2917,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -2980,18 +3131,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" dependencies = [ "memchr", ] [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4" dependencies = [ "memchr", ] @@ -3043,9 +3194,9 @@ checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "raw-window-handle" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" @@ -3334,9 +3485,9 @@ dependencies = [ [[package]] name = "sctk-adwaita" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" +checksum = "7555fcb4f753d095d734fdefebb0ad8c98478a21db500492d87c55913d3b0086" dependencies = [ "ab_glyph", "log", @@ -3471,9 +3622,9 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "smithay-client-toolkit" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" dependencies = [ "bitflags 2.6.0", "calloop", @@ -4118,13 +4269,13 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wayland-backend" -version = "0.3.2" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" +checksum = "f90e11ce2ca99c97b940ee83edbae9da2d56a08f9ea8158550fd77fa31722993" dependencies = [ "cc", "downcast-rs", - "nix", + "rustix 0.38.21", "scoped-tls", "smallvec", "wayland-sys", @@ -4132,12 +4283,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.1" +version = "0.31.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" +checksum = "7e321577a0a165911bdcfb39cf029302479d7527b517ee58ab0f6ad09edf0943" dependencies = [ "bitflags 2.6.0", - "nix", + "rustix 0.38.21", "wayland-backend", "wayland-scanner", ] @@ -4155,20 +4306,20 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.0" +version = "0.31.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b" +checksum = "6ef9489a8df197ebf3a8ce8a7a7f0a2320035c3743f3c1bd0bdbccf07ce64f95" dependencies = [ - "nix", + "rustix 0.38.21", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.31.0" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -4204,20 +4355,20 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.0" +version = "0.31.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" +checksum = "d7b56f89937f1cf2ee1f1259cf2936a17a1f45d8f0aa1019fae6d470d304cfa6" dependencies = [ "proc-macro2", - "quick-xml 0.30.0", + "quick-xml 0.34.0", "quote", ] [[package]] name = "wayland-sys" -version = "0.31.1" +version = "0.31.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148" dependencies = [ "dlib", "log", @@ -4237,12 +4388,11 @@ dependencies = [ [[package]] name = "web-time" -version = "0.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19353897b48e2c4d849a2d73cb0aeb16dc2be4e00c565abfc11eb65a806e47de" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", - "once_cell", "wasm-bindgen", ] @@ -4282,14 +4432,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87e07e87a179614940ad845397e03201847453a37b43a31a3b54eee2e6e32ce" dependencies = [ "arrayvec", - "cfg_aliases", + "cfg_aliases 0.1.1", "document-features", "js-sys", "log", "naga", "parking_lot", "profiling", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.2", "smallvec", "static_assertions", "wasm-bindgen", @@ -4309,7 +4459,7 @@ dependencies = [ "arrayvec", "bit-vec 0.7.0", "bitflags 2.6.0", - "cfg_aliases", + "cfg_aliases 0.1.1", "document-features", "indexmap", "log", @@ -4317,7 +4467,7 @@ dependencies = [ "once_cell", "parking_lot", "profiling", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.2", "rustc-hash", "smallvec", "thiserror", @@ -4336,10 +4486,10 @@ dependencies = [ "ash", "bitflags 2.6.0", "block", - "cfg_aliases", + "cfg_aliases 0.1.1", "core-graphics-types", "glow", - "glutin_wgl_sys 0.6.0", + "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", "gpu-descriptor", @@ -4351,12 +4501,12 @@ dependencies = [ "log", "metal", "naga", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", "parking_lot", "profiling", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.2", "renderdoc-sys", "rustc-hash", "smallvec", @@ -4417,23 +4567,24 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.48.0" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" dependencies = [ - "windows-implement", - "windows-interface", + "windows-core 0.51.1", "windows-targets 0.48.5", ] [[package]] name = "windows" -version = "0.51.1" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ - "windows-core", - "windows-targets 0.48.5", + "windows-core 0.54.0", + "windows-implement", + "windows-interface", + "windows-targets 0.52.5", ] [[package]] @@ -4445,26 +4596,45 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.5", +] + [[package]] name = "windows-implement" -version = "0.48.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" +checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] name = "windows-interface" -version = "0.48.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" +checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.5", ] [[package]] @@ -4674,38 +4844,41 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winit" -version = "0.29.10" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c824f11941eeae66ec71111cc2674373c772f482b58939bb4066b642aa2ffcf" +checksum = "1dc930d6cfbf53c4fe0b95689cdc2e17b8658c3f4214b9953298ccb5a1a15c90" dependencies = [ "ahash", "android-activity", "atomic-waker", "bitflags 2.6.0", + "block2", "bytemuck", "calloop", - "cfg_aliases", + "cfg_aliases 0.2.1", + "concurrent-queue", "core-foundation", "core-graphics", "cursor-icon", - "icrate", + "dpi", "js-sys", "libc", - "log", "memmap2", "ndk", - "ndk-sys", - "objc2 0.4.1", - "once_cell", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", "orbclient", "percent-encoding", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.0", - "redox_syscall 0.3.5", + "pin-project", + "raw-window-handle 0.6.2", + "redox_syscall 0.4.1", "rustix 0.38.21", "sctk-adwaita", "smithay-client-toolkit", "smol_str", + "tracing", "unicode-segmentation", "wasm-bindgen", "wasm-bindgen-futures", @@ -4715,7 +4888,7 @@ dependencies = [ "wayland-protocols-plasma", "web-sys", "web-time", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "x11-dl", "x11rb", "xkbcommon-dl", diff --git a/Cargo.toml b/Cargo.toml index 03d6f7c3652..f6b45737cd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,8 +73,8 @@ bytemuck = "1.7.2" criterion = { version = "0.5.1", default-features = false } document-features = " 0.2.8" glow = "0.13" -glutin = "0.31" -glutin-winit = "0.4" +glutin = "0.32.0" +glutin-winit = "0.5.0" image = { version = "0.25", default-features = false } log = { version = "0.4", features = ["std"] } nohash-hasher = "0.2" @@ -85,7 +85,7 @@ ron = "0.8" raw-window-handle = "0.6.0" serde = { version = "1", features = ["derive"] } thiserror = "1.0.37" -web-time = "0.2" # Timekeeping for native and web +web-time = "1.1.0" # Timekeeping for native and web wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" web-sys = "0.3.58" @@ -93,8 +93,9 @@ wgpu = { version = "22.0.0", default-features = false, features = [ # Make the renderer `Sync` even on wasm32, because it makes the code simpler: "fragile-send-sync-non-atomic-wasm", ] } -winit = { version = "0.29.4", default-features = false } +# Currently can't upgrade above 0.30.2 due to https://github.com/rust-windowing/winit/issues/3837 +winit = { version = "=0.30.2", default-features = false } [workspace.lints.rust] unsafe_code = "deny" diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 22907c0979a..0b18845c34a 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -59,14 +59,7 @@ android-native-activity = ["egui-winit/android-native-activity"] default_fonts = ["egui/default_fonts"] ## Use [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/master/crates/egui_glow). -glow = [ - "dep:egui_glow", - "dep:glow", - "dep:glutin-winit", - "dep:glutin", - "dep:rwh_05", - "winit/rwh_05", -] +glow = ["dep:egui_glow", "dep:glow", "dep:glutin-winit", "dep:glutin"] ## Enable saving app state to disk. persistence = [ @@ -141,10 +134,6 @@ web-time.workspace = true egui_glow = { workspace = true, optional = true, default-features = false } glow = { workspace = true, optional = true } -# glutin stuck on old version of raw-window-handle: -rwh_05 = { package = "raw-window-handle", version = "0.5.2", optional = true, features = [ - "std", -] } ron = { workspace = true, optional = true, features = ["integer128"] } serde = { workspace = true, optional = true } diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index c8c9069a0bf..ac6d0f45a24 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -23,7 +23,7 @@ use static_assertions::assert_not_impl_any; #[cfg(not(target_arch = "wasm32"))] #[cfg(any(feature = "glow", feature = "wgpu"))] -pub use winit::{event_loop::EventLoopBuilder, window::WindowBuilder}; +pub use winit::{event_loop::EventLoopBuilder, window::WindowAttributes}; /// Hook into the building of an event loop before it is run /// diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index c033eeef6ab..5d02e2386e9 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -3,7 +3,7 @@ use web_time::Instant; use std::path::PathBuf; -use winit::event_loop::EventLoopWindowTarget; +use winit::event_loop::ActiveEventLoop; use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _}; @@ -12,9 +12,9 @@ use egui_winit::{EventResponse, WindowSettings}; use crate::{epi, Theme}; -pub fn viewport_builder( +pub fn viewport_builder( egui_zoom_factor: f32, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, native_options: &mut epi::NativeOptions, window_settings: Option, ) -> ViewportBuilder { @@ -95,10 +95,7 @@ pub fn apply_window_settings( } } -fn largest_monitor_point_size( - egui_zoom_factor: f32, - event_loop: &EventLoopWindowTarget, -) -> egui::Vec2 { +fn largest_monitor_point_size(egui_zoom_factor: f32, event_loop: &ActiveEventLoop) -> egui::Vec2 { crate::profile_function!(); let mut max_size = egui::Vec2::ZERO; @@ -229,27 +226,6 @@ impl EpiIntegration { } } - #[cfg(feature = "accesskit")] - pub fn init_accesskit + Send>( - &self, - egui_winit: &mut egui_winit::State, - window: &winit::window::Window, - event_loop_proxy: winit::event_loop::EventLoopProxy, - ) { - crate::profile_function!(); - - let egui_ctx = self.egui_ctx.clone(); - egui_winit.init_accesskit(window, event_loop_proxy, move || { - // This function is called when an accessibility client - // (e.g. screen reader) makes its first request. If we got here, - // we know that an accessibility tree is actually wanted. - egui_ctx.enable_accesskit(); - // Enqueue a repaint so we'll receive a full tree update soon. - egui_ctx.request_repaint(); - egui_ctx.accesskit_placeholder_tree_update() - }); - } - /// If `true`, it is time to close the native window. pub fn should_close(&self) -> bool { self.close diff --git a/crates/eframe/src/native/event_loop_context.rs b/crates/eframe/src/native/event_loop_context.rs new file mode 100644 index 00000000000..2bb9e42555a --- /dev/null +++ b/crates/eframe/src/native/event_loop_context.rs @@ -0,0 +1,54 @@ +use std::cell::Cell; +use winit::event_loop::ActiveEventLoop; + +thread_local! { + static CURRENT_EVENT_LOOP: Cell> = Cell::new(None); +} + +struct EventLoopGuard; + +impl EventLoopGuard { + fn new(event_loop: &ActiveEventLoop) -> Self { + CURRENT_EVENT_LOOP.with(|cell| { + assert!( + cell.get().is_none(), + "Attempted to set a new event loop while one is already set" + ); + cell.set(Some(event_loop as *const ActiveEventLoop)); + }); + Self + } +} + +impl Drop for EventLoopGuard { + fn drop(&mut self) { + CURRENT_EVENT_LOOP.with(|cell| cell.set(None)); + } +} + +// Helper function to safely use the current event loop +#[allow(unsafe_code)] +pub fn with_current_event_loop(f: F) -> Option +where + F: FnOnce(&ActiveEventLoop) -> R, +{ + CURRENT_EVENT_LOOP.with(|cell| { + cell.get().map(|ptr| { + // SAFETY: + // 1. The pointer is guaranteed to be valid when it's Some, as the EventLoopGuard that created it + // lives at least as long as the reference, and clears it when it's dropped. Only run_with_event_loop creates + // a new EventLoopGuard, and does not leak it. + // 2. Since the pointer was created from a borrow which lives at least as long as this pointer there are + // no mutable references to the ActiveEventLoop. + let event_loop = unsafe { &*ptr }; + f(event_loop) + }) + }) +} + +// The only public interface to use the event loop +pub fn with_event_loop_context(event_loop: &ActiveEventLoop, f: impl FnOnce()) { + // NOTE: For safety, this guard must NOT be leaked. + let _guard = EventLoopGuard::new(event_loop); + f(); +} diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index fd19f633fde..366b71cfd7a 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -21,8 +21,9 @@ use glutin::{ prelude::{GlDisplay, PossiblyCurrentGlContext}, surface::GlSurface, }; +use raw_window_handle::HasWindowHandle; use winit::{ - event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}, + event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy}, window::{Window, WindowId}, }; @@ -145,7 +146,7 @@ impl GlowWinitApp { #[allow(unsafe_code)] fn create_glutin_windowed_context( egui_ctx: &egui::Context, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, storage: Option<&dyn Storage>, native_options: &mut NativeOptions, ) -> Result<(GlutinWindowContext, egui_glow::Painter)> { @@ -194,10 +195,7 @@ impl GlowWinitApp { Ok((glutin_window_context, painter)) } - fn init_run_state( - &mut self, - event_loop: &EventLoopWindowTarget, - ) -> Result<&mut GlowWinitRunning> { + fn init_run_state(&mut self, event_loop: &ActiveEventLoop) -> Result<&mut GlowWinitRunning> { crate::profile_function!(); let storage = if let Some(file) = &self.native_options.persistence_path { @@ -279,7 +277,7 @@ impl GlowWinitApp { .. } = viewport { - integration.init_accesskit(egui_winit, window, event_loop_proxy); + egui_winit.init_accesskit(window, event_loop_proxy); } } @@ -330,17 +328,9 @@ impl GlowWinitApp { let painter = Rc::downgrade(&painter); let beginning = integration.beginning; - let event_loop: *const EventLoopWindowTarget = event_loop; - egui::Context::set_immediate_viewport_renderer(move |egui_ctx, immediate_viewport| { if let (Some(glutin), Some(painter)) = (glutin.upgrade(), painter.upgrade()) { - // SAFETY: the event loop lives longer than - // the Rc:s we just upgraded above. - #[allow(unsafe_code)] - let event_loop = unsafe { event_loop.as_ref().unwrap() }; - render_immediate_viewport( - event_loop, egui_ctx, &glutin, &painter, @@ -401,116 +391,108 @@ impl WinitApp for GlowWinitApp { fn run_ui_and_paint( &mut self, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, window_id: WindowId, - ) -> EventResult { + ) -> Result { if let Some(running) = &mut self.running { running.run_ui_and_paint(event_loop, window_id) } else { - EventResult::Wait + Ok(EventResult::Wait) } } - fn on_event( - &mut self, - event_loop: &EventLoopWindowTarget, - event: &winit::event::Event, - ) -> Result { - crate::profile_function!(winit_integration::short_event_description(event)); - - Ok(match event { - winit::event::Event::Resumed => { - log::debug!("Event::Resumed"); - - let running = if let Some(running) = &mut self.running { - // Not the first resume event. Create all outstanding windows. - running - .glutin - .borrow_mut() - .initialize_all_windows(event_loop); - running - } else { - // First resume event. Created our root window etc. - self.init_run_state(event_loop)? - }; - let window_id = running.glutin.borrow().window_from_viewport[&ViewportId::ROOT]; - EventResult::RepaintNow(window_id) - } + fn resumed(&mut self, event_loop: &ActiveEventLoop) -> crate::Result { + log::debug!("Event::Resumed"); - winit::event::Event::Suspended => { - if let Some(running) = &mut self.running { - running.glutin.borrow_mut().on_suspend()?; - } - EventResult::Wait - } + let running = if let Some(running) = &mut self.running { + // Not the first resume event. Create all outstanding windows. + running + .glutin + .borrow_mut() + .initialize_all_windows(event_loop); + running + } else { + // First resume event. Create our root window etc. + self.init_run_state(event_loop)? + }; + let window_id = running.glutin.borrow().window_from_viewport[&ViewportId::ROOT]; + Ok(EventResult::RepaintNow(window_id)) + } - winit::event::Event::WindowEvent { event, window_id } => { - if let Some(running) = &mut self.running { - running.on_window_event(*window_id, event) - } else { - EventResult::Wait - } - } + fn suspended(&mut self, _: &ActiveEventLoop) -> crate::Result { + if let Some(running) = &mut self.running { + running.glutin.borrow_mut().on_suspend()?; + } + Ok(EventResult::Wait) + } - winit::event::Event::DeviceEvent { - device_id: _, - event: winit::event::DeviceEvent::MouseMotion { delta }, - } => { - if let Some(running) = &mut self.running { - let mut glutin = running.glutin.borrow_mut(); - if let Some(viewport) = glutin - .focused_viewport - .and_then(|viewport| glutin.viewports.get_mut(&viewport)) - { - if let Some(egui_winit) = viewport.egui_winit.as_mut() { - egui_winit.on_mouse_motion(*delta); - } + fn device_event( + &mut self, + _: &ActiveEventLoop, + _: winit::event::DeviceId, + event: winit::event::DeviceEvent, + ) -> crate::Result { + if let winit::event::DeviceEvent::MouseMotion { delta } = event { + if let Some(running) = &mut self.running { + let mut glutin = running.glutin.borrow_mut(); + if let Some(viewport) = glutin + .focused_viewport + .and_then(|viewport| glutin.viewports.get_mut(&viewport)) + { + if let Some(egui_winit) = viewport.egui_winit.as_mut() { + egui_winit.on_mouse_motion(delta); + } - if let Some(window) = viewport.window.as_ref() { - EventResult::RepaintNext(window.id()) - } else { - EventResult::Wait - } - } else { - EventResult::Wait + if let Some(window) = viewport.window.as_ref() { + return Ok(EventResult::RepaintNext(window.id())); } - } else { - EventResult::Wait } } + } - #[cfg(feature = "accesskit")] - winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest( - accesskit_winit::ActionRequestEvent { request, window_id }, - )) => { - if let Some(running) = &self.running { - let mut glutin = running.glutin.borrow_mut(); - if let Some(viewport_id) = glutin.viewport_from_window.get(window_id).copied() { - if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) { - if let Some(egui_winit) = &mut viewport.egui_winit { - crate::profile_scope!("on_accesskit_action_request"); - egui_winit.on_accesskit_action_request(request.clone()); - } - } + Ok(EventResult::Wait) + } + + fn window_event( + &mut self, + _: &ActiveEventLoop, + window_id: WindowId, + event: winit::event::WindowEvent, + ) -> Result { + if let Some(running) = &mut self.running { + Ok(running.on_window_event(window_id, &event)) + } else { + Ok(EventResult::Wait) + } + } + + #[cfg(feature = "accesskit")] + fn on_accesskit_event(&mut self, event: accesskit_winit::Event) -> crate::Result { + if let Some(running) = &self.running { + let mut glutin = running.glutin.borrow_mut(); + if let Some(viewport_id) = glutin.viewport_from_window.get(&event.window_id).copied() { + if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) { + if let Some(egui_winit) = &mut viewport.egui_winit { + return Ok(winit_integration::on_accesskit_window_event( + egui_winit, + event.window_id, + &event.window_event, + )); } - // As a form of user input, accessibility actions should - // lead to a repaint. - EventResult::RepaintNext(*window_id) - } else { - EventResult::Wait } } - _ => EventResult::Wait, - }) + } + + Ok(EventResult::Wait) } } impl GlowWinitRunning { fn run_ui_and_paint( &mut self, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, window_id: WindowId, - ) -> EventResult { + ) -> Result { crate::profile_function!(); let Some(viewport_id) = self @@ -520,7 +502,7 @@ impl GlowWinitRunning { .get(&window_id) .copied() else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; #[cfg(feature = "puffin")] @@ -538,10 +520,10 @@ impl GlowWinitRunning { // That means that the viewport cannot be rendered by itself and needs his parent to be rendered. if let Some(parent_viewport) = glutin.viewports.get(&viewport.ids.parent) { if let Some(window) = parent_viewport.window.as_ref() { - return EventResult::RepaintNext(window.id()); + return Ok(EventResult::RepaintNext(window.id())); } } - return EventResult::Wait; + return Ok(EventResult::Wait); } } @@ -549,15 +531,15 @@ impl GlowWinitRunning { let mut glutin = self.glutin.borrow_mut(); let egui_ctx = glutin.egui_ctx.clone(); let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; let Some(window) = viewport.window.as_ref() else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; egui_winit::update_viewport_info(&mut viewport.info, &egui_ctx, window, false); let Some(egui_winit) = viewport.egui_winit.as_mut() else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; let mut raw_input = egui_winit.take_egui_input(window); let viewport_ui_cb = viewport.viewport_ui_cb.clone(); @@ -593,10 +575,10 @@ impl GlowWinitRunning { } = &mut *glutin; let viewport = &viewports[&viewport_id]; let Some(window) = viewport.window.as_ref() else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; let Some(gl_surface) = viewport.gl_surface.as_ref() else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); @@ -651,7 +633,7 @@ impl GlowWinitRunning { } = &mut *glutin; let Some(viewport) = viewports.get_mut(&viewport_id) else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; viewport.info.events.clear(); // they should have been processed @@ -723,13 +705,13 @@ impl GlowWinitRunning { // vsync - don't count as frame-time: frame_timer.pause(); crate::profile_scope!("swap_buffers"); - if let Err(err) = gl_surface.swap_buffers( - current_gl_context - .as_ref() - .expect("failed to get current context to swap buffers"), - ) { - log::error!("swap_buffers failed: {err}"); - } + let context = current_gl_context + .as_ref() + .ok_or(egui_glow::PainterError::from( + "failed to get current context to swap buffers".to_owned(), + ))?; + + gl_surface.swap_buffers(context)?; frame_timer.resume(); } @@ -755,9 +737,9 @@ impl GlowWinitRunning { } if integration.should_close() { - EventResult::Exit + Ok(EventResult::Exit) } else { - EventResult::Wait + Ok(EventResult::Wait) } } @@ -766,8 +748,6 @@ impl GlowWinitRunning { window_id: WindowId, event: &winit::event::WindowEvent, ) -> EventResult { - crate::profile_function!(egui_winit::short_window_event_description(event)); - let mut glutin = self.glutin.borrow_mut(); let viewport_id = glutin.viewport_from_window.get(&window_id).copied(); @@ -908,7 +888,7 @@ impl GlutinWindowContext { egui_ctx: &egui::Context, viewport_builder: ViewportBuilder, native_options: &NativeOptions, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, ) -> Result { crate::profile_function!(); @@ -957,7 +937,7 @@ impl GlutinWindowContext { let display_builder = glutin_winit::DisplayBuilder::new() // we might want to expose this option to users in the future. maybe using an env var or using native_options. .with_preference(glutin_winit::ApiPreference::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 - .with_window_builder(Some(egui_winit::create_winit_window_builder( + .with_window_attributes(Some(egui_winit::create_winit_window_attributes( egui_ctx, event_loop, viewport_builder.clone(), @@ -993,8 +973,9 @@ impl GlutinWindowContext { gl_display.supported_features() ); let glutin_raw_window_handle = window.as_ref().map(|w| { - use rwh_05::HasRawWindowHandle as _; // glutin stuck on old version of raw-window-handle - w.raw_window_handle() + w.window_handle() + .expect("Failed to get window handle") + .as_raw() }); log::debug!("creating gl context using raw window handle: {glutin_raw_window_handle:?}"); @@ -1080,7 +1061,7 @@ impl GlutinWindowContext { /// Create a surface, window, and winit integration for all viewports lacking any of that. /// /// Errors will be logged. - fn initialize_all_windows(&mut self, event_loop: &EventLoopWindowTarget) { + fn initialize_all_windows(&mut self, event_loop: &ActiveEventLoop) { crate::profile_function!(); let viewports: Vec = self.viewports.keys().copied().collect(); @@ -1097,7 +1078,7 @@ impl GlutinWindowContext { pub(crate) fn initialize_window( &mut self, viewport_id: ViewportId, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, ) -> Result { crate::profile_function!(); @@ -1110,17 +1091,18 @@ impl GlutinWindowContext { window } else { log::debug!("Creating a window for viewport {viewport_id:?}"); - let window_builder = egui_winit::create_winit_window_builder( + let window_attributes = egui_winit::create_winit_window_attributes( &self.egui_ctx, event_loop, viewport.builder.clone(), ); - if window_builder.transparent() && self.gl_config.supports_transparency() == Some(false) + if window_attributes.transparent() + && self.gl_config.supports_transparency() == Some(false) { log::error!("Cannot create transparent window: the GL config does not support it"); } let window = - glutin_winit::finalize_window(event_loop, window_builder, &self.gl_config)?; + glutin_winit::finalize_window(event_loop, window_attributes, &self.gl_config)?; egui_winit::apply_viewport_builder_to_window( &self.egui_ctx, &window, @@ -1150,9 +1132,15 @@ impl GlutinWindowContext { let width_px = NonZeroU32::new(width_px).unwrap_or(NonZeroU32::MIN); let height_px = NonZeroU32::new(height_px).unwrap_or(NonZeroU32::MIN); let surface_attributes = { - use rwh_05::HasRawWindowHandle as _; // glutin stuck on old version of raw-window-handle glutin::surface::SurfaceAttributesBuilder::::new() - .build(window.raw_window_handle(), width_px, height_px) + .build( + window + .window_handle() + .expect("Failed to get display handle") + .as_raw(), + width_px, + height_px, + ) }; log::trace!("creating surface with attributes: {surface_attributes:?}"); @@ -1267,7 +1255,7 @@ impl GlutinWindowContext { fn handle_viewport_output( &mut self, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, egui_ctx: &egui::Context, viewport_output: &ViewportIdMap, ) { @@ -1389,7 +1377,6 @@ fn initialize_or_update_viewport( /// This is called (via a callback) by user code to render immediate viewports, /// i.e. viewport that are directly nested inside a parent viewport. fn render_immediate_viewport( - event_loop: &EventLoopWindowTarget, egui_ctx: &egui::Context, glutin: &RefCell, painter: &RefCell, @@ -1417,7 +1404,11 @@ fn render_immediate_viewport( None, ); - if let Err(err) = glutin.initialize_window(viewport_id, event_loop) { + let ret = event_loop_context::with_current_event_loop(|event_loop| { + glutin.initialize_window(viewport_id, event_loop) + }); + + if let Some(Err(err)) = ret { log::error!( "Failed to initialize a window for immediate viewport {viewport_id:?}: {err}" ); @@ -1523,7 +1514,9 @@ fn render_immediate_viewport( egui_winit.handle_platform_output(window, platform_output); - glutin.handle_viewport_output(event_loop, egui_ctx, &viewport_output); + event_loop_context::with_current_event_loop(|event_loop| { + glutin.handle_viewport_output(event_loop, egui_ctx, &viewport_output); + }); } #[cfg(feature = "__screenshot")] diff --git a/crates/eframe/src/native/mod.rs b/crates/eframe/src/native/mod.rs index 5fadd561014..cc0bfd7fc56 100644 --- a/crates/eframe/src/native/mod.rs +++ b/crates/eframe/src/native/mod.rs @@ -1,5 +1,6 @@ mod app_icon; mod epi_integration; +mod event_loop_context; pub mod run; /// File storage which can be used by native backends. diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 6ba5486b678..2d3a9dbe2b0 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -1,36 +1,29 @@ use std::{cell::RefCell, time::Instant}; -use winit::event_loop::{EventLoop, EventLoopBuilder}; +use winit::{ + application::ApplicationHandler, + event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, + window::WindowId, +}; use ahash::HashMap; +use super::winit_integration::{UserEvent, WinitApp}; use crate::{ epi, - native::winit_integration::{short_event_description, EventResult}, + native::{event_loop_context, winit_integration::EventResult}, Result, }; -use super::winit_integration::{UserEvent, WinitApp}; - // ---------------------------------------------------------------------------- - -fn create_event_loop_builder( - native_options: &mut epi::NativeOptions, -) -> EventLoopBuilder { +fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result> { crate::profile_function!(); - let mut event_loop_builder = winit::event_loop::EventLoopBuilder::with_user_event(); + let mut builder = winit::event_loop::EventLoop::with_user_event(); if let Some(hook) = std::mem::take(&mut native_options.event_loop_builder) { - hook(&mut event_loop_builder); + hook(&mut builder); } - event_loop_builder -} - -fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result> { - crate::profile_function!(); - let mut builder = create_event_loop_builder(native_options); - crate::profile_scope!("EventLoopBuilder::build"); Ok(builder.build()?) } @@ -59,324 +52,276 @@ fn with_event_loop( }) } -#[cfg(not(target_os = "ios"))] -fn run_and_return(event_loop: &mut EventLoop, mut winit_app: impl WinitApp) -> Result { - use winit::{event_loop::ControlFlow, platform::run_on_demand::EventLoopExtRunOnDemand}; - - log::trace!("Entering the winit event loop (run_on_demand)…"); - - // When to repaint what window - let mut windows_next_repaint_times = HashMap::default(); - - let mut returned_result = Ok(()); - - event_loop.run_on_demand(|event, event_loop_window_target| { - crate::profile_scope!("winit_event", short_event_description(&event)); - - log::trace!("winit event: {event:?}"); +/// Wraps a [`WinitApp`] to implement [`ApplicationHandler`]. This handles redrawing, exit states, and +/// some events, but otherwise forwards events to the [`WinitApp`]. +struct WinitAppWrapper { + windows_next_repaint_times: HashMap, + winit_app: T, + return_result: Result<(), crate::Error>, + run_and_return: bool, +} - if matches!(event, winit::event::Event::AboutToWait) { - return; // early-out: don't trigger another wait +impl WinitAppWrapper { + fn new(winit_app: T, run_and_return: bool) -> Self { + Self { + windows_next_repaint_times: HashMap::default(), + winit_app, + return_result: Ok(()), + run_and_return, } + } - let event_result = match &event { - winit::event::Event::LoopExiting => { - // On Mac, Cmd-Q we get here and then `run_on_demand` doesn't return (despite its name), - // so we need to save state now: - log::debug!("Received Event::LoopExiting - saving app state…"); - winit_app.save_and_destroy(); - return; - } + fn handle_event_result( + &mut self, + event_loop: &ActiveEventLoop, + event_result: Result, + ) { + let mut exit = false; - winit::event::Event::WindowEvent { - event: winit::event::WindowEvent::RedrawRequested, - window_id, - } => { - windows_next_repaint_times.remove(window_id); - winit_app.run_ui_and_paint(event_loop_window_target, *window_id) - } + log::trace!("event_result: {event_result:?}"); - winit::event::Event::UserEvent(UserEvent::RequestRepaint { - when, - frame_nr, - viewport_id, - }) => { - let current_frame_nr = winit_app.frame_nr(*viewport_id); - if current_frame_nr == *frame_nr || current_frame_nr == *frame_nr + 1 { - log::trace!("UserEvent::RequestRepaint scheduling repaint at {when:?}"); - if let Some(window_id) = winit_app.window_id_from_viewport_id(*viewport_id) { - EventResult::RepaintAt(window_id, *when) + let combined_result = event_result.and_then(|event_result| { + match event_result { + EventResult::Wait => { + event_loop.set_control_flow(ControlFlow::Wait); + Ok(event_result) + } + EventResult::RepaintNow(window_id) => { + log::trace!("RepaintNow of {window_id:?}",); + + if cfg!(target_os = "windows") { + // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280 + self.windows_next_repaint_times.remove(&window_id); + self.winit_app.run_ui_and_paint(event_loop, window_id) } else { - EventResult::Wait + // Fix for https://github.com/emilk/egui/issues/2425 + self.windows_next_repaint_times + .insert(window_id, Instant::now()); + Ok(event_result) } - } else { - log::trace!("Got outdated UserEvent::RequestRepaint"); - EventResult::Wait // old request - we've already repainted } - } - - winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached { - .. - }) => { - log::trace!("Woke up to check next_repaint_time"); - EventResult::Wait - } - - event => match winit_app.on_event(event_loop_window_target, event) { - Ok(event_result) => { - log::trace!("event_result: {event_result:?}"); - event_result + EventResult::RepaintNext(window_id) => { + log::trace!("RepaintNext of {window_id:?}",); + self.windows_next_repaint_times + .insert(window_id, Instant::now()); + Ok(event_result) } - Err(err) => { - log::error!("Exiting because of error: {err} during event {event:?}"); - returned_result = Err(err); - EventResult::Exit + EventResult::RepaintAt(window_id, repaint_time) => { + self.windows_next_repaint_times.insert( + window_id, + self.windows_next_repaint_times + .get(&window_id) + .map_or(repaint_time, |last| (*last).min(repaint_time)), + ); + Ok(event_result) } - }, - }; - - match event_result { - EventResult::Wait => { - event_loop_window_target.set_control_flow(ControlFlow::Wait); - } - EventResult::RepaintNow(window_id) => { - log::trace!( - "RepaintNow of {window_id:?} caused by {}", - short_event_description(&event) - ); - if cfg!(target_os = "windows") { - // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280 - windows_next_repaint_times.remove(&window_id); - - winit_app.run_ui_and_paint(event_loop_window_target, window_id); - } else { - // Fix for https://github.com/emilk/egui/issues/2425 - windows_next_repaint_times.insert(window_id, Instant::now()); + EventResult::Exit => { + exit = true; + Ok(event_result) } } - EventResult::RepaintNext(window_id) => { - log::trace!( - "RepaintNext of {window_id:?} caused by {}", - short_event_description(&event) - ); - windows_next_repaint_times.insert(window_id, Instant::now()); - } - EventResult::RepaintAt(window_id, repaint_time) => { - windows_next_repaint_times.insert( - window_id, - windows_next_repaint_times - .get(&window_id) - .map_or(repaint_time, |last| (*last).min(repaint_time)), - ); - } - EventResult::Exit => { + }); + + if let Err(err) = combined_result { + log::error!("Exiting because of error: {err}"); + exit = true; + self.return_result = Err(err); + }; + + if exit { + if self.run_and_return { log::debug!("Asking to exit event loop…"); - winit_app.save_and_destroy(); - event_loop_window_target.exit(); - return; + event_loop.exit(); + } else { + log::debug!("Quitting - saving app state…"); + self.winit_app.save_and_destroy(); + + log::debug!("Exiting with return code 0"); + + #[allow(clippy::exit)] + std::process::exit(0); } } - let mut next_repaint_time = windows_next_repaint_times.values().min().copied(); + self.check_redraw_requests(event_loop); + } + + fn check_redraw_requests(&mut self, event_loop: &ActiveEventLoop) { + let mut next_repaint_time = self.windows_next_repaint_times.values().min().copied(); - windows_next_repaint_times.retain(|window_id, repaint_time| { - if Instant::now() < *repaint_time { - return true; // not yet ready - }; + self.windows_next_repaint_times + .retain(|window_id, repaint_time| { + if Instant::now() < *repaint_time { + return true; // not yet ready + }; - next_repaint_time = None; - event_loop_window_target.set_control_flow(ControlFlow::Poll); + next_repaint_time = None; + event_loop.set_control_flow(ControlFlow::Poll); - if let Some(window) = winit_app.window(*window_id) { - log::trace!("request_redraw for {window_id:?}"); - let is_minimized = window.is_minimized().unwrap_or(false); - if is_minimized { - false + if let Some(window) = self.winit_app.window(*window_id) { + log::trace!("request_redraw for {window_id:?}"); + let is_minimized = window.is_minimized().unwrap_or(false); + if is_minimized { + false + } else { + window.request_redraw(); + true + } } else { - window.request_redraw(); - true + log::trace!("No window found for {window_id:?}"); + false } - } else { - log::trace!("No window found for {window_id:?}"); - false - } - }); + }); if let Some(next_repaint_time) = next_repaint_time { - event_loop_window_target.set_control_flow(ControlFlow::WaitUntil(next_repaint_time)); - }; - })?; - - log::debug!("eframe window closed"); + // WaitUntil seems to not work on iOS + #[cfg(target_os = "ios")] + winit_app + .get_window_winit_id(ViewportId::ROOT) + .map(|window_id| { + winit_app + .window(window_id) + .map(|window| window.request_redraw()) + }); - drop(winit_app); - - // On Windows this clears out events so that we can later create another window. - // See https://github.com/emilk/egui/pull/1889 for details. - // - // Note that this approach may cause issues on macOS (emilk/egui#2768); therefore, - // we only apply this approach on Windows to minimize the affect. - #[cfg(target_os = "windows")] - { - event_loop - .run_on_demand(|_, event_loop_window_target| { - event_loop_window_target.exit(); - }) - .ok(); + event_loop.set_control_flow(ControlFlow::WaitUntil(next_repaint_time)); + }; } - - returned_result } -fn run_and_exit( - event_loop: EventLoop, - mut winit_app: impl WinitApp + 'static, -) -> Result { - use winit::event_loop::ControlFlow; - log::trace!("Entering the winit event loop (run)…"); +impl ApplicationHandler for WinitAppWrapper { + fn suspended(&mut self, event_loop: &ActiveEventLoop) { + crate::profile_function!("Event::Suspended"); - // When to repaint what window - let mut windows_next_repaint_times = HashMap::default(); + event_loop_context::with_event_loop_context(event_loop, move || { + let event_result = self.winit_app.suspended(event_loop); + self.handle_event_result(event_loop, event_result); + }); + } - event_loop.run(move |event, event_loop_window_target| { - crate::profile_scope!("winit_event", short_event_description(&event)); + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + crate::profile_function!("Event::Resumed"); - log::trace!("winit event: {event:?}"); + // Nb: Make sure this guard is dropped after this function returns. + event_loop_context::with_event_loop_context(event_loop, move || { + let event_result = self.winit_app.resumed(event_loop); + self.handle_event_result(event_loop, event_result); + }); + } - if matches!(event, winit::event::Event::AboutToWait) { - return; // early-out: don't trigger another wait - } + fn exiting(&mut self, event_loop: &ActiveEventLoop) { + // On Mac, Cmd-Q we get here and then `run_app_on_demand` doesn't return (despite its name), + // so we need to save state now: + log::debug!("Received Event::LoopExiting - saving app state…"); + event_loop_context::with_event_loop_context(event_loop, move || { + self.winit_app.save_and_destroy(); + }); + } - let event_result = match &event { - winit::event::Event::LoopExiting => { - log::debug!("Received Event::LoopExiting"); - EventResult::Exit - } + fn device_event( + &mut self, + event_loop: &ActiveEventLoop, + device_id: winit::event::DeviceId, + event: winit::event::DeviceEvent, + ) { + crate::profile_function!(egui_winit::short_device_event_description(&event)); + + // Nb: Make sure this guard is dropped after this function returns. + event_loop_context::with_event_loop_context(event_loop, move || { + let event_result = self.winit_app.device_event(event_loop, device_id, event); + self.handle_event_result(event_loop, event_result); + }); + } - winit::event::Event::WindowEvent { - event: winit::event::WindowEvent::RedrawRequested, - window_id, - } => { - windows_next_repaint_times.remove(window_id); - winit_app.run_ui_and_paint(event_loop_window_target, *window_id) - } + fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { + crate::profile_function!(match &event { + UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint", + #[cfg(feature = "accesskit")] + UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest", + }); - winit::event::Event::UserEvent(UserEvent::RequestRepaint { - when, - frame_nr, - viewport_id, - }) => { - let current_frame_nr = winit_app.frame_nr(*viewport_id); - if current_frame_nr == *frame_nr || current_frame_nr == *frame_nr + 1 { - if let Some(window_id) = winit_app.window_id_from_viewport_id(*viewport_id) { - EventResult::RepaintAt(window_id, *when) + event_loop_context::with_event_loop_context(event_loop, move || { + let event_result = match event { + UserEvent::RequestRepaint { + when, + frame_nr, + viewport_id, + } => { + let current_frame_nr = self.winit_app.frame_nr(viewport_id); + if current_frame_nr == frame_nr || current_frame_nr == frame_nr + 1 { + log::trace!("UserEvent::RequestRepaint scheduling repaint at {when:?}"); + if let Some(window_id) = + self.winit_app.window_id_from_viewport_id(viewport_id) + { + Ok(EventResult::RepaintAt(window_id, when)) + } else { + Ok(EventResult::Wait) + } } else { - EventResult::Wait + log::trace!("Got outdated UserEvent::RequestRepaint"); + Ok(EventResult::Wait) // old request - we've already repainted } - } else { - log::trace!("Got outdated UserEvent::RequestRepaint"); - EventResult::Wait // old request - we've already repainted } - } - - winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached { - .. - }) => { - log::trace!("Woke up to check next_repaint_time"); - EventResult::Wait - } - - event => match winit_app.on_event(event_loop_window_target, event) { - Ok(event_result) => { - log::trace!("event_result: {event_result:?}"); - event_result - } - Err(err) => { - panic!("eframe encountered a fatal error: {err} during event {event:?}"); + #[cfg(feature = "accesskit")] + UserEvent::AccessKitActionRequest(request) => { + self.winit_app.on_accesskit_event(request) } - }, - }; + }; + self.handle_event_result(event_loop, event_result); + }); + } - match event_result { - EventResult::Wait => { - event_loop_window_target.set_control_flow(ControlFlow::Wait); - } - EventResult::RepaintNow(window_id) => { - log::trace!("RepaintNow caused by {}", short_event_description(&event)); - if cfg!(target_os = "windows") { - // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280 - windows_next_repaint_times.remove(&window_id); + fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: winit::event::StartCause) { + if let winit::event::StartCause::ResumeTimeReached { .. } = cause { + log::trace!("Woke up to check next_repaint_time"); + } - winit_app.run_ui_and_paint(event_loop_window_target, window_id); - } else { - // Fix for https://github.com/emilk/egui/issues/2425 - windows_next_repaint_times.insert(window_id, Instant::now()); - } - } - EventResult::RepaintNext(window_id) => { - log::trace!("RepaintNext caused by {}", short_event_description(&event)); - windows_next_repaint_times.insert(window_id, Instant::now()); - } - EventResult::RepaintAt(window_id, repaint_time) => { - windows_next_repaint_times.insert( - window_id, - windows_next_repaint_times - .get(&window_id) - .map_or(repaint_time, |last| (*last).min(repaint_time)), - ); - } - EventResult::Exit => { - log::debug!("Quitting - saving app state…"); - winit_app.save_and_destroy(); + self.check_redraw_requests(event_loop); + } - log::debug!("Exiting with return code 0"); - #[allow(clippy::exit)] - std::process::exit(0); - } - } + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: winit::event::WindowEvent, + ) { + crate::profile_function!(egui_winit::short_window_event_description(&event)); + + // Nb: Make sure this guard is dropped after this function returns. + event_loop_context::with_event_loop_context(event_loop, move || { + let event_result = match event { + winit::event::WindowEvent::RedrawRequested => { + self.windows_next_repaint_times.remove(&window_id); + self.winit_app.run_ui_and_paint(event_loop, window_id) + } + _ => self.winit_app.window_event(event_loop, window_id, event), + }; - let mut next_repaint_time = windows_next_repaint_times.values().min().copied(); + self.handle_event_result(event_loop, event_result); + }); + } +} - windows_next_repaint_times.retain(|window_id, repaint_time| { - if Instant::now() < *repaint_time { - return true; // not yet ready - } +#[cfg(not(target_os = "ios"))] +fn run_and_return(event_loop: &mut EventLoop, winit_app: impl WinitApp) -> Result { + use winit::platform::run_on_demand::EventLoopExtRunOnDemand; - next_repaint_time = None; - event_loop_window_target.set_control_flow(ControlFlow::Poll); + log::trace!("Entering the winit event loop (run_app_on_demand)…"); - if let Some(window) = winit_app.window(*window_id) { - log::trace!("request_redraw for {window_id:?}"); - let is_minimized = window.is_minimized().unwrap_or(false); - if is_minimized { - false - } else { - window.request_redraw(); - true - } - } else { - log::trace!("No window found for {window_id:?}"); - false - } - }); + let mut app = WinitAppWrapper::new(winit_app, true); + event_loop.run_app_on_demand(&mut app)?; + log::debug!("eframe window closed"); + app.return_result +} - if let Some(next_repaint_time) = next_repaint_time { - // WaitUntil seems to not work on iOS - #[cfg(target_os = "ios")] - winit_app - .get_window_winit_id(ViewportId::ROOT) - .map(|window_id| { - winit_app - .window(window_id) - .map(|window| window.request_redraw()) - }); +fn run_and_exit(event_loop: EventLoop, winit_app: impl WinitApp + 'static) -> Result { + log::trace!("Entering the winit event loop (run_app)…"); - event_loop_window_target.set_control_flow(ControlFlow::WaitUntil(next_repaint_time)); - }; - })?; + // When to repaint what window + let mut app = WinitAppWrapper::new(winit_app, false); + event_loop.run_app(&mut app)?; log::debug!("winit event loop unexpectedly returned"); - Ok(()) } diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index fc3f6e9e134..74f4e957c70 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -11,7 +11,7 @@ use egui_winit::ActionRequested; use parking_lot::Mutex; use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _}; use winit::{ - event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}, + event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy}, window::{Window, WindowId}, }; @@ -22,10 +22,11 @@ use egui::{ }; #[cfg(feature = "accesskit")] use egui_winit::accesskit_winit; +use winit_integration::UserEvent; use crate::{ native::{epi_integration::EpiIntegration, winit_integration::EventResult}, - App, AppCreator, CreationContext, NativeOptions, Result, Storage, UserEvent, + App, AppCreator, CreationContext, NativeOptions, Result, Storage, }; use super::{winit_integration::WinitApp, *}; @@ -119,7 +120,7 @@ impl WgpuWinitApp { } /// Create a window for all viewports lacking one. - fn initialized_all_windows(&mut self, event_loop: &EventLoopWindowTarget) { + fn initialized_all_windows(&mut self, event_loop: &ActiveEventLoop) { let Some(running) = &mut self.running else { return; }; @@ -142,11 +143,7 @@ impl WgpuWinitApp { } #[cfg(target_os = "android")] - fn recreate_window( - &self, - event_loop: &EventLoopWindowTarget, - running: &WgpuWinitRunning, - ) { + fn recreate_window(&self, event_loop: &ActiveEventLoop, running: &WgpuWinitRunning) { let SharedState { egui_ctx, viewports, @@ -178,7 +175,7 @@ impl WgpuWinitApp { fn init_run_state( &mut self, egui_ctx: egui::Context, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, storage: Option>, window: Window, builder: ViewportBuilder, @@ -252,7 +249,7 @@ impl WgpuWinitApp { #[cfg(feature = "accesskit")] { let event_loop_proxy = self.repaint_proxy.lock().clone(); - integration.init_accesskit(&mut egui_winit, &window, event_loop_proxy); + egui_winit.init_accesskit(&window, event_loop_proxy); } let theme = system_theme.unwrap_or(self.native_options.default_theme); egui_ctx.set_visuals(theme.egui_visuals()); @@ -311,16 +308,9 @@ impl WgpuWinitApp { let shared = Rc::downgrade(&shared); let beginning = integration.beginning; - let event_loop: *const EventLoopWindowTarget = event_loop; - egui::Context::set_immediate_viewport_renderer(move |_egui_ctx, immediate_viewport| { if let Some(shared) = shared.upgrade() { - // SAFETY: the event loop lives longer than - // the Rc:s we just upgraded above. - #[allow(unsafe_code)] - let event_loop = unsafe { event_loop.as_ref().unwrap() }; - - render_immediate_viewport(event_loop, beginning, &shared, immediate_viewport); + render_immediate_viewport(beginning, &shared, immediate_viewport); } else { log::warn!("render_sync_callback called after window closed"); } @@ -377,134 +367,127 @@ impl WinitApp for WgpuWinitApp { fn run_ui_and_paint( &mut self, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, window_id: WindowId, - ) -> EventResult { + ) -> Result { self.initialized_all_windows(event_loop); if let Some(running) = &mut self.running { running.run_ui_and_paint(window_id) } else { - EventResult::Wait + Ok(EventResult::Wait) } } - fn on_event( - &mut self, - event_loop: &EventLoopWindowTarget, - event: &winit::event::Event, - ) -> Result { - crate::profile_function!(winit_integration::short_event_description(event)); + fn resumed(&mut self, event_loop: &ActiveEventLoop) -> crate::Result { + log::debug!("Event::Resumed"); - self.initialized_all_windows(event_loop); + let running = if let Some(running) = &self.running { + #[cfg(target_os = "android")] + self.recreate_window(event_loop, running); + running + } else { + let storage = if let Some(file) = &self.native_options.persistence_path { + epi_integration::create_storage_with_file(file) + } else { + epi_integration::create_storage( + self.native_options + .viewport + .app_id + .as_ref() + .unwrap_or(&self.app_name), + ) + }; + let egui_ctx = winit_integration::create_egui_context(storage.as_deref()); + let (window, builder) = create_window( + &egui_ctx, + event_loop, + storage.as_deref(), + &mut self.native_options, + )?; + self.init_run_state(egui_ctx, event_loop, storage, window, builder)? + }; + + let viewport = &running.shared.borrow().viewports[&ViewportId::ROOT]; + if let Some(window) = &viewport.window { + Ok(EventResult::RepaintNow(window.id())) + } else { + Ok(EventResult::Wait) + } + } - Ok(match event { - winit::event::Event::Resumed => { - log::debug!("Event::Resumed"); + fn suspended(&mut self, _: &ActiveEventLoop) -> crate::Result { + #[cfg(target_os = "android")] + self.drop_window()?; + Ok(EventResult::Wait) + } - let running = if let Some(running) = &self.running { - #[cfg(target_os = "android")] - self.recreate_window(event_loop, running); - running - } else { - let storage = if let Some(file) = &self.native_options.persistence_path { - epi_integration::create_storage_with_file(file) - } else { - epi_integration::create_storage( - self.native_options - .viewport - .app_id - .as_ref() - .unwrap_or(&self.app_name), - ) - }; - let egui_ctx = winit_integration::create_egui_context(storage.as_deref()); - let (window, builder) = create_window( - &egui_ctx, - event_loop, - storage.as_deref(), - &mut self.native_options, - )?; - self.init_run_state(egui_ctx, event_loop, storage, window, builder)? - }; + fn device_event( + &mut self, + _: &ActiveEventLoop, + _: winit::event::DeviceId, + event: winit::event::DeviceEvent, + ) -> crate::Result { + if let winit::event::DeviceEvent::MouseMotion { delta } = event { + if let Some(running) = &mut self.running { + let mut shared = running.shared.borrow_mut(); + if let Some(viewport) = shared + .focused_viewport + .and_then(|viewport| shared.viewports.get_mut(&viewport)) + { + if let Some(egui_winit) = viewport.egui_winit.as_mut() { + egui_winit.on_mouse_motion(delta); + } - let viewport = &running.shared.borrow().viewports[&ViewportId::ROOT]; - if let Some(window) = &viewport.window { - EventResult::RepaintNow(window.id()) - } else { - EventResult::Wait + if let Some(window) = viewport.window.as_ref() { + return Ok(EventResult::RepaintNext(window.id())); + } } } + } - winit::event::Event::Suspended => { - #[cfg(target_os = "android")] - self.drop_window()?; - EventResult::Wait - } + Ok(EventResult::Wait) + } - winit::event::Event::WindowEvent { event, window_id } => { - if let Some(running) = &mut self.running { - running.on_window_event(*window_id, event) - } else { - EventResult::Wait - } - } + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: winit::event::WindowEvent, + ) -> crate::Result { + self.initialized_all_windows(event_loop); - winit::event::Event::DeviceEvent { - device_id: _, - event: winit::event::DeviceEvent::MouseMotion { delta }, - } => { - if let Some(running) = &mut self.running { - let mut shared = running.shared.borrow_mut(); - if let Some(viewport) = shared - .focused_viewport - .and_then(|viewport| shared.viewports.get_mut(&viewport)) - { - if let Some(egui_winit) = viewport.egui_winit.as_mut() { - egui_winit.on_mouse_motion(*delta); - } + if let Some(running) = &mut self.running { + Ok(running.on_window_event(window_id, &event)) + } else { + Ok(EventResult::Wait) + } + } - if let Some(window) = viewport.window.as_ref() { - EventResult::RepaintNext(window.id()) - } else { - EventResult::Wait - } - } else { - EventResult::Wait - } - } else { - EventResult::Wait + #[cfg(feature = "accesskit")] + fn on_accesskit_event(&mut self, event: accesskit_winit::Event) -> crate::Result { + if let Some(running) = &mut self.running { + let mut shared_lock = running.shared.borrow_mut(); + let SharedState { + viewport_from_window, + viewports, + .. + } = &mut *shared_lock; + if let Some(viewport) = viewport_from_window + .get(&event.window_id) + .and_then(|id| viewports.get_mut(id)) + { + if let Some(egui_winit) = &mut viewport.egui_winit { + return Ok(winit_integration::on_accesskit_window_event( + egui_winit, + event.window_id, + &event.window_event, + )); } } + } - #[cfg(feature = "accesskit")] - winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest( - accesskit_winit::ActionRequestEvent { request, window_id }, - )) => { - if let Some(running) = &mut self.running { - let mut shared_lock = running.shared.borrow_mut(); - let SharedState { - viewport_from_window, - viewports, - .. - } = &mut *shared_lock; - if let Some(viewport) = viewport_from_window - .get(window_id) - .and_then(|id| viewports.get_mut(id)) - { - if let Some(egui_winit) = &mut viewport.egui_winit { - egui_winit.on_accesskit_action_request(request.clone()); - } - } - // As a form of user input, accessibility actions should - // lead to a repaint. - EventResult::RepaintNext(*window_id) - } else { - EventResult::Wait - } - } - _ => EventResult::Wait, - }) + Ok(EventResult::Wait) } } @@ -527,7 +510,7 @@ impl WgpuWinitRunning { } /// This is called both for the root viewport, and all deferred viewports - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + fn run_ui_and_paint(&mut self, window_id: WindowId) -> Result { crate::profile_function!(); let Some(viewport_id) = self @@ -537,7 +520,7 @@ impl WgpuWinitRunning { .get(&window_id) .copied() else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; #[cfg(feature = "puffin")] @@ -562,7 +545,7 @@ impl WgpuWinitRunning { if viewport_id != ViewportId::ROOT { let Some(viewport) = viewports.get(&viewport_id) else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; if viewport.viewport_ui_cb.is_none() { @@ -570,15 +553,15 @@ impl WgpuWinitRunning { // That means that the viewport cannot be rendered by itself and needs his parent to be rendered. if let Some(viewport) = viewports.get(&viewport.ids.parent) { if let Some(window) = viewport.window.as_ref() { - return EventResult::RepaintNext(window.id()); + return Ok(EventResult::RepaintNext(window.id())); } } - return EventResult::Wait; + return Ok(EventResult::Wait); } } let Some(viewport) = viewports.get_mut(&viewport_id) else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; let Viewport { @@ -592,21 +575,17 @@ impl WgpuWinitRunning { let viewport_ui_cb = viewport_ui_cb.clone(); let Some(window) = window else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; egui_winit::update_viewport_info(info, &integration.egui_ctx, window, false); { crate::profile_scope!("set_window"); - if let Err(err) = - pollster::block_on(painter.set_window(viewport_id, Some(window.clone()))) - { - log::warn!("Failed to set window: {err}"); - } + pollster::block_on(painter.set_window(viewport_id, Some(window.clone())))?; } let Some(egui_winit) = egui_winit.as_mut() else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; let mut raw_input = egui_winit.take_egui_input(window); @@ -650,7 +629,7 @@ impl WgpuWinitRunning { remove_viewports_not_in(viewports, painter, viewport_from_window, &viewport_output); let Some(viewport) = viewports.get_mut(&viewport_id) else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; viewport.info.events.clear(); // they should have been processed @@ -661,7 +640,7 @@ impl WgpuWinitRunning { .. } = viewport else { - return EventResult::Wait; + return Ok(EventResult::Wait); }; egui_winit.handle_platform_output(window, platform_output); @@ -751,9 +730,9 @@ impl WgpuWinitRunning { } if integration.should_close() { - EventResult::Exit + Ok(EventResult::Exit) } else { - EventResult::Wait + Ok(EventResult::Wait) } } @@ -762,8 +741,6 @@ impl WgpuWinitRunning { window_id: WindowId, event: &winit::event::WindowEvent, ) -> EventResult { - crate::profile_function!(egui_winit::short_window_event_description(event)); - let Self { integration, shared, @@ -865,7 +842,7 @@ impl Viewport { /// Create winit window, if needed. fn initialize_window( &mut self, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, egui_ctx: &egui::Context, windows_id: &mut HashMap, painter: &mut egui_wgpu::winit::Painter, @@ -910,7 +887,7 @@ impl Viewport { fn create_window( egui_ctx: &egui::Context, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, storage: Option<&dyn Storage>, native_options: &mut NativeOptions, ) -> Result<(Window, ViewportBuilder), winit::error::OsError> { @@ -931,7 +908,6 @@ fn create_window( } fn render_immediate_viewport( - event_loop: &EventLoopWindowTarget, beginning: Instant, shared: &RefCell, immediate_viewport: ImmediateViewport<'_>, @@ -956,7 +932,9 @@ fn render_immediate_viewport( let viewport = initialize_or_update_viewport(viewports, ids, ViewportClass::Immediate, builder, None); if viewport.window.is_none() { - viewport.initialize_window(event_loop, egui_ctx, viewport_from_window, painter); + event_loop_context::with_current_event_loop(|event_loop| { + viewport.initialize_window(event_loop, egui_ctx, viewport_from_window, painter); + }); } let (Some(window), Some(egui_winit)) = (&viewport.window, &mut viewport.egui_winit) else { diff --git a/crates/eframe/src/native/winit_integration.rs b/crates/eframe/src/native/winit_integration.rs index fbbd7910732..1489b0bf40d 100644 --- a/crates/eframe/src/native/winit_integration.rs +++ b/crates/eframe/src/native/winit_integration.rs @@ -1,7 +1,7 @@ use std::{sync::Arc, time::Instant}; use winit::{ - event_loop::EventLoopWindowTarget, + event_loop::ActiveEventLoop, window::{Window, WindowId}, }; @@ -48,12 +48,12 @@ pub enum UserEvent { /// A request related to [`accesskit`](https://accesskit.dev/). #[cfg(feature = "accesskit")] - AccessKitActionRequest(accesskit_winit::ActionRequestEvent), + AccessKitActionRequest(accesskit_winit::Event), } #[cfg(feature = "accesskit")] -impl From for UserEvent { - fn from(inner: accesskit_winit::ActionRequestEvent) -> Self { +impl From for UserEvent { + fn from(inner: accesskit_winit::Event) -> Self { Self::AccessKitActionRequest(inner) } } @@ -70,15 +70,30 @@ pub trait WinitApp { fn run_ui_and_paint( &mut self, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, window_id: WindowId, - ) -> EventResult; + ) -> crate::Result; + + fn suspended(&mut self, event_loop: &ActiveEventLoop) -> crate::Result; + + fn resumed(&mut self, event_loop: &ActiveEventLoop) -> crate::Result; + + fn device_event( + &mut self, + event_loop: &ActiveEventLoop, + device_id: winit::event::DeviceId, + event: winit::event::DeviceEvent, + ) -> crate::Result; - fn on_event( + fn window_event( &mut self, - event_loop: &EventLoopWindowTarget, - event: &winit::event::Event, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: winit::event::WindowEvent, ) -> crate::Result; + + #[cfg(feature = "accesskit")] + fn on_accesskit_event(&mut self, event: accesskit_winit::Event) -> crate::Result; } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -113,15 +128,35 @@ pub fn system_theme(window: &Window, options: &crate::NativeOptions) -> Option) -> &'static str { +#[cfg(feature = "accesskit")] +pub(crate) fn on_accesskit_window_event( + egui_winit: &mut egui_winit::State, + window_id: WindowId, + event: &accesskit_winit::WindowEvent, +) -> EventResult { match event { - winit::event::Event::UserEvent(user_event) => match user_event { - UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint", - #[cfg(feature = "accesskit")] - UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest", - }, - _ => egui_winit::short_generic_event_description(event), + accesskit_winit::WindowEvent::InitialTreeRequested => { + egui_winit.egui_ctx().enable_accesskit(); + // Because we can't provide the initial tree synchronously + // (because that would require the activation handler to access + // the same mutable state as the winit event handler), some + // AccessKit platform adapters will use a placeholder tree + // until we send the first tree update. To minimize the possible + // bad effects of that workaround, repaint and send the tree + // immediately. + EventResult::RepaintNow(window_id) + } + accesskit_winit::WindowEvent::ActionRequested(request) => { + egui_winit.on_accesskit_action_request(request.clone()); + // As a form of user input, accessibility actions should cause + // a repaint, but not until the next regular frame. + EventResult::RepaintNext(window_id) + } + accesskit_winit::WindowEvent::AccessibilityDeactivated => { + egui_winit.egui_ctx().disable_accesskit(); + // Disabling AccessKit support should have no visible effect, + // so there's no need to repaint. + EventResult::Wait + } } } diff --git a/crates/egui-wgpu/Cargo.toml b/crates/egui-wgpu/Cargo.toml index 3eaac2e92f5..647016e302b 100644 --- a/crates/egui-wgpu/Cargo.toml +++ b/crates/egui-wgpu/Cargo.toml @@ -61,9 +61,7 @@ wgpu = { workspace = true, features = ["wgsl"] } # Optional dependencies: -winit = { workspace = true, optional = true, default-features = false, features = [ - "rwh_06", -] } +winit = { workspace = true, optional = true, default-features = false } # Native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index 0cf9bf9403a..95e8215b7d9 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -62,14 +62,15 @@ egui = { workspace = true, default-features = false, features = ["log"] } ahash.workspace = true log.workspace = true +nix = { version = "0.26.4", default-features = false, optional = true } raw-window-handle.workspace = true web-time.workspace = true -winit = { workspace = true, default-features = false, features = ["rwh_06"] } +winit = { workspace = true, default-features = false } #! ### Optional dependencies # feature accesskit -accesskit_winit = { version = "0.16.0", optional = true } +accesskit_winit = { version = "0.22", optional = true } ## Enable this when generating docs. document-features = { workspace = true, optional = true } @@ -81,5 +82,10 @@ webbrowser = { version = "1.0.0", optional = true } [target.'cfg(any(target_os="linux", target_os="dragonfly", target_os="freebsd", target_os="netbsd", target_os="openbsd"))'.dependencies] smithay-clipboard = { version = "0.7.0", optional = true } +# The wayland-cursor normally selected doesn't properly enable all the features it uses +# and thus doesn't compile as it is used in egui-winit. This is fixed upstream, so force +# a slightly newer version. Remove this when winit upgrades past this version. +wayland-cursor = { version = "0.31.1", default-features = false, optional = true } + [target.'cfg(not(target_os = "android"))'.dependencies] arboard = { version = "3.3", optional = true, default-features = false } diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 093835d8594..e2565794ae3 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -31,7 +31,7 @@ pub(crate) use profiling_scopes::*; use winit::{ dpi::{PhysicalPosition, PhysicalSize}, event::ElementState, - event_loop::EventLoopWindowTarget, + event_loop::ActiveEventLoop, window::{CursorGrabMode, Window, WindowButtons, WindowLevel}, }; @@ -158,16 +158,15 @@ impl State { } #[cfg(feature = "accesskit")] - pub fn init_accesskit + Send>( + pub fn init_accesskit + Send>( &mut self, window: &Window, event_loop_proxy: winit::event_loop::EventLoopProxy, - initial_tree_update_factory: impl 'static + FnOnce() -> accesskit::TreeUpdate + Send, ) { crate::profile_function!(); - self.accesskit = Some(accesskit_winit::Adapter::new( + + self.accesskit = Some(accesskit_winit::Adapter::with_event_loop_proxy( window, - initial_tree_update_factory, event_loop_proxy, )); } @@ -263,7 +262,7 @@ impl State { crate::profile_function!(short_window_event_description(event)); #[cfg(feature = "accesskit")] - if let Some(accesskit) = &self.accesskit { + if let Some(accesskit) = self.accesskit.as_mut() { accesskit.process_event(window, event); } @@ -474,13 +473,14 @@ impl State { // Things we completely ignore: WindowEvent::ActivationTokenDone { .. } | WindowEvent::AxisMotion { .. } - | WindowEvent::SmartMagnify { .. } - | WindowEvent::TouchpadRotate { .. } => EventResponse { + | WindowEvent::DoubleTapGesture { .. } + | WindowEvent::RotationGesture { .. } + | WindowEvent::PanGesture { .. } => EventResponse { repaint: false, consumed: false, }, - WindowEvent::TouchpadMagnify { delta, .. } => { + WindowEvent::PinchGesture { delta, .. } => { // Positive delta values indicate magnification (zooming in). // Negative delta values indicate shrinking (zooming out). let zoom_factor = (*delta as f32).exp(); @@ -859,7 +859,7 @@ impl State { } #[cfg(feature = "accesskit")] - if let Some(accesskit) = self.accesskit.as_ref() { + if let Some(accesskit) = self.accesskit.as_mut() { if let Some(update) = accesskit_update { crate::profile_scope!("accesskit"); accesskit.update_if_active(|| update); @@ -879,7 +879,10 @@ impl State { self.current_cursor_icon = Some(cursor_icon); if let Some(winit_cursor_icon) = translate_cursor(cursor_icon) { - window.set_cursor_icon(winit_cursor_icon); + window.set_cursor_visible(true); + window.set_cursor(winit_cursor_icon); + } else { + window.set_cursor_visible(false); } } else { // Remember to set the cursor again once the cursor returns to the screen: @@ -1507,28 +1510,25 @@ fn process_viewport_command( /// /// # Errors /// Possible causes of error include denied permission, incompatible system, and lack of memory. -pub fn create_window( +pub fn create_window( egui_ctx: &egui::Context, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, viewport_builder: &ViewportBuilder, ) -> Result { crate::profile_function!(); - let window_builder = - create_winit_window_builder(egui_ctx, event_loop, viewport_builder.clone()); - let window = { - crate::profile_scope!("WindowBuilder::build"); - window_builder.build(event_loop)? - }; + let window_attributes = + create_winit_window_attributes(egui_ctx, event_loop, viewport_builder.clone()); + let window = event_loop.create_window(window_attributes)?; apply_viewport_builder_to_window(egui_ctx, &window, viewport_builder); Ok(window) } -pub fn create_winit_window_builder( +pub fn create_winit_window_attributes( egui_ctx: &egui::Context, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, viewport_builder: ViewportBuilder, -) -> winit::window::WindowBuilder { +) -> winit::window::WindowAttributes { crate::profile_function!(); // We set sizes and positions in egui:s own ui points, which depends on the egui @@ -1587,7 +1587,7 @@ pub fn create_winit_window_builder( clamp_size_to_monitor_size: _, // Handled in `viewport_builder` in `epi_integration.rs` } = viewport_builder; - let mut window_builder = winit::window::WindowBuilder::new() + let mut window_attributes = winit::window::WindowAttributes::default() .with_title(title.unwrap_or_else(|| "egui window".to_owned())) .with_transparent(transparent.unwrap_or(false)) .with_decorations(decorations.unwrap_or(true)) @@ -1618,28 +1618,28 @@ pub fn create_winit_window_builder( .with_active(active.unwrap_or(true)); if let Some(size) = inner_size { - window_builder = window_builder.with_inner_size(PhysicalSize::new( + window_attributes = window_attributes.with_inner_size(PhysicalSize::new( pixels_per_point * size.x, pixels_per_point * size.y, )); } if let Some(size) = min_inner_size { - window_builder = window_builder.with_min_inner_size(PhysicalSize::new( + window_attributes = window_attributes.with_min_inner_size(PhysicalSize::new( pixels_per_point * size.x, pixels_per_point * size.y, )); } if let Some(size) = max_inner_size { - window_builder = window_builder.with_max_inner_size(PhysicalSize::new( + window_attributes = window_attributes.with_max_inner_size(PhysicalSize::new( pixels_per_point * size.x, pixels_per_point * size.y, )); } if let Some(pos) = position { - window_builder = window_builder.with_position(PhysicalPosition::new( + window_attributes = window_attributes.with_position(PhysicalPosition::new( pixels_per_point * pos.x, pixels_per_point * pos.y, )); @@ -1647,61 +1647,61 @@ pub fn create_winit_window_builder( if let Some(icon) = icon { let winit_icon = to_winit_icon(&icon); - window_builder = window_builder.with_window_icon(winit_icon); + window_attributes = window_attributes.with_window_icon(winit_icon); } #[cfg(all(feature = "wayland", target_os = "linux"))] if let Some(app_id) = _app_id { - use winit::platform::wayland::WindowBuilderExtWayland as _; - window_builder = window_builder.with_name(app_id, ""); + use winit::platform::wayland::WindowAttributesExtWayland as _; + window_attributes = window_attributes.with_name(app_id, ""); } #[cfg(all(feature = "x11", target_os = "linux"))] { if let Some(window_type) = _window_type { - use winit::platform::x11::WindowBuilderExtX11 as _; - use winit::platform::x11::XWindowType; - window_builder = window_builder.with_x11_window_type(vec![match window_type { - egui::X11WindowType::Normal => XWindowType::Normal, - egui::X11WindowType::Utility => XWindowType::Utility, - egui::X11WindowType::Dock => XWindowType::Dock, - egui::X11WindowType::Desktop => XWindowType::Desktop, - egui::X11WindowType::Toolbar => XWindowType::Toolbar, - egui::X11WindowType::Menu => XWindowType::Menu, - egui::X11WindowType::Splash => XWindowType::Splash, - egui::X11WindowType::Dialog => XWindowType::Dialog, - egui::X11WindowType::DropdownMenu => XWindowType::DropdownMenu, - egui::X11WindowType::PopupMenu => XWindowType::PopupMenu, - egui::X11WindowType::Tooltip => XWindowType::Tooltip, - egui::X11WindowType::Notification => XWindowType::Notification, - egui::X11WindowType::Combo => XWindowType::Combo, - egui::X11WindowType::Dnd => XWindowType::Dnd, + use winit::platform::x11::WindowAttributesExtX11 as _; + use winit::platform::x11::WindowType; + window_attributes = window_attributes.with_x11_window_type(vec![match window_type { + egui::X11WindowType::Normal => WindowType::Normal, + egui::X11WindowType::Utility => WindowType::Utility, + egui::X11WindowType::Dock => WindowType::Dock, + egui::X11WindowType::Desktop => WindowType::Desktop, + egui::X11WindowType::Toolbar => WindowType::Toolbar, + egui::X11WindowType::Menu => WindowType::Menu, + egui::X11WindowType::Splash => WindowType::Splash, + egui::X11WindowType::Dialog => WindowType::Dialog, + egui::X11WindowType::DropdownMenu => WindowType::DropdownMenu, + egui::X11WindowType::PopupMenu => WindowType::PopupMenu, + egui::X11WindowType::Tooltip => WindowType::Tooltip, + egui::X11WindowType::Notification => WindowType::Notification, + egui::X11WindowType::Combo => WindowType::Combo, + egui::X11WindowType::Dnd => WindowType::Dnd, }]); } } #[cfg(target_os = "windows")] { - use winit::platform::windows::WindowBuilderExtWindows as _; + use winit::platform::windows::WindowAttributesExtWindows as _; if let Some(enable) = _drag_and_drop { - window_builder = window_builder.with_drag_and_drop(enable); + window_attributes = window_attributes.with_drag_and_drop(enable); } if let Some(show) = _taskbar { - window_builder = window_builder.with_skip_taskbar(!show); + window_attributes = window_attributes.with_skip_taskbar(!show); } } #[cfg(target_os = "macos")] { - use winit::platform::macos::WindowBuilderExtMacOS as _; - window_builder = window_builder + use winit::platform::macos::WindowAttributesExtMacOS as _; + window_attributes = window_attributes .with_title_hidden(!_title_shown.unwrap_or(true)) .with_titlebar_buttons_hidden(!_titlebar_buttons_shown.unwrap_or(true)) .with_titlebar_transparent(!_titlebar_shown.unwrap_or(true)) .with_fullsize_content_view(_fullsize_content_view.unwrap_or(false)); } - window_builder + window_attributes } fn to_winit_icon(icon: &egui::IconData) -> Option { @@ -1771,38 +1771,23 @@ pub fn apply_viewport_builder_to_window( // --------------------------------------------------------------------------- -/// Short and fast description of an event. +/// Short and fast description of a device event. /// Useful for logging and profiling. -pub fn short_generic_event_description(event: &winit::event::Event) -> &'static str { - use winit::event::{DeviceEvent, Event, StartCause}; +pub fn short_device_event_description(event: &winit::event::DeviceEvent) -> &'static str { + use winit::event::DeviceEvent; match event { - Event::AboutToWait => "Event::AboutToWait", - Event::LoopExiting => "Event::LoopExiting", - Event::Suspended => "Event::Suspended", - Event::Resumed => "Event::Resumed", - Event::MemoryWarning => "Event::MemoryWarning", - Event::UserEvent(_) => "UserEvent", - Event::DeviceEvent { event, .. } => match event { - DeviceEvent::Added { .. } => "DeviceEvent::Added", - DeviceEvent::Removed { .. } => "DeviceEvent::Removed", - DeviceEvent::MouseMotion { .. } => "DeviceEvent::MouseMotion", - DeviceEvent::MouseWheel { .. } => "DeviceEvent::MouseWheel", - DeviceEvent::Motion { .. } => "DeviceEvent::Motion", - DeviceEvent::Button { .. } => "DeviceEvent::Button", - DeviceEvent::Key { .. } => "DeviceEvent::Key", - }, - Event::NewEvents(start_cause) => match start_cause { - StartCause::ResumeTimeReached { .. } => "NewEvents::ResumeTimeReached", - StartCause::WaitCancelled { .. } => "NewEvents::WaitCancelled", - StartCause::Poll => "NewEvents::Poll", - StartCause::Init => "NewEvents::Init", - }, - Event::WindowEvent { event, .. } => short_window_event_description(event), + DeviceEvent::Added { .. } => "DeviceEvent::Added", + DeviceEvent::Removed { .. } => "DeviceEvent::Removed", + DeviceEvent::MouseMotion { .. } => "DeviceEvent::MouseMotion", + DeviceEvent::MouseWheel { .. } => "DeviceEvent::MouseWheel", + DeviceEvent::Motion { .. } => "DeviceEvent::Motion", + DeviceEvent::Button { .. } => "DeviceEvent::Button", + DeviceEvent::Key { .. } => "DeviceEvent::Key", } } -/// Short and fast description of an event. +/// Short and fast description of a window event. /// Useful for logging and profiling. pub fn short_window_event_description(event: &winit::event::WindowEvent) -> &'static str { use winit::event::WindowEvent; @@ -1825,16 +1810,17 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent) -> &'st WindowEvent::CursorLeft { .. } => "WindowEvent::CursorLeft", WindowEvent::MouseWheel { .. } => "WindowEvent::MouseWheel", WindowEvent::MouseInput { .. } => "WindowEvent::MouseInput", - WindowEvent::TouchpadMagnify { .. } => "WindowEvent::TouchpadMagnify", + WindowEvent::PinchGesture { .. } => "WindowEvent::PinchGesture", WindowEvent::RedrawRequested { .. } => "WindowEvent::RedrawRequested", - WindowEvent::SmartMagnify { .. } => "WindowEvent::SmartMagnify", - WindowEvent::TouchpadRotate { .. } => "WindowEvent::TouchpadRotate", + WindowEvent::DoubleTapGesture { .. } => "WindowEvent::DoubleTapGesture", + WindowEvent::RotationGesture { .. } => "WindowEvent::RotationGesture", WindowEvent::TouchpadPressure { .. } => "WindowEvent::TouchpadPressure", WindowEvent::AxisMotion { .. } => "WindowEvent::AxisMotion", WindowEvent::Touch { .. } => "WindowEvent::Touch", WindowEvent::ScaleFactorChanged { .. } => "WindowEvent::ScaleFactorChanged", WindowEvent::ThemeChanged { .. } => "WindowEvent::ThemeChanged", WindowEvent::Occluded { .. } => "WindowEvent::Occluded", + WindowEvent::PanGesture { .. } => "WindowEvent::PanGesture", } } diff --git a/crates/egui-winit/src/window_settings.rs b/crates/egui-winit/src/window_settings.rs index ec633d3df0a..627d88158c0 100644 --- a/crates/egui-winit/src/window_settings.rs +++ b/crates/egui-winit/src/window_settings.rs @@ -50,10 +50,10 @@ impl WindowSettings { self.inner_size_points } - pub fn initialize_viewport_builder( + pub fn initialize_viewport_builder( &self, egui_zoom_factor: f32, - event_loop: &winit::event_loop::EventLoopWindowTarget, + event_loop: &winit::event_loop::ActiveEventLoop, mut viewport_builder: ViewportBuilder, ) -> ViewportBuilder { crate::profile_function!(); @@ -110,10 +110,10 @@ impl WindowSettings { } } - pub fn clamp_position_to_monitors( + pub fn clamp_position_to_monitors( &mut self, egui_zoom_factor: f32, - event_loop: &winit::event_loop::EventLoopWindowTarget, + event_loop: &winit::event_loop::ActiveEventLoop, ) { // If the app last ran on two monitors and only one is now connected, then // the given position is invalid. @@ -137,9 +137,9 @@ impl WindowSettings { } } -fn find_active_monitor( +fn find_active_monitor( egui_zoom_factor: f32, - event_loop: &winit::event_loop::EventLoopWindowTarget, + event_loop: &winit::event_loop::ActiveEventLoop, window_size_pts: egui::Vec2, position_px: &egui::Pos2, ) -> Option { @@ -172,9 +172,9 @@ fn find_active_monitor( Some(active_monitor) } -fn clamp_pos_to_monitors( +fn clamp_pos_to_monitors( egui_zoom_factor: f32, - event_loop: &winit::event_loop::EventLoopWindowTarget, + event_loop: &winit::event_loop::ActiveEventLoop, window_size_pts: egui::Vec2, position_px: &mut egui::Pos2, ) { diff --git a/crates/egui/Cargo.toml b/crates/egui/Cargo.toml index 2e8e1ddd557..a920df2bf46 100644 --- a/crates/egui/Cargo.toml +++ b/crates/egui/Cargo.toml @@ -86,7 +86,7 @@ ahash.workspace = true nohash-hasher.workspace = true #! ### Optional dependencies -accesskit = { version = "0.12", optional = true } +accesskit = { version = "0.16", optional = true } backtrace = { workspace = true, optional = true } diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 224ce68f40a..d1a05db4bc2 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -396,8 +396,6 @@ struct ContextImpl { #[cfg(feature = "accesskit")] is_accesskit_enabled: bool, - #[cfg(feature = "accesskit")] - accesskit_node_classes: accesskit::NodeClassSet, loaders: Arc, } @@ -2016,12 +2014,7 @@ impl ContextImpl { state .node_builders .into_iter() - .map(|(id, builder)| { - ( - id.accesskit_id(), - builder.build(&mut self.accesskit_node_classes), - ) - }) + .map(|(id, builder)| (id.accesskit_id(), builder.build())) .collect() }; let focus_id = self @@ -2909,37 +2902,15 @@ impl Context { } /// Enable generation of AccessKit tree updates in all future frames. - /// - /// If it's practical for the egui integration to immediately run the egui - /// application when it is either initializing the AccessKit adapter or - /// being called by the AccessKit adapter to provide the initial tree update, - /// then it should do so, to provide a complete AccessKit tree to the adapter - /// immediately. Otherwise, it should enqueue a repaint and use the - /// placeholder tree update from [`Context::accesskit_placeholder_tree_update`] - /// in the meantime. #[cfg(feature = "accesskit")] pub fn enable_accesskit(&self) { self.write(|ctx| ctx.is_accesskit_enabled = true); } - /// Return a tree update that the egui integration should provide to the - /// AccessKit adapter if it cannot immediately run the egui application - /// to get a full tree update after running [`Context::enable_accesskit`]. + /// Disable generation of AccessKit tree updates in all future frames. #[cfg(feature = "accesskit")] - pub fn accesskit_placeholder_tree_update(&self) -> accesskit::TreeUpdate { - crate::profile_function!(); - - use accesskit::{NodeBuilder, Role, Tree, TreeUpdate}; - - let root_id = crate::accesskit_root_id().accesskit_id(); - self.write(|ctx| TreeUpdate { - nodes: vec![( - root_id, - NodeBuilder::new(Role::Window).build(&mut ctx.accesskit_node_classes), - )], - tree: Some(Tree::new(root_id)), - focus: root_id, - }) + pub fn disable_accesskit(&self) { + self.write(|ctx| ctx.is_accesskit_enabled = false); } } diff --git a/crates/egui/src/response.rs b/crates/egui/src/response.rs index 143175ba2ee..cb2ef5467f1 100644 --- a/crates/egui/src/response.rs +++ b/crates/egui/src/response.rs @@ -964,11 +964,11 @@ impl Response { info: crate::WidgetInfo, ) { use crate::WidgetType; - use accesskit::{Checked, Role}; + use accesskit::{Role, Toggled}; self.fill_accesskit_node_common(builder); builder.set_role(match info.typ { - WidgetType::Label => Role::StaticText, + WidgetType::Label => Role::Label, WidgetType::Link => Role::Link, WidgetType::TextEdit => Role::TextInput, WidgetType::Button | WidgetType::ImageButton | WidgetType::CollapsingHeader => { @@ -976,7 +976,7 @@ impl Response { } WidgetType::Checkbox => Role::CheckBox, WidgetType::RadioButton => Role::RadioButton, - WidgetType::SelectableLabel => Role::ToggleButton, + WidgetType::SelectableLabel => Role::Button, WidgetType::ComboBox => Role::ComboBox, WidgetType::Slider => Role::Slider, WidgetType::DragValue => Role::SpinButton, @@ -997,14 +997,14 @@ impl Response { builder.set_numeric_value(value); } if let Some(selected) = info.selected { - builder.set_checked(if selected { - Checked::True + builder.set_toggled(if selected { + Toggled::True } else { - Checked::False + Toggled::False }); } else if matches!(info.typ, WidgetType::Checkbox) { // Indeterminate state - builder.set_checked(Checked::Mixed); + builder.set_toggled(Toggled::Mixed); } } diff --git a/crates/egui/src/text_selection/label_text_selection.rs b/crates/egui/src/text_selection/label_text_selection.rs index 7988820d6af..f0660de0459 100644 --- a/crates/egui/src/text_selection/label_text_selection.rs +++ b/crates/egui/src/text_selection/label_text_selection.rs @@ -38,7 +38,7 @@ fn paint_selection( ui.ctx(), _response.id, cursor_range, - accesskit::Role::StaticText, + accesskit::Role::Label, galley_pos, galley, ); diff --git a/crates/egui/tests/accesskit.rs b/crates/egui/tests/accesskit.rs index e3d1c7b56ab..e5dc3d97a93 100644 --- a/crates/egui/tests/accesskit.rs +++ b/crates/egui/tests/accesskit.rs @@ -94,7 +94,7 @@ fn toggle_button_node() { let (_, toggle) = output .nodes .iter() - .find(|(_, node)| node.role() == Role::ToggleButton) + .find(|(_, node)| node.role() == Role::Button) .expect("Toggle button should exist in the accesskit output"); assert_eq!(toggle.name(), Some(button_text)); diff --git a/crates/egui_glow/Cargo.toml b/crates/egui_glow/Cargo.toml index 28de667e8c1..615247fb07e 100644 --- a/crates/egui_glow/Cargo.toml +++ b/crates/egui_glow/Cargo.toml @@ -69,9 +69,7 @@ document-features = { workspace = true, optional = true } # Native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] puffin = { workspace = true, optional = true } -winit = { workspace = true, optional = true, default-features = false, features = [ - "rwh_06", # for compatibility with egui-winit -] } +winit = { workspace = true, optional = true, default-features = false } # Web: [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -82,15 +80,7 @@ wasm-bindgen.workspace = true [dev-dependencies] glutin.workspace = true # examples/pure_glow glutin-winit.workspace = true -# glutin stuck on old version of raw-window-handle: -rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = [ - "std", -] } [[example]] name = "pure_glow" -required-features = [ - "winit", - "egui/default_fonts", - "winit/rwh_05", # glutin stuck on old version of raw-window-handle -] +required-features = ["winit", "egui/default_fonts"] diff --git a/crates/egui_glow/examples/pure_glow.rs b/crates/egui_glow/examples/pure_glow.rs index 0066b2ea815..4145e1d6310 100644 --- a/crates/egui_glow/examples/pure_glow.rs +++ b/crates/egui_glow/examples/pure_glow.rs @@ -8,8 +8,10 @@ #![allow(unsafe_code)] use std::num::NonZeroU32; +use std::sync::Arc; use egui_winit::winit; +use winit::raw_window_handle::HasWindowHandle; /// The majority of `GlutinWindowContext` is taken from `eframe` struct GlutinWindowContext { @@ -23,13 +25,12 @@ impl GlutinWindowContext { // refactor this function to use `glutin-winit` crate eventually. // preferably add android support at the same time. #[allow(unsafe_code)] - unsafe fn new(event_loop: &winit::event_loop::EventLoopWindowTarget) -> Self { + unsafe fn new(event_loop: &winit::event_loop::ActiveEventLoop) -> Self { use glutin::context::NotCurrentGlContext; use glutin::display::GetGlDisplay; use glutin::display::GlDisplay; use glutin::prelude::GlSurface; - use rwh_05::HasRawWindowHandle; - let winit_window_builder = winit::window::WindowBuilder::new() + let winit_window_builder = winit::window::WindowAttributes::default() .with_resizable(true) .with_inner_size(winit::dpi::LogicalSize { width: 800.0, @@ -48,7 +49,7 @@ impl GlutinWindowContext { let (mut window, gl_config) = glutin_winit::DisplayBuilder::new() // let glutin-winit helper crate handle the complex parts of opengl context creation .with_preference(glutin_winit::ApiPreference::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 - .with_window_builder(Some(winit_window_builder.clone())) + .with_window_attributes(Some(winit_window_builder.clone())) .build( event_loop, config_template_builder, @@ -62,7 +63,11 @@ impl GlutinWindowContext { let gl_display = gl_config.display(); log::debug!("found gl_config: {:?}", &gl_config); - let raw_window_handle = window.as_ref().map(|w| w.raw_window_handle()); + let raw_window_handle = window.as_ref().map(|w| { + w.window_handle() + .expect("failed to get window handle") + .as_raw() + }); log::debug!("raw window handle: {:?}", raw_window_handle); let context_attributes = glutin::context::ContextAttributesBuilder::new().build(raw_window_handle); @@ -95,7 +100,14 @@ impl GlutinWindowContext { let height = NonZeroU32::new(height).unwrap_or(NonZeroU32::MIN); let surface_attributes = glutin::surface::SurfaceAttributesBuilder::::new() - .build(window.raw_window_handle(), width, height); + .build( + window + .window_handle() + .expect("failed to get window handle") + .as_raw(), + width, + height, + ); log::debug!( "creating surface with attributes: {:?}", &surface_attributes @@ -152,51 +164,81 @@ pub enum UserEvent { Redraw(std::time::Duration), } -fn main() { - let mut clear_color = [0.1, 0.1, 0.1]; +struct GlowApp { + proxy: winit::event_loop::EventLoopProxy, + gl_window: Option, + gl: Option>, + egui_glow: Option, + repaint_delay: std::time::Duration, + clear_color: [f32; 3], +} - let event_loop = winit::event_loop::EventLoopBuilder::::with_user_event() - .build() - .unwrap(); - let (gl_window, gl) = create_display(&event_loop); - let gl = std::sync::Arc::new(gl); - - let mut egui_glow = egui_glow::EguiGlow::new(&event_loop, gl.clone(), None, None, true); - - let event_loop_proxy = egui::mutex::Mutex::new(event_loop.create_proxy()); - egui_glow - .egui_ctx - .set_request_repaint_callback(move |info| { - event_loop_proxy - .lock() - .send_event(UserEvent::Redraw(info.delay)) - .expect("Cannot send event"); - }); +impl GlowApp { + fn new(proxy: winit::event_loop::EventLoopProxy) -> Self { + Self { + proxy, + gl_window: None, + gl: None, + egui_glow: None, + repaint_delay: std::time::Duration::MAX, + clear_color: [0.1, 0.1, 0.1], + } + } +} - let mut repaint_delay = std::time::Duration::MAX; +impl winit::application::ApplicationHandler for GlowApp { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + let (gl_window, gl) = create_display(event_loop); + let gl = std::sync::Arc::new(gl); + gl_window.window().set_visible(true); + + let egui_glow = egui_glow::EguiGlow::new(event_loop, gl.clone(), None, None, true); + + let event_loop_proxy = egui::mutex::Mutex::new(self.proxy.clone()); + egui_glow + .egui_ctx + .set_request_repaint_callback(move |info| { + event_loop_proxy + .lock() + .send_event(UserEvent::Redraw(info.delay)) + .expect("Cannot send event"); + }); + self.gl_window = Some(gl_window); + self.gl = Some(gl); + self.egui_glow = Some(egui_glow); + } - let _ = event_loop.run(move |event, event_loop_window_target| { + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + _window_id: winit::window::WindowId, + event: winit::event::WindowEvent, + ) { let mut redraw = || { let mut quit = false; - egui_glow.run(gl_window.window(), |egui_ctx| { - egui::SidePanel::left("my_side_panel").show(egui_ctx, |ui| { - ui.heading("Hello World!"); - if ui.button("Quit").clicked() { - quit = true; - } - ui.color_edit_button_rgb(&mut clear_color); - }); - }); + self.egui_glow.as_mut().unwrap().run( + self.gl_window.as_mut().unwrap().window(), + |egui_ctx| { + egui::SidePanel::left("my_side_panel").show(egui_ctx, |ui| { + ui.heading("Hello World!"); + if ui.button("Quit").clicked() { + quit = true; + } + + ui.color_edit_button_rgb(self.clear_color.as_mut().try_into().unwrap()); + }); + }, + ); if quit { - event_loop_window_target.exit(); + event_loop.exit(); } else { - event_loop_window_target.set_control_flow(if repaint_delay.is_zero() { - gl_window.window().request_redraw(); + event_loop.set_control_flow(if self.repaint_delay.is_zero() { + self.gl_window.as_mut().unwrap().window().request_redraw(); winit::event_loop::ControlFlow::Poll } else if let Some(repaint_after_instant) = - std::time::Instant::now().checked_add(repaint_delay) + std::time::Instant::now().checked_add(self.repaint_delay) { winit::event_loop::ControlFlow::WaitUntil(repaint_after_instant) } else { @@ -207,64 +249,88 @@ fn main() { { unsafe { use glow::HasContext as _; - gl.clear_color(clear_color[0], clear_color[1], clear_color[2], 1.0); - gl.clear(glow::COLOR_BUFFER_BIT); + self.gl.as_mut().unwrap().clear_color( + self.clear_color[0], + self.clear_color[1], + self.clear_color[2], + 1.0, + ); + self.gl.as_mut().unwrap().clear(glow::COLOR_BUFFER_BIT); } // draw things behind egui here - egui_glow.paint(gl_window.window()); + self.egui_glow + .as_mut() + .unwrap() + .paint(self.gl_window.as_mut().unwrap().window()); // draw things on top of egui here - gl_window.swap_buffers().unwrap(); - gl_window.window().set_visible(true); + self.gl_window.as_mut().unwrap().swap_buffers().unwrap(); + self.gl_window.as_mut().unwrap().window().set_visible(true); } }; - match event { - winit::event::Event::WindowEvent { event, .. } => { - use winit::event::WindowEvent; - if matches!(event, WindowEvent::CloseRequested | WindowEvent::Destroyed) { - event_loop_window_target.exit(); - return; - } + use winit::event::WindowEvent; + if matches!(event, WindowEvent::CloseRequested | WindowEvent::Destroyed) { + event_loop.exit(); + return; + } - if matches!(event, WindowEvent::RedrawRequested) { - redraw(); - return; - } + if matches!(event, WindowEvent::RedrawRequested) { + redraw(); + return; + } - if let winit::event::WindowEvent::Resized(physical_size) = &event { - gl_window.resize(*physical_size); - } + if let winit::event::WindowEvent::Resized(physical_size) = &event { + self.gl_window.as_mut().unwrap().resize(*physical_size); + } - let event_response = egui_glow.on_window_event(gl_window.window(), &event); + let event_response = self + .egui_glow + .as_mut() + .unwrap() + .on_window_event(self.gl_window.as_mut().unwrap().window(), &event); - if event_response.repaint { - gl_window.window().request_redraw(); - } - } + if event_response.repaint { + self.gl_window.as_mut().unwrap().window().request_redraw(); + } + } - winit::event::Event::UserEvent(UserEvent::Redraw(delay)) => { - repaint_delay = delay; - } - winit::event::Event::LoopExiting => { - egui_glow.destroy(); - } - winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached { - .. - }) => { - gl_window.window().request_redraw(); - } + fn user_event(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop, event: UserEvent) { + match event { + UserEvent::Redraw(delay) => self.repaint_delay = delay, + } + } - _ => (), + fn new_events( + &mut self, + _event_loop: &winit::event_loop::ActiveEventLoop, + cause: winit::event::StartCause, + ) { + if let winit::event::StartCause::ResumeTimeReached { .. } = &cause { + self.gl_window.as_mut().unwrap().window().request_redraw(); } - }); + } + + fn exiting(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) { + self.egui_glow.as_mut().unwrap().destroy(); + } +} + +fn main() { + let event_loop = winit::event_loop::EventLoop::::with_user_event() + .build() + .unwrap(); + let proxy = event_loop.create_proxy(); + + let mut app = GlowApp::new(proxy); + event_loop.run_app(&mut app).expect("failed to run app"); } fn create_display( - event_loop: &winit::event_loop::EventLoopWindowTarget, + event_loop: &winit::event_loop::ActiveEventLoop, ) -> (GlutinWindowContext, glow::Context) { let glutin_window_context = unsafe { GlutinWindowContext::new(event_loop) }; let gl = unsafe { diff --git a/crates/egui_glow/src/winit.rs b/crates/egui_glow/src/winit.rs index c3bcfe386b5..0f1d96d08a4 100644 --- a/crates/egui_glow/src/winit.rs +++ b/crates/egui_glow/src/winit.rs @@ -22,8 +22,8 @@ pub struct EguiGlow { impl EguiGlow { /// For automatic shader version detection set `shader_version` to `None`. - pub fn new( - event_loop: &winit::event_loop::EventLoopWindowTarget, + pub fn new( + event_loop: &winit::event_loop::ActiveEventLoop, gl: std::sync::Arc, shader_version: Option, native_pixels_per_point: Option, diff --git a/deny.toml b/deny.toml index 9c8704bcd2d..5b3c546528d 100644 --- a/deny.toml +++ b/deny.toml @@ -49,16 +49,16 @@ skip = [ { name = "bit-set" }, # wgpu's naga depends on 0.6, syntect's (used by egui_extras) fancy-regex depends on 0.5 { name = "bit-vec" }, # dependency of bit-set in turn, different between 0.6 and 0.5 { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … - { name = "block2" }, # old version via glutin->icrate + { name = "cfg_aliases" }, # old version via wgpu { name = "event-listener" }, # TODO(emilk): rustls pulls in two versions of this 😭 - { name = "glutin_wgl_sys" }, # 0.6.0 used by wgpu-hal, 0.5.0 used by glutin which can't be updated until we update to winit 0.30 + { name = "futures-lite" }, # old version via accesskit_unix and zbus { name = "memoffset" }, # tiny dependency + { name = "ndk-sys" }, # old version via wgpu, winit uses newer version { name = "quick-xml" }, # old version via wayland-scanner { name = "redox_syscall" }, # old version via winit { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' + { name = "windows-core" }, # old version via accesskit_windows { name = "windows" }, # old version via accesskit_windows - { name = "x11rb-protocol" }, # old version via arboard - { name = "x11rb" }, # old version via arboard ] skip-tree = [ { name = "criterion" }, # dev-dependency diff --git a/examples/serial_windows/src/main.rs b/examples/serial_windows/src/main.rs index 133155bd131..f82881e84fe 100644 --- a/examples/serial_windows/src/main.rs +++ b/examples/serial_windows/src/main.rs @@ -6,13 +6,6 @@ use eframe::egui; fn main() -> eframe::Result { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). - if cfg!(target_os = "macos") { - eprintln!( - "This example does not work on Mac! See https://github.com/emilk/egui/issues/1918" - ); - return Ok(()); - } - let options = eframe::NativeOptions { run_and_return: true, viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), @@ -59,10 +52,6 @@ impl eframe::App for MyApp { }; ui.label(label_text); - if ctx.os() == egui::os::OperatingSystem::Mac { - ui.label("This example doesn't work on Mac!"); - } - if ui.button("Close").clicked() { eprintln!("Pressed Close button"); ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);