From 8e5959d55d2f5799a365dd2474b8868b7d49ad14 Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Mon, 18 Dec 2023 14:53:14 +0100 Subject: [PATCH] Update to winit 0.29 (#3649) * Closes https://github.com/emilk/egui/issues/3542 * Closes https://github.com/emilk/egui/issues/2977 * Closes https://github.com/emilk/egui/issues/3303 --------- Co-authored-by: Emil Ernerfeldt --- .typos.toml | 3 +- Cargo.lock | 637 +++++++++++------- crates/eframe/Cargo.toml | 8 +- crates/eframe/src/epi.rs | 2 +- crates/eframe/src/lib.rs | 5 + crates/eframe/src/native/epi_integration.rs | 2 +- crates/eframe/src/native/glow_integration.rs | 177 ++--- crates/eframe/src/native/run.rs | 230 ++++--- crates/eframe/src/native/wgpu_integration.rs | 63 +- crates/eframe/src/native/winit_integration.rs | 14 +- crates/eframe/src/web/app_runner.rs | 12 +- crates/eframe/src/web/events.rs | 2 + crates/eframe/src/web/input.rs | 86 +-- crates/eframe/src/web/text_agent.rs | 8 +- crates/egui-wgpu/Cargo.toml | 2 +- crates/egui-winit/Cargo.toml | 6 +- crates/egui-winit/src/lib.rs | 461 ++++++++----- crates/egui/src/data/input.rs | 265 +++++++- crates/egui/src/data/output.rs | 25 +- crates/egui/src/viewport.rs | 3 +- crates/egui/src/widgets/text_edit/builder.rs | 21 +- crates/egui_extras/Cargo.toml | 4 +- crates/egui_extras/src/image.rs | 3 + crates/egui_extras/src/loaders/svg_loader.rs | 5 +- crates/egui_glow/Cargo.toml | 4 +- crates/egui_glow/examples/pure_glow.rs | 55 +- crates/egui_glow/src/winit.rs | 2 +- deny.toml | 32 +- examples/multiple_viewports/Cargo.toml | 2 + 29 files changed, 1309 insertions(+), 830 deletions(-) diff --git a/.typos.toml b/.typos.toml index 46d137138ea..6d856495178 100644 --- a/.typos.toml +++ b/.typos.toml @@ -3,8 +3,7 @@ # run: typos [default.extend-words] -nknown = "nknown" # part of @55nknown username -Prefence = "Prefence" # typo in glutin_winit API +nknown = "nknown" # part of @55nknown username [files] extend-exclude = ["web_demo/egui_demo_app.js"] # auto-generated diff --git a/Cargo.lock b/Cargo.lock index 51e854eb474..6137494b7b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,9 +20,9 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0cc53b7e5d8f45ebe687178cf91af0f45fdba6e78fedf94f0269c5be5b9f296" +checksum = "ca8410747ed85a17c4a1e9ed3f5a74d3e7bdcc876cf9a18ff40ae21d645997b2" dependencies = [ "enumn", "serde", @@ -30,30 +30,30 @@ dependencies = [ [[package]] name = "accesskit_consumer" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39dfcfd32eb0c1b525daaf4b02adcd2fa529c22cd713491e15bf002a01a714f5" +checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6" dependencies = [ "accesskit", ] [[package]] name = "accesskit_macos" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c7e8406319ac3149d7b59983637984f0864bbf738319b1c443976268b6426c" +checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7" dependencies = [ "accesskit", "accesskit_consumer", - "objc2", + "objc2 0.3.0-beta.3.patch-leaks.3", "once_cell", ] [[package]] name = "accesskit_unix" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0c84552a7995c981d5f22e2d4b24ba9a55718bb12fba883506d6d7344acaf1" +checksum = "6c8c9b4467d77cacfbc93cee9aa8e7822f6d527c774efdca5f8b3a5280c34847" dependencies = [ "accesskit", "accesskit_consumer", @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "314d4a797fc82d182b04f4f0665a368924fb556ad9557fccd2d39d38dc8c1c1b" +checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b" dependencies = [ "accesskit", "accesskit_consumer", @@ -82,9 +82,9 @@ dependencies = [ [[package]] name = "accesskit_winit" -version = "0.15.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88e39fcec2e10971e188730b7a76bab60647dacc973d4591855ebebcadfaa738" +checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67" dependencies = [ "accesskit", "accesskit_macos", @@ -115,6 +115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", + "getrandom", "once_cell", "serde", "version_check", @@ -138,13 +139,15 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "android-activity" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c77a0045eda8b888c76ea473c2b0515ba6f471d318f8927c5c72240937035a6" +checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d" dependencies = [ "android-properties", - "bitflags 1.3.2", + "bitflags 2.4.0", "cc", + "cesu8", + "jni", "jni-sys", "libc", "log", @@ -152,6 +155,7 @@ dependencies = [ "ndk-context", "ndk-sys", "num_enum", + "thiserror", ] [[package]] @@ -195,9 +199,9 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arboard" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac57f2b058a76363e357c056e4f74f1945bf734d37b8b3ef49066c4787dde0fc" +checksum = "aafb29b107435aa276664c1db8954ac27a6e105cdad3c88287a199eb0e313c08" dependencies = [ "clipboard-win", "log", @@ -222,6 +226,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5f312b0a56c5cdf967c0aeb67f6289603354951683bc97ddc595ab974ba9aa" + [[package]] name = "ash" version = "0.37.3+1.3.251" @@ -291,7 +301,7 @@ dependencies = [ "futures-lite", "log", "parking", - "polling", + "polling 2.8.0", "rustix 0.37.25", "slab", "socket2", @@ -326,7 +336,7 @@ dependencies = [ "cfg-if", "event-listener 3.0.0", "futures-lite", - "rustix 0.38.14", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -536,7 +546,16 @@ version = "0.1.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" dependencies = [ - "objc-sys", + "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.1", ] [[package]] @@ -545,8 +564,18 @@ version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" dependencies = [ - "block-sys", - "objc2-encode", + "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]] @@ -615,16 +644,28 @@ dependencies = [ [[package]] name = "calloop" -version = "0.10.6" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "log", - "nix 0.25.1", - "slotmap", + "polling 3.3.0", + "rustix 0.38.21", + "slab", "thiserror", - "vec_map", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix 0.38.21", + "wayland-backend", + "wayland-client", ] [[package]] @@ -765,16 +806,16 @@ dependencies = [ [[package]] name = "cocoa" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" dependencies = [ "bitflags 1.3.2", "block", "cocoa-foundation", "core-foundation", "core-graphics", - "foreign-types 0.3.2", + "foreign-types", "libc", "objc", ] @@ -866,14 +907,14 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" dependencies = [ "bitflags 1.3.2", "core-foundation", "core-graphics-types", - "foreign-types 0.3.2", + "foreign-types", "libc", ] @@ -969,6 +1010,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + [[package]] name = "custom_3d_glow" version = "0.1.0" @@ -1133,7 +1180,7 @@ dependencies = [ "percent-encoding", "pollster", "puffin", - "raw-window-handle", + "raw-window-handle 0.5.2", "ron", "serde", "static_assertions", @@ -1188,7 +1235,7 @@ dependencies = [ "egui", "log", "puffin", - "raw-window-handle", + "raw-window-handle 0.5.2", "serde", "smithay-clipboard", "web-time", @@ -1251,8 +1298,6 @@ dependencies = [ "resvg", "serde", "syntect", - "tiny-skia", - "usvg", ] [[package]] @@ -1267,9 +1312,9 @@ dependencies = [ "glutin", "glutin-winit", "log", - "memoffset 0.7.1", + "memoffset", "puffin", - "raw-window-handle", + "raw-window-handle 0.5.2", "wasm-bindgen", "web-sys", ] @@ -1527,15 +1572,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -1543,7 +1579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] @@ -1557,12 +1593,6 @@ dependencies = [ "syn 2.0.37", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1675,9 +1705,9 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177" dependencies = [ "libc", "winapi", @@ -1762,68 +1792,60 @@ dependencies = [ [[package]] name = "glutin" -version = "0.30.10" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc93b03242719b8ad39fb26ed2b01737144ce7bd4bfc7adadcef806596760fe" +checksum = "eca18d477e18c996c1fd1a50e04c6a745b67e2d512c7fb51f2757d9486a0e3ee" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cfg_aliases", "cgl", "core-foundation", "dispatch", "glutin_egl_sys", "glutin_glx_sys", - "glutin_wgl_sys 0.4.0", - "libloading 0.7.4", - "objc2", + "glutin_wgl_sys", + "icrate", + "libloading 0.8.0", + "objc2 0.4.1", "once_cell", - "raw-window-handle", - "wayland-sys 0.30.1", - "windows-sys 0.45.0", + "raw-window-handle 0.5.2", + "wayland-sys", + "windows-sys 0.48.0", "x11-dl", ] [[package]] name = "glutin-winit" -version = "0.3.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629a873fc04062830bfe8f97c03773bcd7b371e23bcc465d0a61448cd1588fa4" +checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" dependencies = [ "cfg_aliases", "glutin", - "raw-window-handle", + "raw-window-handle 0.5.2", "winit", ] [[package]] name = "glutin_egl_sys" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af784eb26c5a68ec85391268e074f0aa618c096eadb5d6330b0911cf34fe57c5" +checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" dependencies = [ "gl_generator", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "glutin_glx_sys" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b53cb5fe568964aa066a3ba91eac5ecbac869fb0842cd0dc9e412434f1a1494" +checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" dependencies = [ "gl_generator", "x11-dl", ] -[[package]] -name = "glutin_wgl_sys" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef89398e90033fc6bc65e9bd42fd29bbbfd483bda5b56dc5562f455550618165" -dependencies = [ - "gl_generator", -] - [[package]] name = "glutin_wgl_sys" version = "0.5.0" @@ -2033,6 +2055,17 @@ 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" @@ -2091,9 +2124,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", ] [[package]] @@ -2114,7 +2144,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.14", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -2213,17 +2243,11 @@ dependencies = [ "arrayvec", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -2268,9 +2292,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "litrs" @@ -2317,22 +2341,13 @@ checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memmap2" -version = "0.5.10" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.7.1" @@ -2351,7 +2366,7 @@ dependencies = [ "bitflags 2.4.0", "block", "core-graphics-types", - "foreign-types 0.5.0", + "foreign-types", "log", "objc", "paste", @@ -2395,18 +2410,6 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "multiple_viewports" version = "0.1.0" @@ -2446,15 +2449,17 @@ dependencies = [ [[package]] name = "ndk" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "jni-sys", + "log", "ndk-sys", "num_enum", - "raw-window-handle", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "thiserror", ] @@ -2466,38 +2471,13 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.4.1+23.1.7779620" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ "jni-sys", ] -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - [[package]] name = "nix" version = "0.26.4" @@ -2507,7 +2487,7 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset 0.7.1", + "memoffset", ] [[package]] @@ -2558,23 +2538,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.37", ] [[package]] @@ -2604,15 +2584,31 @@ 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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e1d07c6eab1ce8b6382b8e3c7246fe117ff3f8b34be065f5ebace6749fe845" + [[package]] name = "objc2" version = "0.3.0-beta.3.patch-leaks.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" dependencies = [ - "block2", - "objc-sys", - "objc2-encode", + "block2 0.2.0-alpha.6", + "objc-sys 0.2.0-beta.2", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys 0.3.1", + "objc2-encode 3.0.0", ] [[package]] @@ -2621,9 +2617,15 @@ version = "2.0.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" dependencies = [ - "objc-sys", + "objc-sys 0.2.0-beta.2", ] +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + [[package]] name = "objc_exception" version = "0.1.2" @@ -2788,7 +2790,7 @@ dependencies = [ "base64 0.21.4", "indexmap", "line-wrap", - "quick-xml", + "quick-xml 0.31.0", "serde", "time", ] @@ -2832,6 +2834,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "polling" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.21", + "tracing", + "windows-sys 0.48.0", +] + [[package]] name = "pollster" version = "0.3.0" @@ -2919,6 +2935,15 @@ dependencies = [ "puffin_http", ] +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.31.0" @@ -2979,6 +3004,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + [[package]] name = "rctree" version = "0.5.0" @@ -3059,7 +3090,7 @@ dependencies = [ "pico-args", "rgb", "svgtypes", - "tiny-skia", + "tiny-skia 0.8.4", "usvg", ] @@ -3081,7 +3112,7 @@ dependencies = [ "objc", "objc-foundation", "objc_id", - "raw-window-handle", + "raw-window-handle 0.5.2", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -3161,14 +3192,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys 0.4.7", + "linux-raw-sys 0.4.11", "windows-sys 0.48.0", ] @@ -3259,15 +3290,15 @@ dependencies = [ [[package]] name = "sctk-adwaita" -version = "0.5.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +checksum = "1729a30a469de249c6effc17ec8d039b0aa29b3af79b819b7f51cb6ab8046a90" dependencies = [ "ab_glyph", "log", "memmap2", "smithay-client-toolkit", - "tiny-skia", + "tiny-skia 0.11.2", ] [[package]] @@ -3396,31 +3427,47 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "smithay-client-toolkit" -version = "0.16.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" +checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "calloop", - "dlib", - "lazy_static", + "calloop-wayland-source", + "cursor-icon", + "libc", "log", "memmap2", - "nix 0.24.3", - "pkg-config", + "rustix 0.38.21", + "thiserror", + "wayland-backend", "wayland-client", + "wayland-csd-frame", "wayland-cursor", "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", ] [[package]] name = "smithay-clipboard" -version = "0.6.6" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" +checksum = "0bb62b280ce5a5cba847669933a0948d00904cf83845c944eae96a4738cea1a6" dependencies = [ + "libc", "smithay-client-toolkit", - "wayland-client", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", ] [[package]] @@ -3559,7 +3606,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.3.5", - "rustix 0.38.14", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -3648,7 +3695,21 @@ dependencies = [ "bytemuck", "cfg-if", "png", - "tiny-skia-path", + "tiny-skia-path 0.8.4", +] + +[[package]] +name = "tiny-skia" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b72a92a05db376db09fe6d50b7948d106011761c05a6a45e23e17ee9b556222" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path 0.11.2", ] [[package]] @@ -3662,6 +3723,17 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tiny-skia-path" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac3865b9708fc7e1961a65c3a4fa55e984272f33092d3c859929f887fceb647" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -3814,6 +3886,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.11" @@ -3893,12 +3971,6 @@ dependencies = [ "svgtypes", ] -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version-compare" version = "0.1.1" @@ -4000,87 +4072,111 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] -name = "wayland-client" -version = "0.29.5" +name = "wayland-backend" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" dependencies = [ - "bitflags 1.3.2", + "cc", "downcast-rs", - "libc", - "nix 0.24.3", + "nix", "scoped-tls", - "wayland-commons", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" +dependencies = [ + "bitflags 2.4.0", + "nix", + "wayland-backend", "wayland-scanner", - "wayland-sys 0.29.5", ] [[package]] -name = "wayland-commons" -version = "0.29.5" +name = "wayland-csd-frame" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys 0.29.5", + "bitflags 2.4.0", + "cursor-icon", + "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.29.5" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b" dependencies = [ - "nix 0.24.3", + "nix", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.29.5" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", + "wayland-backend", "wayland-client", - "wayland-commons", "wayland-scanner", ] [[package]] -name = "wayland-scanner" -version = "0.29.5" +name = "wayland-protocols-plasma" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" dependencies = [ - "proc-macro2", - "quote", - "xml-rs", + "bitflags 2.4.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", ] [[package]] -name = "wayland-sys" -version = "0.29.5" +name = "wayland-protocols-wlr" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "dlib", - "lazy_static", - "pkg-config", + "bitflags 2.4.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" +dependencies = [ + "proc-macro2", + "quick-xml 0.30.0", + "quote", ] [[package]] name = "wayland-sys" -version = "0.30.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", - "lazy_static", "log", + "once_cell", "pkg-config", ] @@ -4117,7 +4213,7 @@ dependencies = [ "log", "ndk-context", "objc", - "raw-window-handle", + "raw-window-handle 0.5.2", "url", "web-sys", ] @@ -4142,7 +4238,7 @@ dependencies = [ "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "smallvec", "static_assertions", "wasm-bindgen", @@ -4167,7 +4263,7 @@ dependencies = [ "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "rustc-hash", "smallvec", "thiserror", @@ -4191,7 +4287,7 @@ dependencies = [ "core-graphics-types", "d3d12", "glow 0.13.0", - "glutin_wgl_sys 0.5.0", + "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", "gpu-descriptor", @@ -4208,7 +4304,7 @@ dependencies = [ "parking_lot", "profiling", "range-alloc", - "raw-window-handle", + "raw-window-handle 0.5.2", "renderdoc-sys", "rustc-hash", "smallvec", @@ -4471,37 +4567,51 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winit" -version = "0.28.7" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" +checksum = "d25d662bb83b511acd839534bb2d88521b0bbc81440969cb077d23c4db9e62c7" dependencies = [ + "ahash", "android-activity", - "bitflags 1.3.2", + "atomic-waker", + "bitflags 2.4.0", + "bytemuck", + "calloop", "cfg_aliases", "core-foundation", "core-graphics", - "dispatch", - "instant", + "cursor-icon", + "icrate", + "js-sys", "libc", "log", - "mio", + "memmap2", "ndk", - "objc2", + "ndk-sys", + "objc2 0.4.1", "once_cell", "orbclient", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "redox_syscall 0.3.5", + "rustix 0.38.21", "sctk-adwaita", "smithay-client-toolkit", + "smol_str", + "unicode-segmentation", "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", "wayland-client", - "wayland-commons", "wayland-protocols", - "wayland-scanner", + "wayland-protocols-plasma", "web-sys", - "windows-sys 0.45.0", + "web-time", + "windows-sys 0.48.0", "x11-dl", + "x11rb", + "xkbcommon-dl", ] [[package]] @@ -4526,12 +4636,16 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.10.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" +checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" dependencies = [ + "as-raw-xcb-connection", "gethostname", - "nix 0.24.3", + "libc", + "libloading 0.7.4", + "nix", + "once_cell", "winapi", "winapi-wsapoll", "x11rb-protocol", @@ -4539,11 +4653,11 @@ dependencies = [ [[package]] name = "x11rb-protocol" -version = "0.10.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" +checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc" dependencies = [ - "nix 0.24.3", + "nix", ] [[package]] @@ -4561,10 +4675,29 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" dependencies = [ - "nix 0.26.4", + "nix", "winapi", ] +[[package]] +name = "xkbcommon-dl" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699" +dependencies = [ + "bitflags 2.4.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + [[package]] name = "xml-rs" version = "0.8.19" @@ -4610,7 +4743,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix 0.26.4", + "nix", "once_cell", "ordered-stream", "rand", diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 68c0bfd7bd8..e9f360802d4 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -131,7 +131,7 @@ image = { version = "0.24", default-features = false, features = [ "png", ] } # Needed for app icon raw-window-handle.workspace = true -winit = { version = "0.28.1", default-features = false } +winit = { version = "0.29.4", default-features = false, features = ["rwh_05"] } # optional native: directories-next = { version = "2", optional = true } @@ -142,14 +142,14 @@ pollster = { version = "0.3", optional = true } # needed for wgpu # we can expose these to user so that they can select which backends they want to enable to avoid compiling useless deps. # this can be done at the same time we expose x11/wayland features of winit crate. -glutin = { version = "0.30", optional = true } -glutin-winit = { version = "0.3.0", optional = true } +glutin = { version = "0.31", optional = true } +glutin-winit = { version = "0.4", optional = true } puffin = { workspace = true, optional = true } wgpu = { workspace = true, optional = true } # mac: [target.'cfg(any(target_os = "macos"))'.dependencies] -cocoa = "0.24.1" # Stuck on old version until we update to winit 0.29 +cocoa = "0.25.0" objc = "0.2.7" # windows: diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 5748d4875ca..95ed9feaccd 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -298,7 +298,7 @@ pub struct NativeOptions { /// /// This feature was introduced in . /// - /// When `true`, [`winit::platform::run_return::EventLoopExtRunReturn::run_return`] is used. + /// When `true`, [`winit::platform::run_on_demand::EventLoopExtRunOnDemand`] is used. /// When `false`, [`winit::event_loop::EventLoop::run`] is used. pub run_and_return: bool, diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index fd6437e305e..de226bc8e0a 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -325,6 +325,11 @@ pub enum Error { #[error("winit error: {0}")] Winit(#[from] winit::error::OsError), + /// An error from [`winit::event_loop::EventLoop`]. + #[cfg(not(target_arch = "wasm32"))] + #[error("winit EventLoopError: {0}")] + WinitEventLoop(#[from] winit::error::EventLoopError), + /// An error from [`glutin`] when using [`glow`]. #[cfg(all(feature = "glow", not(target_arch = "wasm32")))] #[error("glutin error: {0}")] diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 7ad2f9dfdd1..e80785b5d37 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -231,7 +231,7 @@ impl EpiIntegration { &mut self, window: &winit::window::Window, egui_winit: &mut egui_winit::State, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResponse { crate::profile_function!(egui_winit::short_window_event_description(event)); diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 967ff640ffa..cc77972be97 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -1,8 +1,17 @@ +//! Note that this file contains code very similar to [`wgpu_integration`]. +//! When making changes to one you often also want to apply it to the other. +//! +//! This is also very complex code, and not very pretty. +//! There is a bunch of improvements we could do, +//! like removing a bunch of `unwraps`. + use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant}; use glutin::{ + config::GlConfig, + context::NotCurrentGlContext, display::GetGlDisplay, - prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext}, + prelude::{GlDisplay, PossiblyCurrentGlContext}, surface::GlSurface, }; use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _}; @@ -112,6 +121,8 @@ struct Viewport { /// None for immediate viewports. viewport_ui_cb: Option>, + // These three live and die together. + // TODO(emilk): clump them together into one struct! gl_surface: Option>, window: Option>, egui_winit: Option, @@ -160,12 +171,12 @@ impl GlowWinitApp { }; // Creates the window - must come before we create our glow context - glutin_window_context.on_resume(event_loop)?; + glutin_window_context.initialize_window(ViewportId::ROOT, event_loop)?; - if let Some(viewport) = glutin_window_context.viewports.get(&ViewportId::ROOT) { - if let Some(window) = &viewport.window { - epi_integration::apply_window_settings(window, window_settings); - } + { + let viewport = &glutin_window_context.viewports[&ViewportId::ROOT]; + let window = viewport.window.as_ref().unwrap(); // Can't fail - we just called `initialize_all_viewports` + epi_integration::apply_window_settings(window, window_settings); } let gl = unsafe { @@ -390,9 +401,13 @@ impl WinitApp for GlowWinitApp { } } - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + fn run_ui_and_paint( + &mut self, + event_loop: &EventLoopWindowTarget, + window_id: WindowId, + ) -> EventResult { if let Some(running) = &mut self.running { - running.run_ui_and_paint(window_id) + running.run_ui_and_paint(event_loop, window_id) } else { EventResult::Wait } @@ -401,29 +416,27 @@ impl WinitApp for GlowWinitApp { fn on_event( &mut self, event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, UserEvent>, + 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 whatever you need. - running.glutin.borrow_mut().on_resume(event_loop)?; + // Not the first resume event. Create all outstanding windows. + running + .glutin + .borrow_mut() + .initialize_all_windows(event_loop); running } else { - // first resume event. - // we can actually move this outside of event loop. - // and just run the on_resume fn of gl_window + // First resume event. Created our root window etc. self.init_run_state(event_loop)? }; - let window_id = running - .glutin - .borrow() - .window_from_viewport - .get(&ViewportId::ROOT) - .copied(); - EventResult::RepaintNow(window_id.unwrap()) + let window_id = running.glutin.borrow().window_from_viewport[&ViewportId::ROOT]; + EventResult::RepaintNow(window_id) } winit::event::Event::Suspended => { @@ -433,15 +446,6 @@ impl WinitApp for GlowWinitApp { EventResult::Wait } - winit::event::Event::MainEventsCleared => { - if let Some(running) = &self.running { - if let Err(err) = running.glutin.borrow_mut().on_resume(event_loop) { - log::warn!("on_resume failed {err}"); - } - } - EventResult::Wait - } - winit::event::Event::WindowEvent { event, window_id } => { if let Some(running) = &mut self.running { running.on_window_event(*window_id, event) @@ -477,7 +481,11 @@ impl WinitApp for GlowWinitApp { } impl GlowWinitRunning { - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + fn run_ui_and_paint( + &mut self, + event_loop: &EventLoopWindowTarget, + window_id: WindowId, + ) -> EventResult { crate::profile_function!(); let Some(viewport_id) = self @@ -666,7 +674,7 @@ impl GlowWinitRunning { std::thread::sleep(std::time::Duration::from_millis(10)); } - glutin.handle_viewport_output(&integration.egui_ctx, viewport_output); + glutin.handle_viewport_output(event_loop, &integration.egui_ctx, viewport_output); if integration.should_close() { EventResult::Exit @@ -678,7 +686,7 @@ impl GlowWinitRunning { fn on_window_event( &mut self, window_id: WindowId, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResult { crate::profile_function!(egui_winit::short_window_event_description(event)); @@ -717,13 +725,6 @@ impl GlowWinitRunning { } } - winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - if let Some(viewport_id) = viewport_id { - repaint_asap = true; - glutin.resize(viewport_id, **new_inner_size); - } - } - winit::event::WindowEvent::CloseRequested => { if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() { log::debug!( @@ -768,7 +769,11 @@ impl GlowWinitRunning { { event_response = self.integration.on_window_event(window, egui_winit, event); } + } else { + log::trace!("Ignoring event: no viewport for {viewport_id:?}"); } + } else { + log::trace!("Ignoring event: no viewport_id"); } if event_response.repaint { @@ -855,7 +860,7 @@ impl GlutinWindowContext { // Create GL display. This may probably create a window too on most platforms. Definitely on `MS windows`. Never on Android. 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::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 + .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( egui_ctx, event_loop, @@ -968,37 +973,29 @@ impl GlutinWindowContext { focused_viewport: Some(ViewportId::ROOT), }; - slf.on_resume(event_loop)?; + slf.initialize_window(ViewportId::ROOT, event_loop)?; Ok(slf) } - /// This will be run after `new`. on android, it might be called multiple times over the course of the app's lifetime. - /// roughly, - /// 1. check if window already exists. otherwise, create one now. - /// 2. create attributes for surface creation. - /// 3. create surface. - /// 4. make surface and context current. + /// Create a surface, window, and winit integration for all viewports lacking any of that. /// - /// we presently assume that we will - fn on_resume(&mut self, event_loop: &EventLoopWindowTarget) -> Result<()> { + /// Errors will be logged. + fn initialize_all_windows(&mut self, event_loop: &EventLoopWindowTarget) { crate::profile_function!(); - let viewports: Vec = self - .viewports - .iter() - .filter(|(_, viewport)| viewport.gl_surface.is_none()) - .map(|(id, _)| *id) - .collect(); + let viewports: Vec = self.viewports.keys().copied().collect(); for viewport_id in viewports { - self.init_viewport(viewport_id, event_loop)?; + if let Err(err) = self.initialize_window(viewport_id, event_loop) { + log::error!("Failed to initialize a window for viewport {viewport_id:?}: {err}"); + } } - Ok(()) } + /// Create a surface, window, and winit integration for the viewport, if missing. #[allow(unsafe_code)] - pub(crate) fn init_viewport( + pub(crate) fn initialize_window( &mut self, viewport_id: ViewportId, event_loop: &EventLoopWindowTarget, @@ -1013,12 +1010,16 @@ impl GlutinWindowContext { let window = if let Some(window) = &mut viewport.window { window } else { - log::trace!("Window doesn't exist yet. Creating one now with finalize_window"); + log::debug!("Creating a window for viewport {viewport_id:?}"); let window_builder = egui_winit::create_winit_window_builder( &self.egui_ctx, event_loop, viewport.builder.clone(), ); + if window_builder.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)?; egui_winit::apply_viewport_builder_to_window( @@ -1031,7 +1032,20 @@ impl GlutinWindowContext { viewport.window.insert(Rc::new(window)) }; - { + viewport.egui_winit.get_or_insert_with(|| { + log::debug!("Initializing egui_winit for viewport {viewport_id:?}"); + egui_winit::State::new( + self.egui_ctx.clone(), + viewport_id, + event_loop, + Some(window.scale_factor() as f32), + self.max_texture_side, + ) + }); + + if viewport.gl_surface.is_none() { + log::debug!("Creating a gl_surface for viewport {viewport_id:?}"); + // surface attributes let (width_px, height_px): (u32, u32) = window.inner_size().into(); let width_px = std::num::NonZeroU32::new(width_px.at_least(1)).unwrap(); @@ -1071,24 +1085,14 @@ impl GlutinWindowContext { // we will reach this point only once in most platforms except android. // create window/surface/make context current once and just use them forever. - viewport.egui_winit.get_or_insert_with(|| { - egui_winit::State::new( - self.egui_ctx.clone(), - viewport_id, - event_loop, - Some(window.scale_factor() as f32), - self.max_texture_side, - ) - }); - viewport.gl_surface = Some(gl_surface); + self.current_gl_context = Some(current_gl_context); - self.viewport_from_window - .insert(window.id(), viewport.ids.this); - self.window_from_viewport - .insert(viewport.ids.this, window.id()); } + self.viewport_from_window.insert(window.id(), viewport_id); + self.window_from_viewport.insert(viewport_id, window.id()); + Ok(()) } @@ -1153,6 +1157,7 @@ impl GlutinWindowContext { fn handle_viewport_output( &mut self, + event_loop: &EventLoopWindowTarget, egui_ctx: &egui::Context, viewport_output: ViewportIdMap, ) { @@ -1197,6 +1202,9 @@ impl GlutinWindowContext { } } + // Create windows for any new viewports: + self.initialize_all_windows(event_loop); + // GC old viewports self.viewports .retain(|id, _| active_viewports_ids.contains(id)); @@ -1295,10 +1303,12 @@ fn render_immediate_viewport( viewport_ui_cb, } = immediate_viewport; + let viewport_id = ids.this; + { let mut glutin = glutin.borrow_mut(); - let viewport = initialize_or_update_viewport( + initialize_or_update_viewport( egui_ctx, &mut glutin.viewports, ids, @@ -1308,17 +1318,18 @@ fn render_immediate_viewport( None, ); - if viewport.gl_surface.is_none() { - glutin - .init_viewport(ids.this, event_loop) - .expect("Failed to initialize window in egui::Context::show_viewport_immediate"); + if let Err(err) = glutin.initialize_window(viewport_id, event_loop) { + log::error!( + "Failed to initialize a window for immediate viewport {viewport_id:?}: {err}" + ); + return; } } let input = { let mut glutin = glutin.borrow_mut(); - let Some(viewport) = glutin.viewports.get_mut(&ids.this) else { + let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else { return; }; let (Some(egui_winit), Some(window)) = (&mut viewport.egui_winit, &viewport.window) else { @@ -1362,7 +1373,7 @@ fn render_immediate_viewport( .. } = &mut *glutin; - let Some(viewport) = viewports.get_mut(&ids.this) else { + let Some(viewport) = viewports.get_mut(&viewport_id) else { return; }; @@ -1423,7 +1434,7 @@ fn render_immediate_viewport( egui_winit.handle_platform_output(window, platform_output); - glutin.handle_viewport_output(egui_ctx, viewport_output); + glutin.handle_viewport_output(event_loop, egui_ctx, viewport_output); } #[cfg(feature = "__screenshot")] diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 5e3dc91554c..95e355a1048 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -1,10 +1,3 @@ -//! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`]. -//! When making changes to one you often also want to apply it to the other. -//! -//! This is also very complex code, and not very pretty. -//! There is a bunch of improvements we could do, -//! like removing a bunch of `unwraps`. - use std::{cell::RefCell, time::Instant}; use winit::event_loop::{EventLoop, EventLoopBuilder}; @@ -34,12 +27,12 @@ fn create_event_loop_builder( event_loop_builder } -fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop { +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"); - builder.build() + Ok(builder.build()?) } /// Access a thread-local event loop. @@ -49,16 +42,20 @@ fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop( mut native_options: epi::NativeOptions, f: impl FnOnce(&mut EventLoop, epi::NativeOptions) -> R, -) -> R { +) -> Result { thread_local!(static EVENT_LOOP: RefCell>> = RefCell::new(None)); EVENT_LOOP.with(|event_loop| { // Since we want to reference NativeOptions when creating the EventLoop we can't // do that as part of the lazy thread local storage initialization and so we instead // create the event loop lazily here - let mut event_loop = event_loop.borrow_mut(); - let event_loop = event_loop.get_or_insert_with(|| create_event_loop(&mut native_options)); - f(event_loop, native_options) + let mut event_loop_lock = event_loop.borrow_mut(); + let event_loop = if let Some(event_loop) = &mut *event_loop_lock { + event_loop + } else { + event_loop_lock.insert(create_event_loop(&mut native_options)?) + }; + Ok(f(event_loop, native_options)) }) } @@ -67,31 +64,39 @@ fn run_and_return( event_loop: &mut EventLoop, mut winit_app: impl WinitApp, ) -> Result<()> { - use winit::{event_loop::ControlFlow, platform::run_return::EventLoopExtRunReturn as _}; + use winit::{event_loop::ControlFlow, platform::run_on_demand::EventLoopExtRunOnDemand}; - log::debug!("Entering the winit event loop (run_return)…"); + log::debug!("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_return(|event, event_loop, control_flow| { + event_loop.run_on_demand(|event, event_loop_window_target| { crate::profile_scope!("winit_event", short_event_description(&event)); + log::trace!("winit event: {event:?}"); + + if matches!(event, winit::event::Event::AboutToWait) { + return; // early-out: don't trigger another wait + } + let event_result = match &event { - winit::event::Event::LoopDestroyed => { - // On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name), + 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::LoopDestroyed - saving app state…"); + log::debug!("Received Event::LoopExiting - saving app state…"); winit_app.save_and_destroy(); - *control_flow = ControlFlow::Exit; return; } - winit::event::Event::RedrawRequested(window_id) => { + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::RedrawRequested, + window_id, + } => { windows_next_repaint_times.remove(window_id); - winit_app.run_ui_and_paint(*window_id) + winit_app.run_ui_and_paint(event_loop_window_target, *window_id) } winit::event::Event::UserEvent(UserEvent::RequestRepaint { @@ -120,8 +125,11 @@ fn run_and_return( EventResult::Wait } - event => match winit_app.on_event(event_loop, event) { - Ok(event_result) => event_result, + event => match winit_app.on_event(event_loop_window_target, event) { + Ok(event_result) => { + log::trace!("event_result: {event_result:?}"); + event_result + } Err(err) => { log::error!("Exiting because of error: {err} during event {event:?}"); returned_result = Err(err); @@ -132,21 +140,28 @@ fn run_and_return( match event_result { EventResult::Wait => { - control_flow.set_wait(); + event_loop_window_target.set_control_flow(ControlFlow::Wait); } EventResult::RepaintNow(window_id) => { - log::trace!("Repaint caused by {}", short_event_description(&event)); + 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(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::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) => { @@ -160,45 +175,35 @@ fn run_and_return( EventResult::Exit => { log::debug!("Asking to exit event loop…"); winit_app.save_and_destroy(); - *control_flow = ControlFlow::Exit; + event_loop_window_target.exit(); return; } } let mut next_repaint_time = windows_next_repaint_times.values().min().copied(); - // This is for not duplicating redraw requests - use winit::event::Event; - if matches!( - event, - Event::RedrawEventsCleared | Event::RedrawRequested(_) | Event::Resumed - ) { - windows_next_repaint_times.retain(|window_id, repaint_time| { - if Instant::now() < *repaint_time { - return true; - }; - - next_repaint_time = None; - control_flow.set_poll(); - - if let Some(window) = winit_app.window(*window_id) { - log::trace!("request_redraw for {window_id:?}"); - window.request_redraw(); - true - } else { - false - } - }); - } + 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); + + if let Some(window) = winit_app.window(*window_id) { + log::trace!("request_redraw for {window_id:?}"); + window.request_redraw(); + true + } else { + log::trace!("No window found for {window_id:?}"); + false + } + }); if let Some(next_repaint_time) = next_repaint_time { - let time_until_next = next_repaint_time.saturating_duration_since(Instant::now()); - if time_until_next < std::time::Duration::from_secs(10_000) { - log::trace!("WaitUntil {time_until_next:?}"); - } - control_flow.set_wait_until(next_repaint_time); + event_loop_window_target.set_control_flow(ControlFlow::WaitUntil(next_repaint_time)); }; - }); + })?; log::debug!("eframe window closed"); @@ -211,32 +216,47 @@ fn run_and_return( // we only apply this approach on Windows to minimize the affect. #[cfg(target_os = "windows")] { - event_loop.run_return(|_, _, control_flow| { - control_flow.set_exit(); - }); + event_loop + .run_on_demand(|_, event_loop_window_target| { + event_loop_window_target.exit(); + }) + .ok(); } returned_result } -fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + 'static) -> ! { +fn run_and_exit( + event_loop: EventLoop, + mut winit_app: impl WinitApp + 'static, +) -> Result<()> { + use winit::event_loop::ControlFlow; log::debug!("Entering the winit event loop (run)…"); // When to repaint what window let mut windows_next_repaint_times = HashMap::default(); - event_loop.run(move |event, event_loop, control_flow| { + event_loop.run(move |event, event_loop_window_target| { crate::profile_scope!("winit_event", short_event_description(&event)); + log::trace!("winit event: {event:?}"); + + if matches!(event, winit::event::Event::AboutToWait) { + return; // early-out: don't trigger another wait + } + let event_result = match &event { - winit::event::Event::LoopDestroyed => { - log::debug!("Received Event::LoopDestroyed"); + winit::event::Event::LoopExiting => { + log::debug!("Received Event::LoopExiting"); EventResult::Exit } - winit::event::Event::RedrawRequested(window_id) => { + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::RedrawRequested, + window_id, + } => { windows_next_repaint_times.remove(window_id); - winit_app.run_ui_and_paint(*window_id) + winit_app.run_ui_and_paint(event_loop_window_target, *window_id) } winit::event::Event::UserEvent(UserEvent::RequestRepaint { @@ -264,8 +284,11 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + EventResult::Wait } - event => match winit_app.on_event(event_loop, event) { - Ok(event_result) => event_result, + 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:?}"); } @@ -274,22 +297,22 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + match event_result { EventResult::Wait => { - control_flow.set_wait(); + event_loop_window_target.set_control_flow(ControlFlow::Wait); } EventResult::RepaintNow(window_id) => { - log::trace!("Repaint caused by {}", short_event_description(&event)); + 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); - winit_app.run_ui_and_paint(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::RepaintNext(window_id) => { - log::trace!("Repaint caused by {}", short_event_description(&event)); + log::trace!("RepaintNext caused by {}", short_event_description(&event)); windows_next_repaint_times.insert(window_id, Instant::now()); } EventResult::RepaintAt(window_id, repaint_time) => { @@ -303,6 +326,8 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + EventResult::Exit => { log::debug!("Quitting - saving app state…"); winit_app.save_and_destroy(); + + log::debug!("Exiting with return code 0"); #[allow(clippy::exit)] std::process::exit(0); } @@ -310,36 +335,25 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + let mut next_repaint_time = windows_next_repaint_times.values().min().copied(); - // This is for not duplicating redraw requests - use winit::event::Event; - if matches!( - event, - Event::RedrawEventsCleared | Event::RedrawRequested(_) | Event::Resumed - ) { - windows_next_repaint_times.retain(|window_id, repaint_time| { - if Instant::now() < *repaint_time { - return true; - } - - next_repaint_time = None; - control_flow.set_poll(); + windows_next_repaint_times.retain(|window_id, repaint_time| { + if Instant::now() < *repaint_time { + return true; // not yet ready + } - if let Some(window) = winit_app.window(*window_id) { - log::trace!("request_redraw for {window_id:?}"); - window.request_redraw(); - true - } else { - false - } - }); - } + next_repaint_time = None; + event_loop_window_target.set_control_flow(ControlFlow::Poll); - if let Some(next_repaint_time) = next_repaint_time { - let time_until_next = next_repaint_time.saturating_duration_since(Instant::now()); - if time_until_next < std::time::Duration::from_secs(10_000) { - log::trace!("WaitUntil {time_until_next:?}"); + if let Some(window) = winit_app.window(*window_id) { + log::trace!("request_redraw for {window_id:?}"); + window.request_redraw(); + true + } else { + log::trace!("No window found for {window_id:?}"); + false } + }); + if let Some(next_repaint_time) = next_repaint_time { // WaitUntil seems to not work on iOS #[cfg(target_os = "ios")] winit_app @@ -350,9 +364,13 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + .map(|window| window.request_redraw()) }); - control_flow.set_wait_until(next_repaint_time); + event_loop_window_target.set_control_flow(ControlFlow::WaitUntil(next_repaint_time)); }; - }) + })?; + + log::debug!("winit event loop unexpectedly returned"); + + Ok(()) } // ---------------------------------------------------------------------------- @@ -370,12 +388,12 @@ pub fn run_glow( return with_event_loop(native_options, |event_loop, native_options| { let glow_eframe = GlowWinitApp::new(event_loop, app_name, native_options, app_creator); run_and_return(event_loop, glow_eframe) - }); + })?; } - let event_loop = create_event_loop(&mut native_options); + let event_loop = create_event_loop(&mut native_options)?; let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator); - run_and_exit(event_loop, glow_eframe); + run_and_exit(event_loop, glow_eframe) } // ---------------------------------------------------------------------------- @@ -393,10 +411,10 @@ pub fn run_wgpu( return with_event_loop(native_options, |event_loop, native_options| { let wgpu_eframe = WgpuWinitApp::new(event_loop, app_name, native_options, app_creator); run_and_return(event_loop, wgpu_eframe) - }); + })?; } - let event_loop = create_event_loop(&mut native_options); + let event_loop = create_event_loop(&mut native_options)?; let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator); - run_and_exit(event_loop, wgpu_eframe); + run_and_exit(event_loop, wgpu_eframe) } diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index 8b859f5dda3..8f82b7380b7 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -1,3 +1,10 @@ +//! Note that this file contains code very similar to [`glow_integration`]. +//! When making changes to one you often also want to apply it to the other. +//! +//! This is also very complex code, and not very pretty. +//! There is a bunch of improvements we could do, +//! like removing a bunch of `unwraps`. + use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant}; use parking_lot::Mutex; @@ -109,7 +116,8 @@ impl WgpuWinitApp { } } - fn build_windows(&mut self, event_loop: &EventLoopWindowTarget) { + /// Create a window for all viewports lacking one. + fn initialized_all_windows(&mut self, event_loop: &EventLoopWindowTarget) { let Some(running) = &mut self.running else { return; }; @@ -122,14 +130,12 @@ impl WgpuWinitApp { } = &mut *shared; for viewport in viewports.values_mut() { - if viewport.window.is_none() { - viewport.init_window( - &running.integration.egui_ctx, - viewport_from_window, - painter, - event_loop, - ); - } + viewport.initialize_window( + event_loop, + &running.integration.egui_ctx, + viewport_from_window, + painter, + ); } } @@ -350,7 +356,13 @@ impl WinitApp for WgpuWinitApp { } } - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + fn run_ui_and_paint( + &mut self, + event_loop: &EventLoopWindowTarget, + window_id: WindowId, + ) -> EventResult { + self.initialized_all_windows(event_loop); + if let Some(running) = &mut self.running { running.run_ui_and_paint(window_id) } else { @@ -361,14 +373,16 @@ impl WinitApp for WgpuWinitApp { fn on_event( &mut self, event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, UserEvent>, + event: &winit::event::Event, ) -> Result { crate::profile_function!(winit_integration::short_event_description(event)); - self.build_windows(event_loop); + self.initialized_all_windows(event_loop); Ok(match event { winit::event::Event::Resumed => { + log::debug!("Event::Resumed"); + let running = if let Some(running) = &self.running { running } else { @@ -660,7 +674,7 @@ impl WgpuWinitRunning { fn on_window_event( &mut self, window_id: WindowId, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResult { crate::profile_function!(egui_winit::short_window_event_description(event)); @@ -709,18 +723,6 @@ impl WgpuWinitRunning { } } - winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - use std::num::NonZeroU32; - if let (Some(width), Some(height), Some(viewport_id)) = ( - NonZeroU32::new(new_inner_size.width), - NonZeroU32::new(new_inner_size.height), - viewport_id, - ) { - repaint_asap = true; - shared.painter.on_window_resized(viewport_id, width, height); - } - } - winit::event::WindowEvent::CloseRequested => { if viewport_id == Some(ViewportId::ROOT) && integration.should_close() { log::debug!( @@ -775,13 +777,18 @@ impl WgpuWinitRunning { } impl Viewport { - fn init_window( + /// Create winit window, if needed. + fn initialize_window( &mut self, + event_loop: &EventLoopWindowTarget, egui_ctx: &egui::Context, windows_id: &mut HashMap, painter: &mut egui_wgpu::winit::Painter, - event_loop: &EventLoopWindowTarget, ) { + if self.window.is_some() { + return; // we already have one + } + crate::profile_function!(); let viewport_id = self.ids.this; @@ -870,7 +877,7 @@ fn render_immediate_viewport( None, ); if viewport.window.is_none() { - viewport.init_window(egui_ctx, viewport_from_window, painter, 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 53de13cc44d..a7e4bb384b3 100644 --- a/crates/eframe/src/native/winit_integration.rs +++ b/crates/eframe/src/native/winit_integration.rs @@ -74,12 +74,16 @@ pub trait WinitApp { fn save_and_destroy(&mut self); - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult; + fn run_ui_and_paint( + &mut self, + event_loop: &EventLoopWindowTarget, + window_id: WindowId, + ) -> EventResult; fn on_event( &mut self, event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, UserEvent>, + event: &winit::event::Event, ) -> crate::Result; } @@ -117,11 +121,9 @@ pub fn system_theme(window: &Window, options: &crate::NativeOptions) -> Option) -> &'static str { - use winit::event::Event; - +pub fn short_event_description(event: &winit::event::Event) -> &'static str { match event { - Event::UserEvent(user_event) => match user_event { + winit::event::Event::UserEvent(user_event) => match user_event { UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint", #[cfg(feature = "accesskit")] UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest", diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index f5aab09a367..42cca65ed21 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -13,7 +13,7 @@ pub struct AppRunner { app: Box, pub(crate) needs_repaint: std::sync::Arc, last_save_time: f64, - pub(crate) text_cursor_pos: Option, + pub(crate) ime: Option, pub(crate) mutable_text_under_cursor: bool, // Output for the last run: @@ -112,7 +112,7 @@ impl AppRunner { app, needs_repaint, last_save_time: now_sec(), - text_cursor_pos: None, + ime: None, mutable_text_under_cursor: false, textures_delta: Default::default(), clipped_primitives: None, @@ -244,7 +244,7 @@ impl AppRunner { copied_text, events: _, // already handled mutable_text_under_cursor, - text_cursor_pos, + ime, #[cfg(feature = "accesskit")] accesskit_update: _, // not currently implemented } = platform_output; @@ -264,9 +264,9 @@ impl AppRunner { self.mutable_text_under_cursor = mutable_text_under_cursor; - if self.text_cursor_pos != text_cursor_pos { - super::text_agent::move_text_cursor(text_cursor_pos, self.canvas_id()); - self.text_cursor_pos = text_cursor_pos; + if self.ime != ime { + super::text_agent::move_text_cursor(ime, self.canvas_id()); + self.ime = ime; } } } diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 9bb5c0f885a..2418bed95c5 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -91,6 +91,7 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa if let Some(key) = egui_key { runner.input.raw.events.push(egui::Event::Key { key, + physical_key: None, // TODO pressed: true, repeat: false, // egui will fill this in for us! modifiers, @@ -157,6 +158,7 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa if let Some(key) = translate_key(&event.key()) { runner.input.raw.events.push(egui::Event::Key { key, + physical_key: None, // TODO pressed: false, repeat: false, modifiers, diff --git a/crates/eframe/src/web/input.rs b/crates/eframe/src/web/input.rs index 33ea30b6dce..96cad32e20f 100644 --- a/crates/eframe/src/web/input.rs +++ b/crates/eframe/src/web/input.rs @@ -112,91 +112,7 @@ pub fn should_ignore_key(key: &str) -> bool { /// Web sends all keys as strings, so it is up to us to figure out if it is /// a real text input or the name of a key. pub fn translate_key(key: &str) -> Option { - use egui::Key; - - match key { - "ArrowDown" => Some(Key::ArrowDown), - "ArrowLeft" => Some(Key::ArrowLeft), - "ArrowRight" => Some(Key::ArrowRight), - "ArrowUp" => Some(Key::ArrowUp), - - "Esc" | "Escape" => Some(Key::Escape), - "Tab" => Some(Key::Tab), - "Backspace" => Some(Key::Backspace), - "Enter" => Some(Key::Enter), - "Space" | " " => Some(Key::Space), - - "Help" | "Insert" => Some(Key::Insert), - "Delete" => Some(Key::Delete), - "Home" => Some(Key::Home), - "End" => Some(Key::End), - "PageUp" => Some(Key::PageUp), - "PageDown" => Some(Key::PageDown), - - "-" => Some(Key::Minus), - "+" | "=" => Some(Key::PlusEquals), - - "0" => Some(Key::Num0), - "1" => Some(Key::Num1), - "2" => Some(Key::Num2), - "3" => Some(Key::Num3), - "4" => Some(Key::Num4), - "5" => Some(Key::Num5), - "6" => Some(Key::Num6), - "7" => Some(Key::Num7), - "8" => Some(Key::Num8), - "9" => Some(Key::Num9), - - "a" | "A" => Some(Key::A), - "b" | "B" => Some(Key::B), - "c" | "C" => Some(Key::C), - "d" | "D" => Some(Key::D), - "e" | "E" => Some(Key::E), - "f" | "F" => Some(Key::F), - "g" | "G" => Some(Key::G), - "h" | "H" => Some(Key::H), - "i" | "I" => Some(Key::I), - "j" | "J" => Some(Key::J), - "k" | "K" => Some(Key::K), - "l" | "L" => Some(Key::L), - "m" | "M" => Some(Key::M), - "n" | "N" => Some(Key::N), - "o" | "O" => Some(Key::O), - "p" | "P" => Some(Key::P), - "q" | "Q" => Some(Key::Q), - "r" | "R" => Some(Key::R), - "s" | "S" => Some(Key::S), - "t" | "T" => Some(Key::T), - "u" | "U" => Some(Key::U), - "v" | "V" => Some(Key::V), - "w" | "W" => Some(Key::W), - "x" | "X" => Some(Key::X), - "y" | "Y" => Some(Key::Y), - "z" | "Z" => Some(Key::Z), - - "F1" => Some(Key::F1), - "F2" => Some(Key::F2), - "F3" => Some(Key::F3), - "F4" => Some(Key::F4), - "F5" => Some(Key::F5), - "F6" => Some(Key::F6), - "F7" => Some(Key::F7), - "F8" => Some(Key::F8), - "F9" => Some(Key::F9), - "F10" => Some(Key::F10), - "F11" => Some(Key::F11), - "F12" => Some(Key::F12), - "F13" => Some(Key::F13), - "F14" => Some(Key::F14), - "F15" => Some(Key::F15), - "F16" => Some(Key::F16), - "F17" => Some(Key::F17), - "F18" => Some(Key::F18), - "F19" => Some(Key::F19), - "F20" => Some(Key::F20), - - _ => None, - } + egui::Key::from_name(key) } pub fn modifiers_from_event(event: &web_sys::KeyboardEvent) -> egui::Modifiers { diff --git a/crates/eframe/src/web/text_agent.rs b/crates/eframe/src/web/text_agent.rs index d4f3b5ca3c6..0bf8b532b7c 100644 --- a/crates/eframe/src/web/text_agent.rs +++ b/crates/eframe/src/web/text_agent.rs @@ -205,11 +205,13 @@ fn is_mobile() -> Option { // candidate window moves following text element (agent), // so it appears that the IME candidate window moves with text cursor. // On mobile devices, there is no need to do that. -pub fn move_text_cursor(cursor: Option, canvas_id: &str) -> Option<()> { +pub fn move_text_cursor(ime: Option, canvas_id: &str) -> Option<()> { let style = text_agent().style(); - // Note: movint agent on mobile devices will lead to unpredictable scroll. + // Note: moving agent on mobile devices will lead to unpredictable scroll. if is_mobile() == Some(false) { - cursor.as_ref().and_then(|&egui::Pos2 { x, y }| { + ime.as_ref().and_then(|ime| { + let egui::Pos2 { x, y } = ime.cursor_rect.left_top(); + let canvas = canvas_element(canvas_id)?; let bounding_rect = text_agent().get_bounding_client_rect(); let y = (y + (canvas.scroll_top() + canvas.offset_top()) as f32) diff --git a/crates/egui-wgpu/Cargo.toml b/crates/egui-wgpu/Cargo.toml index 22200bafdbe..546c1b32987 100644 --- a/crates/egui-wgpu/Cargo.toml +++ b/crates/egui-wgpu/Cargo.toml @@ -51,7 +51,7 @@ wgpu.workspace = true ## Enable this when generating docs. document-features = { version = "0.2", optional = true } -winit = { version = "0.28", default-features = false, optional = true } +winit = { version = "0.29.4", default-features = false, optional = true, features = ["rwh_05"] } # Native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index 178d99cdaf2..8e8a8108ab3 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -61,12 +61,12 @@ egui = { version = "0.24.1", path = "../egui", default-features = false, feature log = { version = "0.4", features = ["std"] } raw-window-handle.workspace = true web-time = { version = "0.2" } # We use web-time so we can (maybe) compile for web -winit = { version = "0.28", default-features = false } +winit = { version = "0.29.4", default-features = false, features = ["rwh_05"] } #! ### Optional dependencies # feature accesskit -accesskit_winit = { version = "0.15.0", optional = true } +accesskit_winit = { version = "0.16.0", optional = true } ## Enable this when generating docs. document-features = { version = "0.2", optional = true } @@ -76,7 +76,7 @@ serde = { version = "1.0", optional = true, features = ["derive"] } webbrowser = { version = "0.8.3", 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.6.3", optional = true } +smithay-clipboard = { version = "0.7.0", optional = true } [target.'cfg(not(target_os = "android"))'.dependencies] arboard = { version = "3.2", optional = true, default-features = false } diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 11f672bb933..da4d2bc4358 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -232,7 +232,7 @@ impl State { pub fn on_window_event( &mut self, window: &Window, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResponse { crate::profile_function!(short_window_event_description(event)); @@ -295,25 +295,7 @@ impl State { consumed, } } - WindowEvent::ReceivedCharacter(ch) => { - // On Mac we get here when the user presses Cmd-C (copy), ctrl-W, etc. - // We need to ignore these characters that are side-effects of commands. - let is_mac_cmd = cfg!(target_os = "macos") - && (self.egui_input.modifiers.ctrl || self.egui_input.modifiers.mac_cmd); - let consumed = if is_printable_char(*ch) && !is_mac_cmd { - self.egui_input - .events - .push(egui::Event::Text(ch.to_string())); - self.egui_ctx.wants_keyboard_input() - } else { - false - }; - EventResponse { - repaint: true, - consumed, - } - } WindowEvent::Ime(ime) => { // on Mac even Cmd-C is pressed during ime, a `c` is pushed to Preedit. // So no need to check is_mac_cmd. @@ -353,11 +335,12 @@ impl State { consumed: self.egui_ctx.wants_keyboard_input(), } } - WindowEvent::KeyboardInput { input, .. } => { - self.on_keyboard_input(input); + WindowEvent::KeyboardInput { event, .. } => { // When pressing the Tab key, egui focuses the first focusable element, hence Tab always consumes. - let consumed = self.egui_ctx.wants_keyboard_input() - || input.virtual_keycode == Some(winit::event::VirtualKeyCode::Tab); + let consumed = self.on_keyboard_input(event) + || self.egui_ctx.wants_keyboard_input() + || event.logical_key + == winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab); EventResponse { repaint: true, consumed, @@ -405,15 +388,23 @@ impl State { } } WindowEvent::ModifiersChanged(state) => { - self.egui_input.modifiers.alt = state.alt(); - self.egui_input.modifiers.ctrl = state.ctrl(); - self.egui_input.modifiers.shift = state.shift(); - self.egui_input.modifiers.mac_cmd = cfg!(target_os = "macos") && state.logo(); + let state = state.state(); + + let alt = state.alt_key(); + let ctrl = state.control_key(); + let shift = state.shift_key(); + let super_ = state.super_key(); + + self.egui_input.modifiers.alt = alt; + self.egui_input.modifiers.ctrl = ctrl; + self.egui_input.modifiers.shift = shift; + self.egui_input.modifiers.mac_cmd = cfg!(target_os = "macos") && super_; self.egui_input.modifiers.command = if cfg!(target_os = "macos") { - state.logo() + super_ } else { - state.ctrl() + ctrl }; + EventResponse { repaint: true, consumed: false, @@ -421,7 +412,8 @@ impl State { } // Things that may require repaint: - WindowEvent::CursorEntered { .. } + WindowEvent::RedrawRequested + | WindowEvent::CursorEntered { .. } | WindowEvent::Destroyed | WindowEvent::Occluded(_) | WindowEvent::Resized(_) @@ -434,7 +426,8 @@ impl State { }, // Things we completely ignore: - WindowEvent::AxisMotion { .. } + WindowEvent::ActivationTokenDone { .. } + | WindowEvent::AxisMotion { .. } | WindowEvent::SmartMagnify { .. } | WindowEvent::TouchpadRotate { .. } => EventResponse { repaint: false, @@ -655,36 +648,92 @@ impl State { } } - fn on_keyboard_input(&mut self, input: &winit::event::KeyboardInput) { - if let Some(keycode) = input.virtual_keycode { - let pressed = input.state == winit::event::ElementState::Pressed; + fn on_keyboard_input(&mut self, event: &winit::event::KeyEvent) -> bool { + let winit::event::KeyEvent { + // Represents the position of a key independent of the currently active layout. + // + // It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). + // The most prevalent use case for this is games. For example the default keys for the player + // to move around might be the W, A, S, and D keys on a US layout. The position of these keys + // is more important than their label, so they should map to Z, Q, S, and D on an "AZERTY" + // layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.) + physical_key, + + // Represents the results of a keymap, i.e. what character a certain key press represents. + // When telling users "Press Ctrl-F to find", this is where we should + // look for the "F" key, because they may have a dvorak layout on + // a qwerty keyboard, and so the logical "F" character may not be located on the physical `KeyCode::KeyF` position. + logical_key, + + text, + state, + + location: _, // e.g. is it on the numpad? + repeat: _, // egui will figure this out for us + .. + } = event; + + let pressed = *state == winit::event::ElementState::Pressed; + + let physical_key = if let winit::keyboard::PhysicalKey::Code(keycode) = *physical_key { + key_from_key_code(keycode) + } else { + None + }; + + let logical_key = key_from_winit_key(logical_key); + + if let Some(logical_key) = logical_key { if pressed { - // VirtualKeyCode::Paste etc in winit are broken/untrustworthy, + // KeyCode::Paste etc in winit are broken/untrustworthy, // so we detect these things manually: - if is_cut_command(self.egui_input.modifiers, keycode) { + if is_cut_command(self.egui_input.modifiers, logical_key) { self.egui_input.events.push(egui::Event::Cut); - } else if is_copy_command(self.egui_input.modifiers, keycode) { + return true; + } else if is_copy_command(self.egui_input.modifiers, logical_key) { self.egui_input.events.push(egui::Event::Copy); - } else if is_paste_command(self.egui_input.modifiers, keycode) { + return true; + } else if is_paste_command(self.egui_input.modifiers, logical_key) { if let Some(contents) = self.clipboard.get() { let contents = contents.replace("\r\n", "\n"); if !contents.is_empty() { self.egui_input.events.push(egui::Event::Paste(contents)); } } + return true; } } - if let Some(key) = translate_virtual_key_code(keycode) { - self.egui_input.events.push(egui::Event::Key { - key, - pressed, - repeat: false, // egui will fill this in for us! - modifiers: self.egui_input.modifiers, - }); + self.egui_input.events.push(egui::Event::Key { + key: logical_key, + physical_key, + pressed, + repeat: false, // egui will fill this in for us! + modifiers: self.egui_input.modifiers, + }); + } + + if let Some(text) = &text { + // Make sure there is text, and that it is not control characters + // (e.g. delete is sent as "\u{f728}" on macOS). + if !text.is_empty() && text.chars().all(is_printable_char) { + // On some platforms we get here when the user presses Cmd-C (copy), ctrl-W, etc. + // We need to ignore these characters that are side-effects of commands. + // Also make sure the key is pressed (not released). On Linux, text might + // contain some data even when the key is released. + let is_cmd = self.egui_input.modifiers.ctrl + || self.egui_input.modifiers.command + || self.egui_input.modifiers.mac_cmd; + if pressed && !is_cmd { + self.egui_input + .events + .push(egui::Event::Text(text.to_string())); + } } } + + false } /// Call with the output given by `egui`. @@ -708,7 +757,7 @@ impl State { copied_text, events: _, // handled elsewhere mutable_text_under_cursor: _, // only used in eframe web - text_cursor_pos, + ime, #[cfg(feature = "accesskit")] accesskit_update, } = platform_output; @@ -723,14 +772,25 @@ impl State { self.clipboard.set(copied_text); } - let allow_ime = text_cursor_pos.is_some(); + let allow_ime = ime.is_some(); if self.allow_ime != allow_ime { self.allow_ime = allow_ime; window.set_ime_allowed(allow_ime); } - if let Some(egui::Pos2 { x, y }) = text_cursor_pos { - window.set_ime_position(winit::dpi::LogicalPosition { x, y }); + if let Some(ime) = ime { + let rect = ime.rect; + let pixels_per_point = pixels_per_point(&self.egui_ctx, window); + window.set_ime_cursor_area( + winit::dpi::PhysicalPosition { + x: pixels_per_point * rect.min.x, + y: pixels_per_point * rect.min.y, + }, + winit::dpi::PhysicalSize { + width: pixels_per_point * rect.width(), + height: pixels_per_point * rect.height(), + }, + ); } #[cfg(feature = "accesskit")] @@ -880,25 +940,19 @@ fn is_printable_char(chr: char) -> bool { !is_in_private_use_area && !chr.is_ascii_control() } -fn is_cut_command(modifiers: egui::Modifiers, keycode: winit::event::VirtualKeyCode) -> bool { - (modifiers.command && keycode == winit::event::VirtualKeyCode::X) - || (cfg!(target_os = "windows") - && modifiers.shift - && keycode == winit::event::VirtualKeyCode::Delete) +fn is_cut_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool { + (modifiers.command && keycode == egui::Key::X) + || (cfg!(target_os = "windows") && modifiers.shift && keycode == egui::Key::Delete) } -fn is_copy_command(modifiers: egui::Modifiers, keycode: winit::event::VirtualKeyCode) -> bool { - (modifiers.command && keycode == winit::event::VirtualKeyCode::C) - || (cfg!(target_os = "windows") - && modifiers.ctrl - && keycode == winit::event::VirtualKeyCode::Insert) +fn is_copy_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool { + (modifiers.command && keycode == egui::Key::C) + || (cfg!(target_os = "windows") && modifiers.ctrl && keycode == egui::Key::Insert) } -fn is_paste_command(modifiers: egui::Modifiers, keycode: winit::event::VirtualKeyCode) -> bool { - (modifiers.command && keycode == winit::event::VirtualKeyCode::V) - || (cfg!(target_os = "windows") - && modifiers.shift - && keycode == winit::event::VirtualKeyCode::Insert) +fn is_paste_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool { + (modifiers.command && keycode == egui::Key::V) + || (cfg!(target_os = "windows") && modifiers.shift && keycode == egui::Key::Insert) } fn translate_mouse_button(button: winit::event::MouseButton) -> Option { @@ -906,100 +960,159 @@ fn translate_mouse_button(button: winit::event::MouseButton) -> Option Some(egui::PointerButton::Primary), winit::event::MouseButton::Right => Some(egui::PointerButton::Secondary), winit::event::MouseButton::Middle => Some(egui::PointerButton::Middle), - winit::event::MouseButton::Other(1) => Some(egui::PointerButton::Extra1), - winit::event::MouseButton::Other(2) => Some(egui::PointerButton::Extra2), + winit::event::MouseButton::Back => Some(egui::PointerButton::Extra1), + winit::event::MouseButton::Forward => Some(egui::PointerButton::Extra2), winit::event::MouseButton::Other(_) => None, } } -fn translate_virtual_key_code(key: winit::event::VirtualKeyCode) -> Option { +fn key_from_winit_key(key: &winit::keyboard::Key) -> Option { + match key { + winit::keyboard::Key::Named(named_key) => key_from_named_key(*named_key), + winit::keyboard::Key::Character(str) => egui::Key::from_name(str.as_str()), + winit::keyboard::Key::Unidentified(_) | winit::keyboard::Key::Dead(_) => None, + } +} + +fn key_from_named_key(named_key: winit::keyboard::NamedKey) -> Option { use egui::Key; - use winit::event::VirtualKeyCode; + use winit::keyboard::NamedKey; + + match named_key { + NamedKey::Enter => Some(Key::Enter), + NamedKey::Tab => Some(Key::Tab), + NamedKey::Space => Some(Key::Space), + NamedKey::ArrowDown => Some(Key::ArrowDown), + NamedKey::ArrowLeft => Some(Key::ArrowLeft), + NamedKey::ArrowRight => Some(Key::ArrowRight), + NamedKey::ArrowUp => Some(Key::ArrowUp), + NamedKey::End => Some(Key::End), + NamedKey::Home => Some(Key::Home), + NamedKey::PageDown => Some(Key::PageDown), + NamedKey::PageUp => Some(Key::PageUp), + NamedKey::Backspace => Some(Key::Backspace), + NamedKey::Delete => Some(Key::Delete), + NamedKey::Insert => Some(Key::Insert), + NamedKey::Escape => Some(Key::Escape), + NamedKey::F1 => Some(Key::F1), + NamedKey::F2 => Some(Key::F2), + NamedKey::F3 => Some(Key::F3), + NamedKey::F4 => Some(Key::F4), + NamedKey::F5 => Some(Key::F5), + NamedKey::F6 => Some(Key::F6), + NamedKey::F7 => Some(Key::F7), + NamedKey::F8 => Some(Key::F8), + NamedKey::F9 => Some(Key::F9), + NamedKey::F10 => Some(Key::F10), + NamedKey::F11 => Some(Key::F11), + NamedKey::F12 => Some(Key::F12), + NamedKey::F13 => Some(Key::F13), + NamedKey::F14 => Some(Key::F14), + NamedKey::F15 => Some(Key::F15), + NamedKey::F16 => Some(Key::F16), + NamedKey::F17 => Some(Key::F17), + NamedKey::F18 => Some(Key::F18), + NamedKey::F19 => Some(Key::F19), + NamedKey::F20 => Some(Key::F20), + _ => { + log::trace!("Unknown key: {named_key:?}"); + None + } + } +} + +fn key_from_key_code(key: winit::keyboard::KeyCode) -> Option { + use egui::Key; + use winit::keyboard::KeyCode; Some(match key { - VirtualKeyCode::Down => Key::ArrowDown, - VirtualKeyCode::Left => Key::ArrowLeft, - VirtualKeyCode::Right => Key::ArrowRight, - VirtualKeyCode::Up => Key::ArrowUp, - - VirtualKeyCode::Escape => Key::Escape, - VirtualKeyCode::Tab => Key::Tab, - VirtualKeyCode::Back => Key::Backspace, - VirtualKeyCode::Return | VirtualKeyCode::NumpadEnter => Key::Enter, - VirtualKeyCode::Space => Key::Space, - - VirtualKeyCode::Insert => Key::Insert, - VirtualKeyCode::Delete => Key::Delete, - VirtualKeyCode::Home => Key::Home, - VirtualKeyCode::End => Key::End, - VirtualKeyCode::PageUp => Key::PageUp, - VirtualKeyCode::PageDown => Key::PageDown, - - VirtualKeyCode::Minus | VirtualKeyCode::NumpadSubtract => Key::Minus, + KeyCode::ArrowDown => Key::ArrowDown, + KeyCode::ArrowLeft => Key::ArrowLeft, + KeyCode::ArrowRight => Key::ArrowRight, + KeyCode::ArrowUp => Key::ArrowUp, + + KeyCode::Escape => Key::Escape, + KeyCode::Tab => Key::Tab, + KeyCode::Backspace => Key::Backspace, + KeyCode::Enter | KeyCode::NumpadEnter => Key::Enter, + KeyCode::Space => Key::Space, + + KeyCode::Insert => Key::Insert, + KeyCode::Delete => Key::Delete, + KeyCode::Home => Key::Home, + KeyCode::End => Key::End, + KeyCode::PageUp => Key::PageUp, + KeyCode::PageDown => Key::PageDown, + + KeyCode::Comma => Key::Comma, + KeyCode::Period => Key::Period, + // KeyCode::Colon => Key::Colon, // NOTE: there is no physical colon key on an american keyboard + KeyCode::Semicolon => Key::Semicolon, + + KeyCode::Minus | KeyCode::NumpadSubtract => Key::Minus, + // Using Mac the key with the Plus sign on it is reported as the Equals key // (with both English and Swedish keyboard). - VirtualKeyCode::Equals | VirtualKeyCode::Plus | VirtualKeyCode::NumpadAdd => { - Key::PlusEquals - } - - VirtualKeyCode::Key0 | VirtualKeyCode::Numpad0 => Key::Num0, - VirtualKeyCode::Key1 | VirtualKeyCode::Numpad1 => Key::Num1, - VirtualKeyCode::Key2 | VirtualKeyCode::Numpad2 => Key::Num2, - VirtualKeyCode::Key3 | VirtualKeyCode::Numpad3 => Key::Num3, - VirtualKeyCode::Key4 | VirtualKeyCode::Numpad4 => Key::Num4, - VirtualKeyCode::Key5 | VirtualKeyCode::Numpad5 => Key::Num5, - VirtualKeyCode::Key6 | VirtualKeyCode::Numpad6 => Key::Num6, - VirtualKeyCode::Key7 | VirtualKeyCode::Numpad7 => Key::Num7, - VirtualKeyCode::Key8 | VirtualKeyCode::Numpad8 => Key::Num8, - VirtualKeyCode::Key9 | VirtualKeyCode::Numpad9 => Key::Num9, - - VirtualKeyCode::A => Key::A, - VirtualKeyCode::B => Key::B, - VirtualKeyCode::C => Key::C, - VirtualKeyCode::D => Key::D, - VirtualKeyCode::E => Key::E, - VirtualKeyCode::F => Key::F, - VirtualKeyCode::G => Key::G, - VirtualKeyCode::H => Key::H, - VirtualKeyCode::I => Key::I, - VirtualKeyCode::J => Key::J, - VirtualKeyCode::K => Key::K, - VirtualKeyCode::L => Key::L, - VirtualKeyCode::M => Key::M, - VirtualKeyCode::N => Key::N, - VirtualKeyCode::O => Key::O, - VirtualKeyCode::P => Key::P, - VirtualKeyCode::Q => Key::Q, - VirtualKeyCode::R => Key::R, - VirtualKeyCode::S => Key::S, - VirtualKeyCode::T => Key::T, - VirtualKeyCode::U => Key::U, - VirtualKeyCode::V => Key::V, - VirtualKeyCode::W => Key::W, - VirtualKeyCode::X => Key::X, - VirtualKeyCode::Y => Key::Y, - VirtualKeyCode::Z => Key::Z, - - VirtualKeyCode::F1 => Key::F1, - VirtualKeyCode::F2 => Key::F2, - VirtualKeyCode::F3 => Key::F3, - VirtualKeyCode::F4 => Key::F4, - VirtualKeyCode::F5 => Key::F5, - VirtualKeyCode::F6 => Key::F6, - VirtualKeyCode::F7 => Key::F7, - VirtualKeyCode::F8 => Key::F8, - VirtualKeyCode::F9 => Key::F9, - VirtualKeyCode::F10 => Key::F10, - VirtualKeyCode::F11 => Key::F11, - VirtualKeyCode::F12 => Key::F12, - VirtualKeyCode::F13 => Key::F13, - VirtualKeyCode::F14 => Key::F14, - VirtualKeyCode::F15 => Key::F15, - VirtualKeyCode::F16 => Key::F16, - VirtualKeyCode::F17 => Key::F17, - VirtualKeyCode::F18 => Key::F18, - VirtualKeyCode::F19 => Key::F19, - VirtualKeyCode::F20 => Key::F20, + KeyCode::Equal | KeyCode::NumpadAdd => Key::PlusEquals, + + KeyCode::Digit0 | KeyCode::Numpad0 => Key::Num0, + KeyCode::Digit1 | KeyCode::Numpad1 => Key::Num1, + KeyCode::Digit2 | KeyCode::Numpad2 => Key::Num2, + KeyCode::Digit3 | KeyCode::Numpad3 => Key::Num3, + KeyCode::Digit4 | KeyCode::Numpad4 => Key::Num4, + KeyCode::Digit5 | KeyCode::Numpad5 => Key::Num5, + KeyCode::Digit6 | KeyCode::Numpad6 => Key::Num6, + KeyCode::Digit7 | KeyCode::Numpad7 => Key::Num7, + KeyCode::Digit8 | KeyCode::Numpad8 => Key::Num8, + KeyCode::Digit9 | KeyCode::Numpad9 => Key::Num9, + + KeyCode::KeyA => Key::A, + KeyCode::KeyB => Key::B, + KeyCode::KeyC => Key::C, + KeyCode::KeyD => Key::D, + KeyCode::KeyE => Key::E, + KeyCode::KeyF => Key::F, + KeyCode::KeyG => Key::G, + KeyCode::KeyH => Key::H, + KeyCode::KeyI => Key::I, + KeyCode::KeyJ => Key::J, + KeyCode::KeyK => Key::K, + KeyCode::KeyL => Key::L, + KeyCode::KeyM => Key::M, + KeyCode::KeyN => Key::N, + KeyCode::KeyO => Key::O, + KeyCode::KeyP => Key::P, + KeyCode::KeyQ => Key::Q, + KeyCode::KeyR => Key::R, + KeyCode::KeyS => Key::S, + KeyCode::KeyT => Key::T, + KeyCode::KeyU => Key::U, + KeyCode::KeyV => Key::V, + KeyCode::KeyW => Key::W, + KeyCode::KeyX => Key::X, + KeyCode::KeyY => Key::Y, + KeyCode::KeyZ => Key::Z, + + KeyCode::F1 => Key::F1, + KeyCode::F2 => Key::F2, + KeyCode::F3 => Key::F3, + KeyCode::F4 => Key::F4, + KeyCode::F5 => Key::F5, + KeyCode::F6 => Key::F6, + KeyCode::F7 => Key::F7, + KeyCode::F8 => Key::F8, + KeyCode::F9 => Key::F9, + KeyCode::F10 => Key::F10, + KeyCode::F11 => Key::F11, + KeyCode::F12 => Key::F12, + KeyCode::F13 => Key::F13, + KeyCode::F14 => Key::F14, + KeyCode::F15 => Key::F15, + KeyCode::F16 => Key::F16, + KeyCode::F17 => Key::F17, + KeyCode::F18 => Key::F18, + KeyCode::F19 => Key::F19, + KeyCode::F20 => Key::F20, _ => { return None; @@ -1024,7 +1137,7 @@ fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option Some(winit::window::CursorIcon::Move), egui::CursorIcon::NoDrop => Some(winit::window::CursorIcon::NoDrop), egui::CursorIcon::NotAllowed => Some(winit::window::CursorIcon::NotAllowed), - egui::CursorIcon::PointingHand => Some(winit::window::CursorIcon::Hand), + egui::CursorIcon::PointingHand => Some(winit::window::CursorIcon::Pointer), egui::CursorIcon::Progress => Some(winit::window::CursorIcon::Progress), egui::CursorIcon::ResizeHorizontal => Some(winit::window::CursorIcon::EwResize), @@ -1112,7 +1225,12 @@ fn process_viewport_command( ViewportCommand::InnerSize(size) => { let width_px = pixels_per_point * size.x.max(1.0); let height_px = pixels_per_point * size.y.max(1.0); - window.set_inner_size(PhysicalSize::new(width_px, height_px)); + if window + .request_inner_size(PhysicalSize::new(width_px, height_px)) + .is_some() + { + log::debug!("ViewportCommand::InnerSize ignored by winit"); + } } ViewportCommand::BeginResize(direction) => { if let Err(err) = window.drag_resize_window(match direction { @@ -1196,11 +1314,14 @@ fn process_viewport_command( .expect("Invalid ICON data!") })); } - ViewportCommand::IMEPosition(pos) => { - window.set_ime_position(PhysicalPosition::new( - pixels_per_point * pos.x, - pixels_per_point * pos.y, - )); + ViewportCommand::IMERect(rect) => { + window.set_ime_cursor_area( + PhysicalPosition::new(pixels_per_point * rect.min.x, pixels_per_point * rect.min.y), + PhysicalSize::new( + pixels_per_point * rect.size().x, + pixels_per_point * rect.size().y, + ), + ); } ViewportCommand::IMEAllowed(v) => window.set_ime_allowed(v), ViewportCommand::IMEPurpose(p) => window.set_ime_purpose(match p { @@ -1448,10 +1569,15 @@ pub fn apply_viewport_builder_to_window( let pixels_per_point = pixels_per_point(egui_ctx, window); if let Some(size) = builder.inner_size { - window.set_inner_size(PhysicalSize::new( - pixels_per_point * size.x, - pixels_per_point * size.y, - )); + if window + .request_inner_size(PhysicalSize::new( + pixels_per_point * size.x, + pixels_per_point * size.y, + )) + .is_some() + { + log::debug!("Failed to set window size"); + } } if let Some(size) = builder.min_inner_size { window.set_min_inner_size(Some(PhysicalSize::new( @@ -1476,16 +1602,15 @@ pub fn apply_viewport_builder_to_window( /// Short and fast description of an event. /// Useful for logging and profiling. -pub fn short_generic_event_description(event: &winit::event::Event<'_, T>) -> &'static str { +pub fn short_generic_event_description(event: &winit::event::Event) -> &'static str { use winit::event::{DeviceEvent, Event, StartCause}; match event { + Event::AboutToWait => "Event::AboutToWait", + Event::LoopExiting => "Event::LoopExiting", Event::Suspended => "Event::Suspended", Event::Resumed => "Event::Resumed", - Event::MainEventsCleared => "Event::MainEventsCleared", - Event::RedrawRequested(_) => "Event::RedrawRequested", - Event::RedrawEventsCleared => "Event::RedrawEventsCleared", - Event::LoopDestroyed => "Event::LoopDestroyed", + Event::MemoryWarning => "Event::MemoryWarning", Event::UserEvent(_) => "UserEvent", Event::DeviceEvent { event, .. } => match event { DeviceEvent::Added { .. } => "DeviceEvent::Added", @@ -1495,7 +1620,6 @@ pub fn short_generic_event_description(event: &winit::event::Event<'_, T>) -> DeviceEvent::Motion { .. } => "DeviceEvent::Motion", DeviceEvent::Button { .. } => "DeviceEvent::Button", DeviceEvent::Key { .. } => "DeviceEvent::Key", - DeviceEvent::Text { .. } => "DeviceEvent::Text", }, Event::NewEvents(start_cause) => match start_cause { StartCause::ResumeTimeReached { .. } => "NewEvents::ResumeTimeReached", @@ -1509,10 +1633,11 @@ pub fn short_generic_event_description(event: &winit::event::Event<'_, T>) -> /// Short and fast description of an event. /// Useful for logging and profiling. -pub fn short_window_event_description(event: &winit::event::WindowEvent<'_>) -> &'static str { +pub fn short_window_event_description(event: &winit::event::WindowEvent) -> &'static str { use winit::event::WindowEvent; match event { + WindowEvent::ActivationTokenDone { .. } => "WindowEvent::ActivationTokenDone", WindowEvent::Resized { .. } => "WindowEvent::Resized", WindowEvent::Moved { .. } => "WindowEvent::Moved", WindowEvent::CloseRequested { .. } => "WindowEvent::CloseRequested", @@ -1520,7 +1645,6 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent<'_>) -> WindowEvent::DroppedFile { .. } => "WindowEvent::DroppedFile", WindowEvent::HoveredFile { .. } => "WindowEvent::HoveredFile", WindowEvent::HoveredFileCancelled { .. } => "WindowEvent::HoveredFileCancelled", - WindowEvent::ReceivedCharacter { .. } => "WindowEvent::ReceivedCharacter", WindowEvent::Focused { .. } => "WindowEvent::Focused", WindowEvent::KeyboardInput { .. } => "WindowEvent::KeyboardInput", WindowEvent::ModifiersChanged { .. } => "WindowEvent::ModifiersChanged", @@ -1531,6 +1655,7 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent<'_>) -> WindowEvent::MouseWheel { .. } => "WindowEvent::MouseWheel", WindowEvent::MouseInput { .. } => "WindowEvent::MouseInput", WindowEvent::TouchpadMagnify { .. } => "WindowEvent::TouchpadMagnify", + WindowEvent::RedrawRequested { .. } => "WindowEvent::RedrawRequested", WindowEvent::SmartMagnify { .. } => "WindowEvent::SmartMagnify", WindowEvent::TouchpadRotate { .. } => "WindowEvent::TouchpadRotate", WindowEvent::TouchpadPressure { .. } => "WindowEvent::TouchpadPressure", diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index 0c10f79078f..afb6e1dcbeb 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -361,8 +361,20 @@ pub enum Event { /// A key was pressed or released. Key { + /// The logical key, heeding the users keymap. + /// + /// For instance, if the user is using Dvorak keyboard layout, + /// this will take that into account. key: Key, + /// The physical key, corresponding to the actual position on the keyboard. + /// + /// This ignored keymaps, so it is not recommended to use this. + /// The only thing it makes sense for is things like games, + /// where e.g. the physical location of WSAD on QWERTY should always map to movement, + /// even if the user is using Dvorak or AZERTY. + physical_key: Option, + /// Was it pressed or released? pressed: bool, @@ -844,11 +856,8 @@ impl<'a> ModifierNames<'a> { /// Keyboard keys. /// -/// Includes all keys egui is interested in (such as `Home` and `End`) -/// plus a few that are useful for detecting keyboard shortcuts. -/// -/// Many keys are omitted because they are not always physical keys (depending on keyboard language), e.g. `;` and `§`, -/// and are therefore unsuitable as keyboard shortcuts if you want your app to be portable. +/// egui usually uses logical keys, i.e. after applying any user keymap. +// TODO(emilk): split into `LogicalKey` and `PhysicalKey` #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Key { @@ -870,12 +879,28 @@ pub enum Key { PageUp, PageDown, - /// The virtual keycode for the Minus key. + // ---------------------------------------------- + // Punctuation: + /// `:` + Colon, + + /// `,` + Comma, + + /// `-` Minus, - /// The virtual keycode for the Plus/Equals key. + /// `.` + Period, + + /// The for the Plus/Equals key. PlusEquals, + /// `;` + Semicolon, + + // ---------------------------------------------- + // Digits: /// Either from the main row or from the numpad. Num0, @@ -906,6 +931,8 @@ pub enum Key { /// Either from the main row or from the numpad. Num9, + // ---------------------------------------------- + // Letters: A, // Used for cmd+A (select All) B, C, // |CMD COPY| @@ -933,7 +960,8 @@ pub enum Key { Y, Z, // |CMD UNDO| - // The function keys: + // ---------------------------------------------- + // Function keys: F1, F2, F3, @@ -954,9 +982,196 @@ pub enum Key { F18, F19, F20, + // When adding keys, remember to also update `crates/egui-winit/src/lib.rs` + // and [`Self::ALL`]. + // Also: don't add keys last; add them to the group they best belong to. } impl Key { + /// All egui keys + pub const ALL: &'static [Self] = &[ + Self::ArrowDown, + Self::ArrowLeft, + Self::ArrowRight, + Self::ArrowUp, + Self::Escape, + Self::Tab, + Self::Backspace, + Self::Enter, + Self::Space, + Self::Insert, + Self::Delete, + Self::Home, + Self::End, + Self::PageUp, + Self::PageDown, + // Punctuation: + Self::Colon, + Self::Comma, + Self::Minus, + Self::Period, + Self::PlusEquals, + Self::Semicolon, + // Digits: + Self::Num0, + Self::Num1, + Self::Num2, + Self::Num3, + Self::Num4, + Self::Num5, + Self::Num6, + Self::Num7, + Self::Num8, + Self::Num9, + // Letters: + Self::A, + Self::B, + Self::C, + Self::D, + Self::E, + Self::F, + Self::G, + Self::H, + Self::I, + Self::J, + Self::K, + Self::L, + Self::M, + Self::N, + Self::O, + Self::P, + Self::Q, + Self::R, + Self::S, + Self::T, + Self::U, + Self::V, + Self::W, + Self::X, + Self::Y, + Self::Z, + // Function keys: + Self::F1, + Self::F2, + Self::F3, + Self::F4, + Self::F5, + Self::F6, + Self::F7, + Self::F8, + Self::F9, + Self::F10, + Self::F11, + Self::F12, + Self::F13, + Self::F14, + Self::F15, + Self::F16, + Self::F17, + Self::F18, + Self::F19, + Self::F20, + ]; + + /// Converts `"A"` to `Key::A`, `Space` to `Key::Space`, etc. + /// + /// Makes sense for logical keys. + /// + /// This will parse the output of both [`Self::name`] and [`Self::symbol_or_name`], + /// but will also parse single characters, so that both `"-"` and `"Minus"` will return `Key::Minus`. + /// + /// This should support both the names generated in a web browser, + /// and by winit. Please test on both with `eframe`. + pub fn from_name(key: &str) -> Option { + Some(match key { + "ArrowDown" | "Down" | "⏷" => Self::ArrowDown, + "ArrowLeft" | "Left" | "⏴" => Self::ArrowLeft, + "ArrowRight" | "Right" | "⏵" => Self::ArrowRight, + "ArrowUp" | "Up" | "⏶" => Self::ArrowUp, + + "Escape" | "Esc" => Self::Escape, + "Tab" => Self::Tab, + "Backspace" => Self::Backspace, + "Enter" | "Return" => Self::Enter, + "Space" | " " => Self::Space, + + "Help" | "Insert" => Self::Insert, + "Delete" => Self::Delete, + "Home" => Self::Home, + "End" => Self::End, + "PageUp" => Self::PageUp, + "PageDown" => Self::PageDown, + + "Colon" | ":" => Self::Colon, + "Comma" | "," => Self::Comma, + "Minus" | "-" | "−" => Self::Minus, + "Period" | "." => Self::Period, + "Plus" | "+" | "Equals" | "=" => Self::PlusEquals, + "Semicolon" | ";" => Self::Semicolon, + + "0" => Self::Num0, + "1" => Self::Num1, + "2" => Self::Num2, + "3" => Self::Num3, + "4" => Self::Num4, + "5" => Self::Num5, + "6" => Self::Num6, + "7" => Self::Num7, + "8" => Self::Num8, + "9" => Self::Num9, + + "a" | "A" => Self::A, + "b" | "B" => Self::B, + "c" | "C" => Self::C, + "d" | "D" => Self::D, + "e" | "E" => Self::E, + "f" | "F" => Self::F, + "g" | "G" => Self::G, + "h" | "H" => Self::H, + "i" | "I" => Self::I, + "j" | "J" => Self::J, + "k" | "K" => Self::K, + "l" | "L" => Self::L, + "m" | "M" => Self::M, + "n" | "N" => Self::N, + "o" | "O" => Self::O, + "p" | "P" => Self::P, + "q" | "Q" => Self::Q, + "r" | "R" => Self::R, + "s" | "S" => Self::S, + "t" | "T" => Self::T, + "u" | "U" => Self::U, + "v" | "V" => Self::V, + "w" | "W" => Self::W, + "x" | "X" => Self::X, + "y" | "Y" => Self::Y, + "z" | "Z" => Self::Z, + + "F1" => Self::F1, + "F2" => Self::F2, + "F3" => Self::F3, + "F4" => Self::F4, + "F5" => Self::F5, + "F6" => Self::F6, + "F7" => Self::F7, + "F8" => Self::F8, + "F9" => Self::F9, + "F10" => Self::F10, + "F11" => Self::F11, + "F12" => Self::F12, + "F13" => Self::F13, + "F14" => Self::F14, + "F15" => Self::F15, + "F16" => Self::F16, + "F17" => Self::F17, + "F18" => Self::F18, + "F19" => Self::F19, + "F20" => Self::F20, + + _ => return None, + }) + } + /// Emoji or name representing the key pub fn symbol_or_name(self) -> &'static str { // TODO(emilk): add support for more unicode symbols (see for instance https://wincent.com/wiki/Unicode_representations_of_modifier_keys). @@ -980,19 +1195,27 @@ impl Key { Key::ArrowLeft => "Left", Key::ArrowRight => "Right", Key::ArrowUp => "Up", + Key::Escape => "Escape", Key::Tab => "Tab", Key::Backspace => "Backspace", Key::Enter => "Enter", Key::Space => "Space", + Key::Insert => "Insert", Key::Delete => "Delete", Key::Home => "Home", Key::End => "End", Key::PageUp => "PageUp", Key::PageDown => "PageDown", + + Key::Colon => "Colon", + Key::Comma => "Comma", Key::Minus => "Minus", + Key::Period => "Period", Key::PlusEquals => "Plus", + Key::Semicolon => "Semicolon", + Key::Num0 => "0", Key::Num1 => "1", Key::Num2 => "2", @@ -1003,6 +1226,7 @@ impl Key { Key::Num7 => "7", Key::Num8 => "8", Key::Num9 => "9", + Key::A => "A", Key::B => "B", Key::C => "C", @@ -1053,6 +1277,31 @@ impl Key { } } +#[test] +fn test_key_from_name() { + assert_eq!( + Key::ALL.len(), + Key::F20 as usize + 1, + "Some keys are missing in Key::ALL" + ); + + for &key in Key::ALL { + let name = key.name(); + assert_eq!( + Key::from_name(name), + Some(key), + "Failed to roundtrip {key:?} from name {name:?}" + ); + + let symbol = key.symbol_or_name(); + assert_eq!( + Key::from_name(symbol), + Some(key), + "Failed to roundtrip {key:?} from symbol {symbol:?}" + ); + } +} + // ---------------------------------------------------------------------------- /// A keyboard shortcut, e.g. `Ctrl+Alt+W`. diff --git a/crates/egui/src/data/output.rs b/crates/egui/src/data/output.rs index f9ee018c3e4..7a239dd05c5 100644 --- a/crates/egui/src/data/output.rs +++ b/crates/egui/src/data/output.rs @@ -64,6 +64,21 @@ impl FullOutput { } } +/// Information about text being edited. +/// +/// Useful for IME. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct IMEOutput { + /// Where the [`crate::TextEdit`] is located on screen. + pub rect: crate::Rect, + + /// Where the cursor is. + /// + /// This is a very thin rectangle. + pub cursor_rect: crate::Rect, +} + /// The non-rendering part of what egui emits each frame. /// /// You can access (and modify) this with [`crate::Context::output`]. @@ -98,10 +113,10 @@ pub struct PlatformOutput { /// Use by `eframe` web to show/hide mobile keyboard and IME agent. pub mutable_text_under_cursor: bool, - /// Screen-space position of text edit cursor (used for IME). + /// This is et if, and only if, the user is currently editing text. /// - /// Iff `Some`, the user is editing text. - pub text_cursor_pos: Option, + /// Useful for IME. + pub ime: Option, /// The difference in the widget tree since last frame. /// @@ -137,7 +152,7 @@ impl PlatformOutput { copied_text, mut events, mutable_text_under_cursor, - text_cursor_pos, + ime, #[cfg(feature = "accesskit")] accesskit_update, } = newer; @@ -151,7 +166,7 @@ impl PlatformOutput { } self.events.append(&mut events); self.mutable_text_under_cursor = mutable_text_under_cursor; - self.text_cursor_pos = text_cursor_pos.or(self.text_cursor_pos); + self.ime = ime.or(self.ime); #[cfg(feature = "accesskit")] { diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index 3a9c60ebaa6..df1b30a9287 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -899,7 +899,8 @@ pub enum ViewportCommand { /// The the window icon. Icon(Option>), - IMEPosition(Pos2), + /// Set the IME cursor editing area. + IMERect(crate::Rect), IMEAllowed(bool), IMEPurpose(IMEPurpose), diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 0b4bb0b9d9f..adf53149146 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -683,7 +683,7 @@ impl<'t> TextEdit<'t> { paint_cursor_selection(ui, &painter, text_draw_pos, &galley, &cursor_range); if text.is_mutable() { - let cursor_pos = paint_cursor_end( + let cursor_rect = paint_cursor_end( ui, row_height, &painter, @@ -694,23 +694,14 @@ impl<'t> TextEdit<'t> { let is_fully_visible = ui.clip_rect().contains_rect(rect); // TODO: remove this HACK workaround for https://github.com/emilk/egui/issues/1531 if (response.changed || selection_changed) && !is_fully_visible { - ui.scroll_to_rect(cursor_pos, None); // keep cursor in view + ui.scroll_to_rect(cursor_rect, None); // keep cursor in view } if interactive { - // eframe web uses `text_cursor_pos` when showing IME, - // so only set it when text is editable and visible! - // But `winit` and `egui_web` differs in how to set the - // position of IME. - if cfg!(target_arch = "wasm32") { - ui.ctx().output_mut(|o| { - o.text_cursor_pos = Some(cursor_pos.left_top()); - }); - } else { - ui.ctx().output_mut(|o| { - o.text_cursor_pos = Some(cursor_pos.left_bottom()); - }); - } + // For IME, so only set it when text is editable and visible! + ui.ctx().output_mut(|o| { + o.ime = Some(crate::output::IMEOutput { rect, cursor_rect }); + }); } } } diff --git a/crates/egui_extras/Cargo.toml b/crates/egui_extras/Cargo.toml index 92c2a44af6e..21bcfe20a76 100644 --- a/crates/egui_extras/Cargo.toml +++ b/crates/egui_extras/Cargo.toml @@ -53,7 +53,7 @@ image = ["dep:image"] puffin = ["dep:puffin", "egui/puffin"] ## Support loading svg images. -svg = ["resvg", "tiny-skia", "usvg"] +svg = ["resvg"] ## Enable better syntax highlighting using [`syntect`](https://docs.rs/syntect). syntect = ["dep:syntect"] @@ -93,8 +93,6 @@ syntect = { version = "5", optional = true, default-features = false, features = # svg feature resvg = { version = "0.28", optional = true, default-features = false } -tiny-skia = { version = "0.8", optional = true, default-features = false } # must be updated in lock-step with resvg -usvg = { version = "0.28", optional = true, default-features = false } # http feature ehttp = { version = "0.3.1", optional = true, default-features = false } diff --git a/crates/egui_extras/src/image.rs b/crates/egui_extras/src/image.rs index 72be580d2c9..fc609e1dae6 100644 --- a/crates/egui_extras/src/image.rs +++ b/crates/egui_extras/src/image.rs @@ -2,6 +2,9 @@ use egui::{mutex::Mutex, TextureOptions}; +#[cfg(feature = "svg")] +use resvg::{tiny_skia, usvg}; + #[cfg(feature = "svg")] pub use usvg::FitTo; diff --git a/crates/egui_extras/src/loaders/svg_loader.rs b/crates/egui_extras/src/loaders/svg_loader.rs index ff2cab86844..00a3bdfef89 100644 --- a/crates/egui_extras/src/loaders/svg_loader.rs +++ b/crates/egui_extras/src/loaders/svg_loader.rs @@ -1,10 +1,13 @@ +use std::{mem::size_of, path::Path, sync::Arc}; + use egui::{ ahash::HashMap, load::{BytesPoll, ImageLoadResult, ImageLoader, ImagePoll, LoadError, SizeHint}, mutex::Mutex, ColorImage, }; -use std::{mem::size_of, path::Path, sync::Arc}; + +use resvg::usvg; type Entry = Result, String>; diff --git a/crates/egui_glow/Cargo.toml b/crates/egui_glow/Cargo.toml index 3d169aa3b51..71005497706 100644 --- a/crates/egui_glow/Cargo.toml +++ b/crates/egui_glow/Cargo.toml @@ -69,9 +69,9 @@ wasm-bindgen = "0.2" [dev-dependencies] -glutin = "0.30" # examples/pure_glow +glutin = "0.31" # examples/pure_glow raw-window-handle.workspace = true -glutin-winit = "0.3.0" +glutin-winit = "0.4.0" [[example]] diff --git a/crates/egui_glow/examples/pure_glow.rs b/crates/egui_glow/examples/pure_glow.rs index 1bff00d5837..c9fbb33dab0 100644 --- a/crates/egui_glow/examples/pure_glow.rs +++ b/crates/egui_glow/examples/pure_glow.rs @@ -19,7 +19,7 @@ impl GlutinWindowContext { #[allow(unsafe_code)] unsafe fn new(event_loop: &winit::event_loop::EventLoopWindowTarget) -> Self { use egui::NumExt; - use glutin::context::NotCurrentGlContextSurfaceAccessor; + use glutin::context::NotCurrentGlContext; use glutin::display::GetGlDisplay; use glutin::display::GlDisplay; use glutin::prelude::GlSurface; @@ -42,7 +42,7 @@ impl GlutinWindowContext { log::debug!("trying to get gl_config"); 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::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 + .with_preference(glutin_winit::ApiPreference::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 .with_window_builder(Some(winit_window_builder.clone())) .build( event_loop, @@ -150,7 +150,9 @@ pub enum UserEvent { fn main() { let mut clear_color = [0.1, 0.1, 0.1]; - let event_loop = winit::event_loop::EventLoopBuilder::::with_user_event().build(); + 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); @@ -168,7 +170,7 @@ fn main() { let mut repaint_delay = std::time::Duration::MAX; - event_loop.run(move |event, _, control_flow| { + let _ = event_loop.run(move |event, event_loop_window_target| { let mut redraw = || { let mut quit = false; @@ -182,18 +184,20 @@ fn main() { }); }); - *control_flow = if quit { - winit::event_loop::ControlFlow::Exit - } else if repaint_delay.is_zero() { - gl_window.window().request_redraw(); - winit::event_loop::ControlFlow::Poll - } else if let Some(repaint_delay_instant) = - std::time::Instant::now().checked_add(repaint_delay) - { - winit::event_loop::ControlFlow::WaitUntil(repaint_delay_instant) + if quit { + event_loop_window_target.exit(); } else { - winit::event_loop::ControlFlow::Wait - }; + event_loop_window_target.set_control_flow(if repaint_delay.is_zero() { + gl_window.window().request_redraw(); + winit::event_loop::ControlFlow::Poll + } else if let Some(repaint_after_instant) = + std::time::Instant::now().checked_add(repaint_delay) + { + winit::event_loop::ControlFlow::WaitUntil(repaint_after_instant) + } else { + winit::event_loop::ControlFlow::Wait + }); + } { unsafe { @@ -214,25 +218,20 @@ fn main() { }; match event { - // Platform-dependent event handlers to workaround a winit bug - // See: https://github.com/rust-windowing/winit/issues/987 - // See: https://github.com/rust-windowing/winit/issues/1619 - winit::event::Event::RedrawEventsCleared if cfg!(target_os = "windows") => redraw(), - winit::event::Event::RedrawRequested(_) if !cfg!(target_os = "windows") => redraw(), - winit::event::Event::WindowEvent { event, .. } => { use winit::event::WindowEvent; if matches!(event, WindowEvent::CloseRequested | WindowEvent::Destroyed) { - *control_flow = winit::event_loop::ControlFlow::Exit; + event_loop_window_target.exit(); + return; + } + + if matches!(event, WindowEvent::RedrawRequested) { + redraw(); + return; } if let winit::event::WindowEvent::Resized(physical_size) = &event { gl_window.resize(*physical_size); - } else if let winit::event::WindowEvent::ScaleFactorChanged { - new_inner_size, .. - } = &event - { - gl_window.resize(**new_inner_size); } let event_response = egui_glow.on_window_event(gl_window.window(), &event); @@ -245,7 +244,7 @@ fn main() { winit::event::Event::UserEvent(UserEvent::Redraw(delay)) => { repaint_delay = delay; } - winit::event::Event::LoopDestroyed => { + winit::event::Event::LoopExiting => { egui_glow.destroy(); } winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached { diff --git a/crates/egui_glow/src/winit.rs b/crates/egui_glow/src/winit.rs index 567ce65f60d..5d87d000416 100644 --- a/crates/egui_glow/src/winit.rs +++ b/crates/egui_glow/src/winit.rs @@ -58,7 +58,7 @@ impl EguiGlow { pub fn on_window_event( &mut self, window: &winit::window::Window, - event: &winit::event::WindowEvent<'_>, + event: &winit::event::WindowEvent, ) -> EventResponse { self.egui_winit.on_window_event(window, event) } diff --git a/deny.toml b/deny.toml index 73f492c5d91..6882337c630 100644 --- a/deny.toml +++ b/deny.toml @@ -34,28 +34,26 @@ deny = [ ] skip = [ - { name = "arrayvec" }, # old version via tiny-skiaz - { name = "base64" }, # small crate, old version from usvg - { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … - { name = "glow" }, # TODO(@wumpf): Old version use for glow backend right now, newer for wgpu. Updating this trickles out to updating winit. - { name = "glutin_wgl_sys" }, # TODO(@wumpf): Old version use for glow backend right now, newer for wgpu. Updating this trickles out to updating winit. - { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 - { name = "memoffset" }, # tiny dependency - { name = "nix" }, # old version via winit - { name = "redox_syscall" }, # old version via winit - { name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu. - { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' - { name = "tiny-skia" }, # winit uses a different version from egui_extras (TODO(emilk): update egui_extras!) - { name = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg - { name = "wayland-sys" }, # old version via winit - { name = "windows_x86_64_msvc" }, # old version via glutin - { name = "windows-sys" }, # old version via glutin - { name = "windows" }, # old version via accesskit + { name = "arrayvec" }, # old version via tiny-skiaz + { name = "base64" }, # small crate, old version from usvg + { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … + { name = "glow" }, # TODO(@wumpf): updatere glow + { name = "glutin_wgl_sys" }, # TODO(@wumpf): updatere glow + { name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7 + { name = "memoffset" }, # tiny dependency + { name = "quick-xml" }, # old version via wayland-scanner + { name = "redox_syscall" }, # old version via directories-next + { name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu. + { name = "time" }, # old version pulled in by unmaintianed crate 'chrono' + { name = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg + { name = "windows" }, # old version via accesskit_windows ] skip-tree = [ { name = "criterion" }, # dev-dependency { name = "foreign-types" }, # small crate. Old version via cocoa and core-graphics (winit). + { name = "objc2" }, # old version via accesskit_macos { name = "rfd" }, # example dependency + { name = "tiny-skia" }, # old version via old resvg in egui_extras - see https://github.com/emilk/egui/issues/3652 ] diff --git a/examples/multiple_viewports/Cargo.toml b/examples/multiple_viewports/Cargo.toml index 18898bdc0a8..1e77e32ea74 100644 --- a/examples/multiple_viewports/Cargo.toml +++ b/examples/multiple_viewports/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" rust-version = "1.72" publish = false +[features] +wgpu = ["eframe/wgpu"] [dependencies] eframe = { path = "../../crates/eframe", features = [