diff --git a/Cargo.lock b/Cargo.lock index 806f40433b..9629279f2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,9 +63,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-activity" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", "bitflags 2.6.0", @@ -77,7 +77,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "thiserror", ] @@ -172,9 +172,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" @@ -617,6 +617,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "cgl" version = "0.3.2" @@ -1049,6 +1055,15 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +dependencies = [ + "serde", +] + [[package]] name = "dwrote" version = "0.11.0" @@ -1275,7 +1290,7 @@ dependencies = [ [[package]] name = "fontique" version = "0.1.0" -source = "git+https://github.com/linebender/parley#5b60803d1256ab821f79eaa06721a3112de7202a" +source = "git+https://github.com/linebender/parley?rev=5b60803d1256ab821f79eaa06721a3112de7202a#5b60803d1256ab821f79eaa06721a3112de7202a" dependencies = [ "core-foundation", "core-text 20.1.0", @@ -1619,55 +1634,56 @@ dependencies = [ [[package]] name = "glutin" -version = "0.31.3" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" +checksum = "2491aa3090f682ddd920b184491844440fdd14379c7eef8f5bc10ef7fb3242fd" dependencies = [ "bitflags 2.6.0", - "cfg_aliases", + "cfg_aliases 0.2.1", "cgl", "core-foundation", "dispatch", "glutin_egl_sys", "glutin_glx_sys", - "glutin_wgl_sys", - "icrate", + "glutin_wgl_sys 0.6.0", "libloading 0.8.5", - "objc2 0.4.1", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", "once_cell", - "raw-window-handle 0.5.2", + "raw-window-handle", "wayland-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "x11-dl", ] [[package]] name = "glutin-winit" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" +checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" dependencies = [ - "cfg_aliases", + "cfg_aliases 0.2.1", "glutin", - "raw-window-handle 0.5.2", + "raw-window-handle", "winit", ] [[package]] name = "glutin_egl_sys" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" +checksum = "cae99fff4d2850dbe6fb8c1fa8e4fead5525bab715beaacfccf3fb994e01c827" dependencies = [ "gl_generator", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "glutin_glx_sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" +checksum = "9c2b2d3918e76e18e08796b55eb64e8fe6ec67d5a6b2e2a7e2edce224ad24c63" dependencies = [ "gl_generator", "x11-dl", @@ -1682,6 +1698,15 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "glutin_wgl_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c" +dependencies = [ + "gl_generator", +] + [[package]] name = "gpu-alloc" version = "0.6.0" @@ -1822,7 +1847,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" dependencies = [ "block2 0.3.0", - "dispatch", "objc2 0.4.1", ] @@ -2301,17 +2325,16 @@ dependencies = [ [[package]] name = "ndk" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ "bitflags 2.6.0", "jni-sys", "log", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", + "raw-window-handle", "thiserror", ] @@ -2330,6 +2353,15 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + [[package]] name = "neovide" version = "0.13.3" @@ -2363,7 +2395,7 @@ dependencies = [ "parking_lot", "parley", "rand 0.8.5", - "raw-window-handle 0.5.2", + "raw-window-handle", "regex", "rmpv", "rust-embed", @@ -2617,6 +2649,82 @@ dependencies = [ "objc2-encode 4.0.3", ] +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-contacts", + "objc2-foundation", +] + [[package]] name = "objc2-encode" version = "3.0.0" @@ -2637,10 +2745,103 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.6.0", "block2 0.5.1", + "dispatch", "libc", "objc2 0.5.2", ] +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation", +] + [[package]] name = "objc_id" version = "0.1.1" @@ -2775,7 +2976,7 @@ dependencies = [ [[package]] name = "parley" version = "0.1.0" -source = "git+https://github.com/linebender/parley#5b60803d1256ab821f79eaa06721a3112de7202a" +source = "git+https://github.com/linebender/parley?rev=5b60803d1256ab821f79eaa06721a3112de7202a#5b60803d1256ab821f79eaa06721a3112de7202a" dependencies = [ "fontique", "peniko", @@ -2866,6 +3067,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -3099,12 +3320,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -3121,15 +3336,6 @@ dependencies = [ "font-types", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -3368,9 +3574,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sctk-adwaita" -version = "0.8.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7" +checksum = "7555fcb4f753d095d734fdefebb0ad8c98478a21db500492d87c55913d3b0086" dependencies = [ "ab_glyph", "log", @@ -4095,7 +4301,6 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vide" version = "0.1.0" -source = "git+https://github.com/neovide/vide?rev=61239a676e89e561c4f3c19b8aec32947c818340#61239a676e89e561c4f3c19b8aec32947c818340" dependencies = [ "base64", "bytemuck", @@ -4369,9 +4574,9 @@ dependencies = [ [[package]] name = "web-time" -version = "0.2.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -4385,14 +4590,14 @@ checksum = "90e37c7b9921b75dfd26dd973fdcbce36f13dfa6e2dc82aece584e0ed48c355c" dependencies = [ "arrayvec", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.1.1", "document-features", "js-sys", "log", "naga", "parking_lot", "profiling", - "raw-window-handle 0.6.2", + "raw-window-handle", "smallvec", "static_assertions", "wasm-bindgen", @@ -4412,7 +4617,7 @@ dependencies = [ "arrayvec", "bit-vec", "bitflags 2.6.0", - "cfg_aliases", + "cfg_aliases 0.1.1", "codespan-reporting", "document-features", "indexmap", @@ -4421,7 +4626,7 @@ dependencies = [ "once_cell", "parking_lot", "profiling", - "raw-window-handle 0.6.2", + "raw-window-handle", "rustc-hash", "smallvec", "thiserror", @@ -4442,11 +4647,11 @@ dependencies = [ "bit-set", "bitflags 2.6.0", "block", - "cfg_aliases", + "cfg_aliases 0.1.1", "core-graphics-types", "d3d12", "glow", - "glutin_wgl_sys", + "glutin_wgl_sys 0.5.0", "gpu-alloc", "gpu-allocator", "gpu-descriptor", @@ -4458,13 +4663,13 @@ dependencies = [ "log", "metal", "naga", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", "parking_lot", "profiling", "range-alloc", - "raw-window-handle 0.6.2", + "raw-window-handle", "renderdoc-sys", "rustc-hash", "smallvec", @@ -4875,39 +5080,42 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.29.15" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" +checksum = "49f45a7b7e2de6af35448d7718dab6d95acec466eb3bb7a56f4d31d1af754004" dependencies = [ "ahash", "android-activity", "atomic-waker", "bitflags 2.6.0", + "block2 0.5.1", "bytemuck", "calloop 0.12.4", - "cfg_aliases", + "cfg_aliases 0.2.1", + "concurrent-queue", "core-foundation", "core-graphics 0.23.2", "cursor-icon", - "icrate", + "dpi", "js-sys", "libc", - "log", "memmap2", "ndk", - "ndk-sys", - "objc2 0.4.1", - "once_cell", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", "orbclient", "percent-encoding", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", - "redox_syscall 0.3.5", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", "rustix 0.38.34", "sctk-adwaita", "serde", "smithay-client-toolkit 0.18.1", "smol_str", + "tracing", "unicode-segmentation", "wasm-bindgen", "wasm-bindgen-futures", @@ -4917,7 +5125,7 @@ dependencies = [ "wayland-protocols-plasma", "web-sys", "web-time", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "x11-dl", "x11rb", "xkbcommon-dl", diff --git a/Cargo.toml b/Cargo.toml index aff31eb2e7..8c5810e839 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,8 +39,8 @@ dirs = "5.0.0" glamour = { version = "0.11.1", features = ["serde"] } flexi_logger = { version = "0.28.0", default-features = false } futures = "0.3.21" -glutin = "0.31.1" -glutin-winit = "0.4.2" +glutin = "0.32.0" +glutin-winit = "0.5.0" image = { version = "0.25.0", default-features = false, features = ["ico"] } indoc = "2.0.5" itertools = "0.13.0" @@ -52,9 +52,9 @@ num = "0.4.1" nvim-rs = { version = "0.7.0", features = ["use_tokio"] } palette = "0.7.6" parking_lot = "0.12.0" -parley = { git = "https://github.com/linebender/parley" } +parley = { git = "https://github.com/linebender/parley", rev="5b60803d1256ab821f79eaa06721a3112de7202a" } rand = "0.8.5" -raw-window-handle = "0.5.0" +raw-window-handle = "0.6.0" rmpv = "1.0.0" rust-embed = "8.2.0" serde = { version = "1.0.136", features = ["derive"] } @@ -75,9 +75,9 @@ tracy-client-sys = { version = "0.22.0", optional = true, default-features = fal "fibers", ] } unicode-segmentation = "1.9.0" -vide = { git = "https://github.com/neovide/vide", rev="61239a676e89e561c4f3c19b8aec32947c818340" } +vide = { path = "../vide" } which = "6.0.1" -winit = { version = "=0.29.15", features = ["serde"] } +winit = { version = "=0.30.3", features = ["serde"] } xdg = "2.4.1" notify-debouncer-full = "0.3.1" regex = "1.10.3" @@ -109,6 +109,7 @@ icrate = { version = "0.0.4", features = [ "apple", "Foundation", "Foundation_NSThread", + "Foundation_NSProcessInfo", "AppKit", "AppKit_NSColor", "AppKit_NSEvent", diff --git a/macos-builder/readme.md b/macos-builder/readme.md new file mode 100644 index 0000000000..9d25f7742e --- /dev/null +++ b/macos-builder/readme.md @@ -0,0 +1,77 @@ +# Neovide Packaging for macOS + +This script is designed to build (if needed) and package the Neovide application packaging it into a macOS `.app` bundle, and then creating a `.dmg` disk image for distribution. + +## Prerequisites + +Before running the script, ensure you have the following dependencies installed: + +- [cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) +- [create-dmg](https://github.com/create-dmg/create-dmg) +- [codesign](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution) + +**Run the Script**: + +```bash +./macos-builder/run aarch64-apple-darwin +``` + +### Steps + +**Set Up Release Directory**: +The script sets the `RELEASE_DIR` based on the `TARGET_ARCHITECTURE` environment variable. If `TARGET_ARCHITECTURE` is not set, it defaults to `target/release`. + +```bash +RELEASE_DIR=${TARGET_ARCHITECTURE:+target/${TARGET_ARCHITECTURE}/release} +RELEASE_DIR=${RELEASE_DIR:-target/release} +``` + +**Build the Project**: +If the release directory does not exist, the script runs the `cargo build --release` command to build the project. + +_*This is for local development purposes since you would typically build the project beforehand.*_ + +```bash +if [ ! -d "${RELEASE_DIR}" ]; then + cargo build --release ${TARGET_ARCHITECTURE:+--target "${TARGET_ARCHITECTURE}"} +fi +``` + +**Prepare the Application Bundle**: + +- Sets up directories for the `.app` bundle. +- Copies the built binary and other necessary resources into the bundle. +- Signs the application using `codesign`. + +```bash +mkdir -p "${APP_BINARY_DIR}" +mkdir -p "${APP_EXTRAS_DIR}" +cp -fRp "${APP_TEMPLATE}" "${APP_DIR}" +cp -fp "${APP_BINARY}" "${APP_BINARY_DIR}" +touch -r "${APP_BINARY}" "${APP_DIR}/${APP_NAME}" +codesign --remove-signature "${APP_DIR}/${APP_NAME}" +codesign --force --deep --sign - "${APP_DIR}/${APP_NAME}" +``` + +**Create the Disk Image**: +Uses `create-dmg` to create a `.dmg` file for the application. + +```bash +create-dmg \ + --filesystem "${DMG_FILESYSTEM}" \ + --format "${DMG_FORMAT}" \ + --volname "${DMG_VOLNAME}" \ + --volicon "${DMG_ICNS}" \ + --background "${DMG_BACKGROUND}" \ + --window-size 650 470 \ + --icon-size 80 \ + --icon Neovide.app 240 320 \ + --app-drop-link 410 320 \ + "${APP_DIR}/${DMG_NAME}" \ + "${APP_DIR}/${APP_NAME}" +``` + +## Notes + +- Ensure all paths and filenames are correct and that the necessary files (like `Neovide.icns` and `neovide-dmg-background.tiff`) are present in their respective directories. +- The script assumes a macOS environment with the necessary tools installed. diff --git a/macos-builder/run b/macos-builder/run index d13e6e4dad..2d419adf79 100755 --- a/macos-builder/run +++ b/macos-builder/run @@ -2,10 +2,22 @@ set -e +if [ -n "$1" ]; then + TARGET_ARCHITECTURE=$1 +fi + +RELEASE_DIR=${TARGET_ARCHITECTURE:+target/${TARGET_ARCHITECTURE}/release} +RELEASE_DIR=${RELEASE_DIR:-target/release} + +if [ ! -d "${RELEASE_DIR}" ]; then + echo "Release directory '${RELEASE_DIR}' does not exist." + echo "Running 'cargo build --release${TARGET_ARCHITECTURE:+ --target ${TARGET_ARCHITECTURE}}' ..." + cargo build --release ${TARGET_ARCHITECTURE:+--target "${TARGET_ARCHITECTURE}"} +fi + TARGET="neovide" EXTRAS_DIR="extra" ASSETS_DIR="assets" -RELEASE_DIR="target/$1/release" BUNDLE_DIR="${RELEASE_DIR}/bundle" APP_NAME="Neovide.app" @@ -15,7 +27,7 @@ APP_BINARY="${RELEASE_DIR}/${TARGET}" APP_BINARY_DIR="${APP_DIR}/${APP_NAME}/Contents/MacOS" APP_EXTRAS_DIR="${APP_DIR}/${APP_NAME}/Contents/Resources" -DMG_NAME="Neovide-$1.dmg" +DMG_NAME="Neovide${TARGET_ARCHITECTURE:+-${TARGET_ARCHITECTURE}}.dmg" DMG_VOLNAME="Neovide" DMG_FILESYSTEM="HFS+" DMG_FORMAT="UDZO" diff --git a/src/clipboard.rs b/src/clipboard.rs index ce85b0cd1e..65f057b0da 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -8,7 +8,7 @@ use copypasta::{ }; use copypasta::{ClipboardContext, ClipboardProvider}; use parking_lot::Mutex; -use raw_window_handle::HasRawDisplayHandle; +use raw_window_handle::HasDisplayHandle; #[cfg(target_os = "linux")] use raw_window_handle::{RawDisplayHandle, WaylandDisplayHandle}; use winit::event_loop::EventLoop; @@ -27,26 +27,30 @@ static CLIPBOARD: OnceLock> = OnceLock::new(); pub fn init(event_loop: &EventLoop) { CLIPBOARD - .set(Mutex::new(match event_loop.raw_display_handle() { - #[cfg(target_os = "linux")] - RawDisplayHandle::Wayland(WaylandDisplayHandle { display, .. }) => unsafe { - let (selection, clipboard) = - wayland_clipboard::create_clipboards_from_external(display); - Clipboard { - clipboard: Box::new(clipboard), - selection: Box::new(selection), - } + .set(Mutex::new( + match event_loop.display_handle().unwrap().as_raw() { + #[cfg(target_os = "linux")] + RawDisplayHandle::Wayland(WaylandDisplayHandle { mut display, .. }) => unsafe { + let (selection, clipboard) = + wayland_clipboard::create_clipboards_from_external(display.as_mut()); + Clipboard { + clipboard: Box::new(clipboard), + selection: Box::new(selection), + } + }, + #[cfg(target_os = "linux")] + _ => Clipboard { + clipboard: Box::new(ClipboardContext::new().unwrap()), + selection: Box::new( + X11ClipboardContext::::new().unwrap(), + ), + }, + #[cfg(not(target_os = "linux"))] + _ => Clipboard { + clipboard: Box::new(ClipboardContext::new().unwrap()), + }, }, - #[cfg(target_os = "linux")] - _ => Clipboard { - clipboard: Box::new(ClipboardContext::new().unwrap()), - selection: Box::new(X11ClipboardContext::::new().unwrap()), - }, - #[cfg(not(target_os = "linux"))] - _ => Clipboard { - clipboard: Box::new(ClipboardContext::new().unwrap()), - }, - })) + )) .ok(); } diff --git a/src/main.rs b/src/main.rs index 5fd719ca9b..759a6be3ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,7 +55,7 @@ use renderer::{cursor_renderer::CursorSettings, RendererSettings}; #[cfg_attr(target_os = "windows", allow(unused_imports))] use settings::SETTINGS; use window::{ - create_event_loop, determine_window_size, main_loop, UserEvent, WindowSettings, WindowSize, + create_event_loop, determine_window_size, UpdateLoop, UserEvent, WindowSettings, WindowSize, }; pub use channel_utils::*; @@ -95,7 +95,10 @@ fn main() -> NeovideExitCode { match setup(event_loop.create_proxy()) { Err(err) => handle_startup_errors(err, event_loop).into(), Ok((window_size, font_settings, _runtime)) => { - main_loop(window_size, font_settings, event_loop).into() + let mut update_loop = + UpdateLoop::new(window_size, font_settings); + + event_loop.run_app(&mut update_loop).into() } } } diff --git a/src/renderer/cursor_renderer/mod.rs b/src/renderer/cursor_renderer/mod.rs index 79ade3ece9..b6b0545ff0 100644 --- a/src/renderer/cursor_renderer/mod.rs +++ b/src/renderer/cursor_renderer/mod.rs @@ -3,7 +3,7 @@ mod cursor_vfx; use std::collections::HashMap; -use winit::event::{Event, WindowEvent}; +use winit::event::WindowEvent; use crate::{ bridge::EditorMode, @@ -12,7 +12,7 @@ use crate::{ renderer::{animation_utils::*, GridRenderer, RenderedWindow}, settings::{ParseFromValue, SETTINGS}, units::{GridPos, GridScale, PixelPos, PixelSize}, - window::{ShouldRender, UserEvent}, + window::ShouldRender, }; use blink::*; @@ -191,16 +191,9 @@ impl CursorRenderer { renderer } - pub fn handle_event(&mut self, event: &Event) -> bool { - if let Event::WindowEvent { - event: WindowEvent::Focused(is_focused), - .. - } = event - { + pub fn handle_event(&mut self, event: &WindowEvent) { + if let WindowEvent::Focused(is_focused) = event { self.window_has_focus = *is_focused; - true - } else { - false } } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 72782ce4ee..9aa4089f89 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -19,7 +19,7 @@ use itertools::Itertools; use log::{error, warn}; use palette::{LinSrgba, WithAlpha}; -use winit::{event::Event, window::Window}; +use winit::{event::WindowEvent, window::Window}; use vide::{Layer, Scene, WinitRenderer}; @@ -31,7 +31,7 @@ use crate::{ renderer::rendered_layer::{group_windows, FloatingLayer}, settings::*, units::{GridPos, GridRect, GridSize, PixelPos}, - window::{ShouldRender, UserEvent}, + window::ShouldRender, WindowSettings, }; @@ -173,9 +173,11 @@ impl Renderer { self.wgpu_renderer = Some(block_on(create_renderer(window))); } - pub fn handle_event(&mut self, event: &Event) -> bool { + pub fn handle_event(&mut self, event: &WindowEvent) { if let Some(wgpu_renderer) = self.wgpu_renderer.as_mut() { - wgpu_renderer.handle_event(event); + if let WindowEvent::Resized(new_size) = event { + wgpu_renderer.resize(new_size.width, new_size.height); + } } self.cursor_renderer.handle_event(event) } @@ -469,7 +471,11 @@ impl Renderer { DEFAULT_GRID_SIZE } } -} + + pub fn exit(&mut self) { + self.wgpu_renderer = None; + } +} /// Defines how floating windows are sorted. fn floating_sort(window_a: &&mut RenderedWindow, window_b: &&mut RenderedWindow) -> Ordering { diff --git a/src/renderer/opengl.rs b/src/renderer/opengl.rs new file mode 100644 index 0000000000..79057fcd81 --- /dev/null +++ b/src/renderer/opengl.rs @@ -0,0 +1,301 @@ +use std::{ + convert::TryInto, + env, + env::consts::OS, + ffi::{c_void, CStr, CString}, + num::NonZeroU32, +}; + +use gl::{types::*, MAX_RENDERBUFFER_SIZE}; +use glutin::surface::SwapInterval; +use glutin::{ + config::{Config, ConfigTemplateBuilder}, + context::{ContextAttributesBuilder, GlProfile, PossiblyCurrentContext}, + display::GetGlDisplay, + prelude::*, + surface::{Surface, SurfaceAttributesBuilder, WindowSurface}, +}; +use glutin_winit::DisplayBuilder; +use raw_window_handle::HasWindowHandle; +use skia_safe::{ + canvas::Canvas, + gpu::{ + backend_render_targets::make_gl, gl::FramebufferInfo, surfaces::wrap_backend_render_target, + DirectContext, SurfaceOrigin, + }, + ColorSpace, ColorType, PixelGeometry, SurfaceProps, SurfacePropsFlags, +}; +use winit::{ + dpi::PhysicalSize, + event_loop::{ActiveEventLoop, EventLoopProxy}, + window::{Window, WindowAttributes}, +}; + +#[cfg(target_os = "windows")] +pub use super::vsync::VSyncWinDwm; + +#[cfg(target_os = "macos")] +pub use super::vsync::VSyncMacos; + +use super::{RendererSettings, SkiaRenderer, VSync, WindowConfig, WindowConfigType}; + +use crate::{profiling::tracy_gpu_zone, settings::SETTINGS, window::UserEvent}; + +#[cfg(feature = "gpu_profiling")] +use crate::profiling::{opengl::create_opengl_gpu_context, GpuCtx}; + +pub struct OpenGLSkiaRenderer { + // NOTE: The destruction order is important, so don't re-arrange + // If possible keep it the reverse of the initialization order + skia_surface: skia_safe::Surface, + fb_info: FramebufferInfo, + pub gr_context: DirectContext, + context: PossiblyCurrentContext, + window_surface: Surface, + config: Config, + window: Option, +} + +fn clamp_render_buffer_size(size: &PhysicalSize) -> PhysicalSize { + PhysicalSize::new( + size.width.clamp(1, MAX_RENDERBUFFER_SIZE), + size.height.clamp(1, MAX_RENDERBUFFER_SIZE), + ) +} + +fn get_proc_address(surface: &Surface, addr: &CStr) -> *const c_void { + GlDisplay::get_proc_address(&surface.display(), addr) +} + +impl OpenGLSkiaRenderer { + pub fn new(window: WindowConfig, srgb: bool, vsync: bool) -> Self { + #[allow(irrefutable_let_patterns)] // This can only be something else than OpenGL on Windows + let config = if let WindowConfigType::OpenGL(config) = window.config { + config + } else { + panic!("Not an opengl window"); + }; + let window = window.window; + let gl_display = config.display(); + let raw_window_handle = window.window_handle().unwrap().as_raw(); + + let size = clamp_render_buffer_size(&window.inner_size()); + + let surface_attributes = SurfaceAttributesBuilder::::new() + .with_srgb(Some(srgb)) + .build( + raw_window_handle, + NonZeroU32::new(size.width).unwrap(), + NonZeroU32::new(size.height).unwrap(), + ); + let window_surface = + unsafe { gl_display.create_window_surface(&config, &surface_attributes) } + .expect("Failed to create Windows Surface"); + + let context_attributes = ContextAttributesBuilder::new() + .with_profile(GlProfile::Core) + .build(Some(raw_window_handle)); + let context = unsafe { gl_display.create_context(&config, &context_attributes) } + .expect("Failed to create OpenGL context") + .make_current(&window_surface) + .unwrap(); + + // NOTE: We don't care if these fails, the driver can override the SwapInterval in any case, so it needs to work in all cases + // The OpenGL VSync is always disabled on Wayland and Windows, since they have their own + // implementation + let _ = if vsync && env::var("WAYLAND_DISPLAY").is_err() && OS != "windows" && OS != "macos" + { + window_surface + .set_swap_interval(&context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) + } else { + window_surface.set_swap_interval(&context, SwapInterval::DontWait) + }; + + gl::load_with(|s| get_proc_address(&window_surface, CString::new(s).unwrap().as_c_str())); + + let interface = skia_safe::gpu::gl::Interface::new_load_with(|name| { + if name == "eglGetCurrentDisplay" { + return std::ptr::null(); + } + get_proc_address(&window_surface, CString::new(name).unwrap().as_c_str()) + }) + .expect("Could not create interface"); + + let mut gr_context = skia_safe::gpu::direct_contexts::make_gl(interface, None) + .expect("Could not create direct context"); + let fb_info = { + let mut fboid: GLint = 0; + unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) }; + + FramebufferInfo { + fboid: fboid.try_into().expect("Could not create frame buffer id"), + format: skia_safe::gpu::gl::Format::RGBA8.into(), + ..Default::default() + } + }; + let skia_surface = create_surface( + &config, + &window.inner_size(), + &context, + &window_surface, + &mut gr_context, + &fb_info, + ); + + Self { + window_surface, + context, + window: Some(window), + config, + gr_context, + fb_info, + skia_surface, + } + } +} + +impl SkiaRenderer for OpenGLSkiaRenderer { + fn window(&self) -> &Window { + self.window.as_ref().unwrap() + } + + fn flush(&mut self) { + { + tracy_gpu_zone!("skia flush"); + self.gr_context.flush_and_submit(); + } + } + + fn swap_buffers(&mut self) { + { + tracy_gpu_zone!("swap buffers"); + self.window().pre_present_notify(); + let _ = self.window_surface.swap_buffers(&self.context); + } + } + + fn canvas(&mut self) -> &Canvas { + self.skia_surface.canvas() + } + + fn resize(&mut self) { + self.skia_surface = create_surface( + &self.config, + &self.window().inner_size(), + &self.context, + &self.window_surface, + &mut self.gr_context, + &self.fb_info, + ); + } + + #[allow(unused_variables)] + fn create_vsync(&self, proxy: EventLoopProxy) -> VSync { + #[cfg(target_os = "linux")] + if env::var("WAYLAND_DISPLAY").is_ok() { + VSync::WinitThrottling() + } else { + VSync::Opengl() + } + + #[cfg(target_os = "windows")] + { + VSync::WindowsDwm(VSyncWinDwm::new(proxy)) + } + + #[cfg(target_os = "macos")] + { + VSync::Macos(VSyncMacos::new(self.window(), proxy)) + } + } + + #[cfg(feature = "gpu_profiling")] + fn tracy_create_gpu_context(&self, name: &str) -> Box { + create_opengl_gpu_context(name) + } +} + +impl Drop for OpenGLSkiaRenderer { + fn drop(&mut self) { + match self.window_surface.display() { + #[cfg(not(target_os = "macos"))] + glutin::display::Display::Egl(display) => { + // Ensure that all the windows are dropped, so the destructors for + // Renderer and contexts ran. + self.window = None; + + self.gr_context.release_resources_and_abandon(); + + // SAFETY: the display is being destroyed after destroying all the + // windows, thus no attempt to access the EGL state will be made. + unsafe { + display.terminate(); + } + } + _ => (), + } + } +} + +fn gen_config(mut config_iterator: Box + '_>) -> Config { + config_iterator.next().unwrap() +} + +pub fn build_window( + window_attributes: WindowAttributes, + event_loop: &ActiveEventLoop, +) -> WindowConfig { + let template_builder = ConfigTemplateBuilder::new() + .with_stencil_size(8) + .with_transparency(true); + let (window, config) = DisplayBuilder::new() + .with_window_attributes(Some(window_attributes)) + .build(event_loop, template_builder, gen_config) + .expect("Failed to create Window"); + let window = window.expect("Could not create Window"); + let config = WindowConfigType::OpenGL(config); + WindowConfig { window, config } +} + +fn create_surface( + pixel_format: &Config, + size: &PhysicalSize, + context: &PossiblyCurrentContext, + window_surface: &Surface, + gr_context: &mut DirectContext, + fb_info: &FramebufferInfo, +) -> skia_safe::Surface { + let size = clamp_render_buffer_size(size); + let backend_render_target = make_gl( + size.into(), + Some(pixel_format.num_samples().into()), + pixel_format.stencil_size().into(), + *fb_info, + ); + + let width = NonZeroU32::new(size.width).unwrap(); + let height = NonZeroU32::new(size.height).unwrap(); + GlSurface::resize(window_surface, context, width, height); + + let render_settings = SETTINGS.get::(); + + let surface_props = SurfaceProps::new_with_text_properties( + SurfacePropsFlags::default(), + PixelGeometry::default(), + render_settings.text_contrast, + render_settings.text_gamma, + ); + + // NOTE: It would be much better to render using a linear gamma format, and SRGB backbuffer + // format But currently the Skia glyph atlas uses a 32-bit linear format texture, so some color + // precision is lost, and the font colors will be slightly off. + wrap_backend_render_target( + gr_context, + &backend_render_target, + SurfaceOrigin::BottomLeft, + ColorType::RGBA8888, + ColorSpace::new_srgb(), + Some(surface_props).as_ref(), + ) + .expect("Could not create skia backend render target") +} diff --git a/src/renderer/vsync/macos_display_link.rs b/src/renderer/vsync/macos_display_link.rs new file mode 100644 index 0000000000..4911a204d3 --- /dev/null +++ b/src/renderer/vsync/macos_display_link.rs @@ -0,0 +1,226 @@ +use std::{ffi::c_void, pin::Pin}; + +use crate::profiling::tracy_zone; + +use self::core_video::CVReturn; + +use icrate::{AppKit::NSView, Foundation::NSString}; +use objc2::msg_send; +use objc2::rc::Id; + +use raw_window_handle::{HasWindowHandle, RawWindowHandle}; +use winit::window::Window; + +// Display link api reference: https://developer.apple.com/documentation/corevideo/cvdisplaylink?language=objc +#[allow(non_upper_case_globals, non_camel_case_types)] +pub mod core_video { + use std::ffi::c_void; + + pub type CGDirectDisplayID = u32; + + pub type CVReturn = i32; + pub const kCVReturnSuccess: CVReturn = 0; + pub const kCVReturnDisplayLinkAlreadyRunning: CVReturn = -6671; + pub const kCVReturnDisplayLinkNotRunning: CVReturn = -6672; + // pub const kCVReturnDisplayLinkCallbacksNotSet: CVReturn = -6673; + + type SInt16 = i16; + type UInt32 = u32; + type uint32_t = u32; + type int32_t = i32; + type uint64_t = u64; + type int64_t = i64; + type double = f64; + + #[repr(C)] + #[allow(non_snake_case)] + pub struct CVSMPTETime { + subframes: SInt16, + subframeDivisor: SInt16, + counter: UInt32, + _type: UInt32, + flags: UInt32, + hours: SInt16, + minutes: SInt16, + seconds: SInt16, + frames: SInt16, + } + + #[repr(C)] + #[allow(non_snake_case)] + pub struct CVTimeStamp { + version: uint32_t, + videoTimeScale: int32_t, + videoTime: int64_t, + hostTime: uint64_t, + rateScalar: double, + videoRefreshPeriod: int64_t, + smpteTime: CVSMPTETime, + flags: uint64_t, + reserved: uint64_t, + } + + pub type CVDisplayLinkRef = *mut c_void; + + pub type CVDisplayLinkOutputCallback = extern "C" fn( + displayLink: CVDisplayLinkRef, + inNow: *const CVTimeStamp, + inOutputTime: *const CVTimeStamp, + flagsIn: u64, + flagsOut: *mut u64, + displayLinkContext: *mut c_void, + ) -> CVReturn; + + #[link(name = "CoreVideo", kind = "framework")] + extern "C" { + pub fn CVDisplayLinkCreateWithCGDisplay( + displayID: CGDirectDisplayID, + displayLinkOut: *mut CVDisplayLinkRef, + ) -> CVReturn; + pub fn CVDisplayLinkRelease(displayLink: CVDisplayLinkRef); + pub fn CVDisplayLinkStart(displayLink: CVDisplayLinkRef) -> CVReturn; + // Because display link is destroyed directly, this function is unnecessary + #[allow(dead_code)] + pub fn CVDisplayLinkStop(displayLink: CVDisplayLinkRef) -> CVReturn; + pub fn CVDisplayLinkSetOutputCallback( + displayLink: CVDisplayLinkRef, + callback: CVDisplayLinkOutputCallback, + userInfo: *mut c_void, + ) -> CVReturn; + } +} + +pub struct MacosDisplayLinkCallbackArgs { + // some_info: ... in future +} + +pub type MacosDisplayLinkCallback = fn(&mut MacosDisplayLinkCallbackArgs, &mut UserData); + +struct MacosDisplayLinkCallbackContext { + callback: MacosDisplayLinkCallback, + user_data: UserData, +} + +pub struct MacosDisplayLink { + display_link_ref: core_video::CVDisplayLinkRef, + // The context must be pinned since it is passed as a pointer to callback. If it moves, the pointer will be dangling. + context: Pin>>, +} + +#[allow(unused_variables, non_snake_case)] +extern "C" fn c_callback( + displayLink: core_video::CVDisplayLinkRef, + inNow: *const core_video::CVTimeStamp, + inOutputTime: *const core_video::CVTimeStamp, + flagsIn: u64, + flagsOut: *mut u64, + displayLinkContext: *mut c_void, +) -> core_video::CVReturn { + tracy_zone!("VSyncDisplayLinkCallback"); + + // The display link should be dropped before vsync, so this should be safe. + let context = + unsafe { &mut *(displayLinkContext as *mut MacosDisplayLinkCallbackContext) }; + + let mut args = MacosDisplayLinkCallbackArgs {}; + + (context.callback)(&mut args, &mut context.user_data); + + core_video::kCVReturnSuccess +} + +impl MacosDisplayLink { + pub fn new_from_display( + display_id: core_video::CGDirectDisplayID, + callback: MacosDisplayLinkCallback, + user_data: UserData, + ) -> Result { + let mut display_link = Self { + display_link_ref: std::ptr::null_mut(), + context: Box::>::pin( + MacosDisplayLinkCallbackContext { + callback, + user_data, + }, + ), + }; + + unsafe { + let result = core_video::CVDisplayLinkCreateWithCGDisplay( + display_id, + &mut display_link.display_link_ref, + ); + + if result != core_video::kCVReturnSuccess { + return Err(result); + } + + core_video::CVDisplayLinkSetOutputCallback( + display_link.display_link_ref, + c_callback::, + // Cast the display link to an unsafe pointer and pass to display link. + &*display_link.context as *const MacosDisplayLinkCallbackContext + as *mut c_void, + ); + } + + Ok(display_link) + } + + pub fn start(&self) -> Result { + unsafe { + let result = core_video::CVDisplayLinkStart(self.display_link_ref); + + match result { + core_video::kCVReturnSuccess => Ok(true), + core_video::kCVReturnDisplayLinkAlreadyRunning => Ok(false), + _ => Err(result), + } + } + } + + // Because display link is destroyed directly, this function is unnecessary + #[allow(dead_code)] + pub fn stop(&self) -> Result { + unsafe { + let result = core_video::CVDisplayLinkStop(self.display_link_ref); + + match result { + core_video::kCVReturnSuccess => Ok(true), + core_video::kCVReturnDisplayLinkNotRunning => Ok(false), + _ => Err(result), + } + } + } +} + +impl Drop for MacosDisplayLink { + fn drop(&mut self) { + unsafe { + core_video::CVDisplayLinkRelease(self.display_link_ref); + } + } +} + +// Here is the doc about how to do this. https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription?language=objc +pub fn get_display_id_of_window(window: &Window) -> core_video::CGDirectDisplayID { + unsafe fn get_display_id(window: &Window) -> Option { + let key: Id = NSString::from_str("NSScreenNumber"); + if let RawWindowHandle::AppKit(handle) = window.window_handle().unwrap().as_raw() { + let ns_view = handle.ns_view.as_ptr(); + let ns_view: Id = unsafe { Id::retain(ns_view.cast()) }.unwrap(); + let ns_window = ns_view + .window() + .expect("view was not installed in a window"); + let screen = ns_window.screen()?; + let descr = screen.deviceDescription(); + let display_id_ns_number = descr.get(&key)?; + let res = msg_send![display_id_ns_number, unsignedIntValue]; + Some(res) + } else { + // Should be impossible. + panic!("Not an AppKitWindowHandle.") + } + } + unsafe { get_display_id(window) }.unwrap_or(0) +} diff --git a/src/window/error_window.rs b/src/window/error_window.rs index eb067656c0..21033f323e 100644 --- a/src/window/error_window.rs +++ b/src/window/error_window.rs @@ -10,11 +10,12 @@ use skia_safe::{ use strum::IntoEnumIterator; use strum::{EnumCount, EnumIter}; use winit::{ + application::ApplicationHandler, dpi::PhysicalSize, - event::{ElementState, Event, KeyEvent, Modifiers, MouseScrollDelta, WindowEvent}, - event_loop::{EventLoop, EventLoopWindowTarget}, + event::{ElementState, KeyEvent, Modifiers, MouseScrollDelta, WindowEvent}, + event_loop::{ActiveEventLoop, EventLoop}, keyboard::{Key, NamedKey}, - window::WindowBuilder, + window::Window, }; use crate::{ @@ -34,7 +35,7 @@ const DEFAULT_SIZE: PhysicalSize = PhysicalSize::new(800, 600); pub fn show_error_window(message: &str, event_loop: EventLoop) { let mut error_window = ErrorWindow::new(message); - error_window.run_event_loop(event_loop); + event_loop.run_app(&mut error_window).ok(); } #[derive(Debug)] @@ -83,25 +84,28 @@ impl<'a> ErrorWindow<'a> { message, } } +} - fn run_event_loop(&mut self, event_loop: EventLoop) { - let _ = event_loop.run(|e, window_target| match e { - Event::Resumed => { - if self.state.is_none() { - self.state = Some(State::new(self.message, window_target)); - } - } - Event::WindowEvent { event, .. } => { - let state = self.state.as_mut().unwrap(); - state.handle_window_event(event, window_target, self.message); - } - _ => {} - }); +impl<'a> ApplicationHandler for ErrorWindow<'a> { + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: winit::window::WindowId, + event: WindowEvent, + ) { + let state = self.state.as_mut().unwrap(); + state.handle_window_event(event, event_loop, self.message); + } + + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + if self.state.is_none() { + self.state = Some(State::new(self.message, event_loop)); + } } } impl State { - fn new(message: &str, event_loop: &EventLoopWindowTarget) -> Self { + fn new(message: &str, event_loop: &ActiveEventLoop) -> Self { let message = message.trim_end(); let font_manager = FontMgr::new(); @@ -137,12 +141,12 @@ impl State { fn handle_window_event( &mut self, event: WindowEvent, - window_target: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, message: &str, ) { match event { WindowEvent::CloseRequested => { - window_target.exit(); + event_loop.exit(); } WindowEvent::RedrawRequested => { self.render(); @@ -161,7 +165,7 @@ impl State { is_synthetic: false, .. } => { - if self.handle_keyboard_input(event, window_target, message) { + if self.handle_keyboard_input(event, event_loop, message) { self.skia_renderer.window().request_redraw(); } } @@ -212,7 +216,7 @@ impl State { fn handle_keyboard_input( &mut self, event: KeyEvent, - window_target: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, message: &str, ) -> bool { if event.state != ElementState::Pressed { @@ -244,7 +248,7 @@ impl State { true } "q" => { - window_target.exit(); + event_loop.exit(); true } "y" => { @@ -267,7 +271,7 @@ impl State { true } NamedKey::Escape => { - window_target.exit(); + event_loop.exit(); true } _ => false, @@ -480,10 +484,10 @@ fn create_paragraphs( } } -fn create_window(event_loop: &EventLoopWindowTarget) -> WindowConfig { +fn create_window(event_loop: &ActiveEventLoop) -> WindowConfig { let icon = load_icon(); - let winit_window_builder = WindowBuilder::new() + let window_attributes = Window::default_attributes() .with_title("Neovide") .with_window_icon(Some(icon)) .with_transparent(false) @@ -492,5 +496,5 @@ fn create_window(event_loop: &EventLoopWindowTarget) -> WindowConfig .with_inner_size(DEFAULT_SIZE) .with_min_inner_size(MIN_SIZE); - build_window_config(winit_window_builder, event_loop) + build_window_config(window_attributes, event_loop) } diff --git a/src/window/macos.rs b/src/window/macos.rs index 855a7132d3..9f2d9f1bec 100644 --- a/src/window/macos.rs +++ b/src/window/macos.rs @@ -16,14 +16,11 @@ use objc2::{ }; use csscolorparser::Color; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; -use winit::event::{Event, WindowEvent}; +use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use winit::window::Window; use crate::bridge::{send_ui, ParallelCommand}; -use crate::{ - cmd_line::CmdLineSettings, error_msg, frame::Frame, settings::SETTINGS, window::UserEvent, -}; +use crate::{cmd_line::CmdLineSettings, error_msg, frame::Frame, settings::SETTINGS}; use super::{WindowSettings, WindowSettingsChanged}; @@ -70,9 +67,13 @@ pub struct MacosWindowFeature { impl MacosWindowFeature { pub fn from_winit_window(window: &Window, mtm: MainThreadMarker) -> MacosWindowFeature { - let ns_window = match window.raw_window_handle() { + let ns_window = match window.window_handle().unwrap().as_raw() { RawWindowHandle::AppKit(handle) => unsafe { - Id::retain(handle.ns_window as *mut NSWindow).unwrap() + let ns_view = handle.ns_view.as_ptr(); + let ns_view: Id = Id::retain(ns_view.cast()).unwrap(); + ns_view + .window() + .expect("view was not installed in a window") }, _ => panic!("Not an appkit window."), }; @@ -298,16 +299,10 @@ impl Menu { quit_handler: QuitHandler::new(mtm), } } - pub fn ensure_menu_added(&mut self, ev: &Event) { - if let Event::WindowEvent { - event: WindowEvent::Focused(_), - .. - } = ev - { - if !self.menu_added { - self.add_menus(); - self.menu_added = true; - } + pub fn ensure_menu_added(&mut self) { + if !self.menu_added { + self.add_menus(); + self.menu_added = true; } } diff --git a/src/window/mod.rs b/src/window/mod.rs index bce7e9d562..bfecbfeab1 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -11,22 +11,17 @@ mod macos; #[cfg(target_os = "linux")] use std::env; -#[cfg(target_os = "macos")] -use icrate::Foundation::MainThreadMarker; - use winit::{ dpi::{PhysicalSize, Size}, - error::EventLoopError, - event::Event, - event_loop::{EventLoop, EventLoopBuilder, EventLoopWindowTarget}, - window::{Icon, Theme, Window, WindowBuilder}, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Icon, Theme, Window}, }; #[cfg(target_os = "macos")] -use winit::platform::macos::WindowBuilderExtMacOS; +use winit::platform::macos::WindowAttributesExtMacOS; #[cfg(target_os = "linux")] -use winit::platform::{wayland::WindowBuilderExtWayland, x11::WindowBuilderExtX11}; +use winit::platform::{wayland::WindowAttributesExtWayland, x11::WindowAttributesExtX11}; #[cfg(target_os = "macos")] use winit::platform::macos::EventLoopBuilderExtMacOS; @@ -34,21 +29,21 @@ use winit::platform::macos::EventLoopBuilderExtMacOS; use image::{load_from_memory, GenericImageView, Pixel}; use keyboard_manager::KeyboardManager; use mouse_manager::MouseManager; -use update_loop::UpdateLoop; use crate::{ cmd_line::{CmdLineSettings, GeometryArgs}, frame::Frame, renderer::DrawCommand, settings::{ - clamped_grid_size, load_last_window_settings, save_window_size, FontSettings, - HotReloadConfigs, PersistentWindowSettings, SettingsChanged, SETTINGS, + clamped_grid_size, load_last_window_settings, save_window_size, HotReloadConfigs, + PersistentWindowSettings, SettingsChanged, SETTINGS, }, units::GridSize, }; //pub use error_window::show_error_window; pub use settings::{WindowSettings, WindowSettingsChanged}; pub use update_loop::ShouldRender; +pub use update_loop::UpdateLoop; pub use window_wrapper::WinitWindowWrapper; static ICON: &[u8] = include_bytes!("../../assets/neovide.ico"); @@ -117,7 +112,7 @@ impl From for UserEvent { } pub fn create_event_loop() -> EventLoop { - let mut builder = EventLoopBuilder::::with_user_event(); + let mut builder = EventLoop::with_user_event(); #[cfg(target_os = "macos")] builder.with_default_menu(false); let event_loop = builder.build().expect("Failed to create winit event loop"); @@ -127,11 +122,7 @@ pub fn create_event_loop() -> EventLoop { event_loop } -pub fn create_window( - event_loop: &EventLoopWindowTarget, - maximized: bool, - title: &str, -) -> Window { +pub fn create_window(event_loop: &ActiveEventLoop, maximized: bool, title: &str) -> Window { let icon = load_icon(); let cmd_line_settings = SETTINGS.get::(); @@ -143,7 +134,7 @@ pub fn create_window( _ => None, }; - let winit_window_builder = WindowBuilder::new() + let window_attributes = Window::default_attributes() .with_title(title) .with_window_icon(Some(icon)) .with_maximized(maximized) @@ -157,45 +148,44 @@ pub fn create_window( // There is only two options for windows & linux, no need to match more options. #[cfg(not(target_os = "macos"))] - let mut winit_window_builder = - winit_window_builder.with_decorations(frame_decoration == Frame::Full); + let mut window_attributes = window_attributes.with_decorations(frame_decoration == Frame::Full); #[cfg(target_os = "macos")] - let mut winit_window_builder = match frame_decoration { - Frame::Full => winit_window_builder, - Frame::None => winit_window_builder.with_decorations(false), - Frame::Buttonless => winit_window_builder + let mut window_attributes = match frame_decoration { + Frame::Full => window_attributes, + Frame::None => window_attributes.with_decorations(false), + Frame::Buttonless => window_attributes .with_title_hidden(title_hidden) .with_titlebar_buttons_hidden(true) .with_titlebar_transparent(true) .with_fullsize_content_view(true), - Frame::Transparent => winit_window_builder + Frame::Transparent => window_attributes .with_title_hidden(title_hidden) .with_titlebar_transparent(true) .with_fullsize_content_view(true), }; if let Some(previous_position) = previous_position { - winit_window_builder = winit_window_builder.with_position(previous_position); + window_attributes = window_attributes.with_position(previous_position); } #[cfg(target_os = "linux")] - let winit_window_builder = { + let window_attributes = { if env::var("WAYLAND_DISPLAY").is_ok() { let app_id = &cmd_line_settings.wayland_app_id; - WindowBuilderExtWayland::with_name(winit_window_builder, "neovide", app_id.clone()) + WindowAttributesExtWayland::with_name(window_attributes, "neovide", app_id.clone()) } else { let class = &cmd_line_settings.x11_wm_class; let instance = &cmd_line_settings.x11_wm_class_instance; - WindowBuilderExtX11::with_name(winit_window_builder, class, instance) + WindowAttributesExtX11::with_name(window_attributes, class, instance) } }; #[cfg(target_os = "macos")] - let winit_window_builder = winit_window_builder.with_accepts_first_mouse(false); + let window_attributes = window_attributes.with_accepts_first_mouse(false); #[allow(clippy::let_and_return)] - let window = winit_window_builder.build(event_loop).unwrap(); + let window = event_loop.create_window(window_attributes).unwrap(); #[cfg(target_os = "macos")] if let Some(previous_position) = previous_position { @@ -257,54 +247,6 @@ pub fn determine_window_size(window_settings: Option<&PersistentWindowSettings>) } } -pub fn main_loop( - initial_window_size: WindowSize, - initial_font_settings: Option, - event_loop: EventLoop, -) -> Result<(), EventLoopError> { - let cmd_line_settings = SETTINGS.get::(); - let mut update_loop = UpdateLoop::new(cmd_line_settings.idle); - - let mut window_wrapper = Some(WinitWindowWrapper::new( - initial_window_size, - initial_font_settings, - )); - let mut create_window_allowed = false; - - #[cfg(target_os = "macos")] - let mut menu = { - let mtm = MainThreadMarker::new().expect("must be on the main thread"); - macos::Menu::new(mtm) - }; - let res = event_loop.run(move |e, window_target| { - #[cfg(target_os = "macos")] - menu.ensure_menu_added(&e); - - match e { - Event::Resumed => { - create_window_allowed = true; - if let Some(window_wrapper) = &mut window_wrapper { - window_wrapper.try_create_window(window_target); - } - } - Event::LoopExiting => window_wrapper = None, - Event::UserEvent(UserEvent::NeovimExited) => { - save_window_size(window_wrapper.as_ref().unwrap()); - window_target.exit(); - } - _ => { - if let Some(window_wrapper) = &mut window_wrapper { - window_target.set_control_flow(update_loop.step(window_wrapper, e)); - if create_window_allowed { - window_wrapper.try_create_window(window_target); - } - } - } - } - }); - res -} - pub fn load_icon() -> Icon { let icon = load_from_memory(ICON).expect("Failed to parse icon data"); let (width, height) = icon.dimensions(); diff --git a/src/window/update_loop.rs b/src/window/update_loop.rs index 4ec9029df0..15e369321b 100644 --- a/src/window/update_loop.rs +++ b/src/window/update_loop.rs @@ -1,16 +1,25 @@ use std::time::{Duration, Instant}; use winit::{ - event::{Event, WindowEvent}, - event_loop::ControlFlow, + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, ControlFlow}, }; -use super::{UserEvent, WindowSettings, WinitWindowWrapper}; +#[cfg(target_os = "macos")] +use icrate::Foundation::MainThreadMarker; + +use super::{save_window_size, CmdLineSettings, UserEvent, WindowSettings, WinitWindowWrapper}; use crate::{ profiling::{tracy_plot, tracy_zone}, + renderer::DrawCommand, settings::SETTINGS, + FontSettings, WindowSize, }; +#[cfg(target_os = "macos")] +use super::macos::Menu; + enum FocusedState { Focused, UnfocusedNotDrawn, @@ -76,13 +85,21 @@ pub struct UpdateLoop { num_consecutive_rendered: u32, focused: FocusedState, pending_render: bool, // We should render as soon as the compositor/vsync allows - pending_draw_commands: Vec>, + pending_draw_commands: Vec>, animation_start: Instant, // When the last animation started (went from idle to animating) animation_time: Duration, // How long the current animation has been simulated, will usually be in the future + + window_wrapper: WinitWindowWrapper, + create_window_allowed: bool, + #[cfg(target_os = "macos")] + menu: Menu, } impl UpdateLoop { - pub fn new(idle: bool) -> Self { + pub fn new( + initial_window_size: WindowSize, + initial_font_settings: Option, + ) -> Self { let previous_frame_start = Instant::now(); let last_dt = 0.0; let should_render = ShouldRender::Immediately; @@ -93,6 +110,17 @@ impl UpdateLoop { let animation_start = Instant::now(); let animation_time = Duration::from_millis(0); + #[cfg(target_os = "macos")] + let menu = { + let mtm = MainThreadMarker::new().expect("must be on the main thread"); + Menu::new(mtm) + }; + + let cmd_line_settings = SETTINGS.get::(); + let idle = cmd_line_settings.idle; + + let window_wrapper = WinitWindowWrapper::new(initial_window_size, initial_font_settings); + Self { idle, previous_frame_start, @@ -104,6 +132,12 @@ impl UpdateLoop { pending_draw_commands, animation_start, animation_time, + + window_wrapper, + create_window_allowed: false, + + #[cfg(target_os = "macos")] + menu, } } @@ -138,12 +172,22 @@ impl UpdateLoop { } } - fn animate(&mut self, window_wrapper: &mut WinitWindowWrapper) { - if window_wrapper.window.is_none() { + fn schedule_next_event(&mut self, event_loop: &ActiveEventLoop) { + #[cfg(feature = "profiling")] + self.should_render.plot_tracy(); + if self.create_window_allowed { + self.window_wrapper + .try_create_window(event_loop); + } + event_loop.set_control_flow(ControlFlow::WaitUntil(self.get_event_deadline())); + } + + fn animate(&mut self) { + if self.window_wrapper.window.is_none() { return; } - let window = window_wrapper.window.as_ref().unwrap(); - let vsync = window_wrapper.vsync.as_mut().unwrap(); + let window = self.window_wrapper.window.as_ref().unwrap(); + let vsync = self.window_wrapper.vsync.as_mut().unwrap(); let dt = Duration::from_secs_f32(vsync.get_refresh_rate(window)); @@ -171,16 +215,16 @@ impl UpdateLoop { let num_steps = (dt.as_secs_f64() / MAX_ANIMATION_DT).ceil() as u32; let step = dt / num_steps; for _ in 0..num_steps { - if window_wrapper.animate_frame(step.as_secs_f32()) { + if self.window_wrapper.animate_frame(step.as_secs_f32()) { self.should_render = ShouldRender::Immediately; } } } - fn render(&mut self, window_wrapper: &mut WinitWindowWrapper) { + fn render(&mut self) { self.pending_render = false; tracy_plot!("pending_render", self.pending_render as u8 as f64); - window_wrapper.draw_frame(self.last_dt); + self.window_wrapper.draw_frame(self.last_dt); if let FocusedState::UnfocusedNotDrawn = self.focused { self.focused = FocusedState::Unfocused; @@ -195,11 +239,12 @@ impl UpdateLoop { self.previous_frame_start = Instant::now(); } - fn process_buffered_draw_commands(&mut self, window_wrapper: &mut WinitWindowWrapper) { - for e in self.pending_draw_commands.drain(..) { - if window_wrapper.handle_event(e) { - self.should_render = ShouldRender::Immediately; - } + fn process_buffered_draw_commands(&mut self) { + if !self.pending_draw_commands.is_empty() { + self.pending_draw_commands + .drain(..) + .for_each(|b| self.window_wrapper.handle_draw_commands(b)); + self.should_render = ShouldRender::Immediately; } } @@ -211,12 +256,12 @@ impl UpdateLoop { } } - fn schedule_render(&mut self, skipped_frame: bool, window_wrapper: &mut WinitWindowWrapper) { - if window_wrapper.window.is_none() { + fn schedule_render(&mut self, skipped_frame: bool) { + if self.window_wrapper.window.is_none() { return; } - let window = window_wrapper.window.as_ref().unwrap(); - let vsync = window_wrapper.vsync.as_mut().unwrap(); + let window = self.window_wrapper.window.as_ref().unwrap(); + let vsync = self.window_wrapper.vsync.as_mut().unwrap(); // There's really no point in trying to render if the frame is skipped // (most likely due to the compositor being busy). The animated frame will @@ -229,17 +274,17 @@ impl UpdateLoop { self.pending_render = true; tracy_plot!("pending_render", self.pending_render as u8 as f64); } else { - self.render(window_wrapper); + self.render(); } } } - fn prepare_and_animate(&mut self, window_wrapper: &mut WinitWindowWrapper) { + fn prepare_and_animate(&mut self) { // We will also animate, but not render when frames are skipped or a bit late, to reduce visual artifacts let skipped_frame = self.pending_render && Instant::now() > (self.animation_start + self.animation_time); - let res = window_wrapper.prepare_frame(); + let res = self.window_wrapper.prepare_frame(); self.should_render.update(res); let should_animate = @@ -247,8 +292,8 @@ impl UpdateLoop { if should_animate { self.reset_animation_period(); - self.animate(window_wrapper); - self.schedule_render(skipped_frame, window_wrapper); + self.animate(); + self.schedule_render(skipped_frame); } else { self.num_consecutive_rendered = 0; tracy_plot!( @@ -260,61 +305,89 @@ impl UpdateLoop { } } - pub fn step( + fn redraw_requested(&mut self) { + if self.pending_render { + tracy_zone!("render (redraw requested)"); + self.render(); + // We should process all buffered draw commands as soon as the rendering has finished + self.process_buffered_draw_commands(); + } else { + tracy_zone!("redraw requested"); + // The OS itself asks us to redraw, so we need to prepare first + self.should_render = ShouldRender::Immediately; + } + } +} + +impl ApplicationHandler for UpdateLoop { + fn window_event( &mut self, - window_wrapper: &mut WinitWindowWrapper, - event: Event, - ) -> ControlFlow { - tracy_zone!("render loop", 0); + event_loop: &ActiveEventLoop, + _window_id: winit::window::WindowId, + event: winit::event::WindowEvent, + ) { + tracy_zone!("window_event"); match event { - // Window focus changed - Event::WindowEvent { - event: WindowEvent::Focused(focused_event), - .. - } => { + WindowEvent::RedrawRequested => { + self.redraw_requested(); + } + WindowEvent::Focused(focused_event) => { self.focused = if focused_event { FocusedState::Focused } else { FocusedState::UnfocusedNotDrawn }; - } - Event::AboutToWait => { - self.prepare_and_animate(window_wrapper); - } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } - | Event::UserEvent(UserEvent::RedrawRequested) => { - if self.pending_render { - tracy_zone!("render (redraw requested)"); - self.render(window_wrapper); - // We should process all buffered draw commands as soon as the rendering has finished - self.process_buffered_draw_commands(window_wrapper); - } else { - tracy_zone!("redraw requested"); - // The OS itself asks us to redraw, so we need to prepare first - self.should_render = ShouldRender::Immediately; - } + #[cfg(target_os = "macos")] + self.menu.ensure_menu_added(); } _ => {} } - if self.pending_render && matches!(&event, Event::UserEvent(UserEvent::DrawCommandBatch(_))) - { - // Buffer the draw commands if we have a pending render, we have already decided what to - // draw, so it's not a good idea to process them now. - // They will be processed immediately after the rendering. - self.pending_draw_commands.push(event); - } else if window_wrapper.handle_event(event) { - // But we need to handle other events (in the if statement itself) - // Also schedule a render as soon as possible + if self.window_wrapper.handle_window_event(event) { self.should_render = ShouldRender::Immediately; } + self.schedule_next_event(event_loop); + } - #[cfg(feature = "profiling")] - self.should_render.plot_tracy(); + fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { + tracy_zone!("user_event"); + match event { + UserEvent::NeovimExited => { + save_window_size(&self.window_wrapper); + event_loop.exit(); + } + UserEvent::RedrawRequested => { + self.redraw_requested(); + } + UserEvent::DrawCommandBatch(batch) if self.pending_render => { + // Buffer the draw commands if we have a pending render, we have already decided what to + // draw, so it's not a good idea to process them now. + // They will be processed immediately after the rendering. + self.pending_draw_commands.push(batch); + } + _ => { + self.window_wrapper.handle_user_event(event); + self.should_render = ShouldRender::Immediately; + } + } + self.schedule_next_event(event_loop); + } + + fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { + tracy_zone!("about_to_wait"); + self.prepare_and_animate(); + self.schedule_next_event(event_loop); + } + + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + tracy_zone!("resumed"); + self.create_window_allowed = true; + self.schedule_next_event(event_loop); + } - ControlFlow::WaitUntil(self.get_event_deadline()) + fn exiting(&mut self, event_loop: &ActiveEventLoop) { + tracy_zone!("exiting"); + self.window_wrapper.exit(); + self.schedule_next_event(event_loop); } } diff --git a/src/window/window_wrapper.rs b/src/window/window_wrapper.rs index ec5115102c..529feb5345 100644 --- a/src/window/window_wrapper.rs +++ b/src/window/window_wrapper.rs @@ -32,11 +32,11 @@ use super::macos::MacosWindowFeature; use icrate::Foundation::MainThreadMarker; use log::trace; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use winit::{ dpi, - event::{Event, Ime, WindowEvent}, - event_loop::EventLoopWindowTarget, + event::{Ime, WindowEvent}, + event_loop::ActiveEventLoop, window::{Fullscreen, Theme, Window}, }; @@ -120,6 +120,11 @@ impl WinitWindowWrapper { } } + pub fn exit(&mut self) { + self.renderer.exit(); + self.vsync = None; + } + pub fn set_fullscreen(&mut self, fullscreen: bool) { if let Some(window) = &self.window { if fullscreen { @@ -285,60 +290,7 @@ impl WinitWindowWrapper { } } - /// Handles an event from winit and returns an boolean indicating if - /// the window should be rendered. - pub fn handle_event(&mut self, event: Event) -> bool { - tracy_zone!("handle_event", 0); - - let renderer_asks_to_be_rendered = self.renderer.handle_event(&event); - let mut should_render = true; - match event { - Event::Resumed => { - tracy_zone!("Resumed"); - // No need to do anything, but handle the event so that should_render gets set - } - Event::WindowEvent { event, .. } => { - if !self.handle_window_event(event) { - should_render = renderer_asks_to_be_rendered; - } - } - Event::UserEvent(UserEvent::DrawCommandBatch(batch)) => { - self.handle_draw_commands(batch); - } - Event::UserEvent(UserEvent::WindowCommand(e)) => { - self.handle_window_command(e); - } - Event::UserEvent(UserEvent::SettingsChanged(SettingsChanged::Window(e))) => { - self.handle_window_settings_changed(e); - } - Event::UserEvent(UserEvent::SettingsChanged(SettingsChanged::Renderer(e))) => { - self.handle_render_settings_changed(e); - } - Event::UserEvent(UserEvent::ConfigsChanged(config)) => { - self.handle_config_changed(*config); - } - _ => { - match event { - Event::AboutToWait { .. } => { - tracy_zone!("AboutToWait"); - } - Event::DeviceEvent { .. } => { - tracy_zone!("DeviceEvent"); - } - Event::NewEvents(..) => { - tracy_zone!("NewEvents"); - } - _ => { - tracy_zone!("Unknown"); - } - } - should_render = renderer_asks_to_be_rendered; - } - } - self.ui_state >= UIState::FirstFrame && should_render - } - - fn handle_window_event(&mut self, event: WindowEvent) -> bool { + pub fn handle_window_event(&mut self, event: WindowEvent) -> bool { // The renderer and vsync should always be created when a window event is received let window = self.window.as_mut().unwrap(); let vsync = self.vsync.as_mut().unwrap(); @@ -346,6 +298,8 @@ impl WinitWindowWrapper { self.mouse_manager .handle_event(&event, &self.keyboard_manager, &self.renderer, window); self.keyboard_manager.handle_event(&event); + self.renderer.handle_event(&event); + let mut should_render = true; match event { WindowEvent::CloseRequested => { @@ -399,10 +353,31 @@ impl WinitWindowWrapper { } _ => { tracy_zone!("Unknown WindowEvent"); - return false; + should_render = false; } } - true + self.ui_state >= UIState::FirstFrame && should_render + } + + pub fn handle_user_event(&mut self, event: UserEvent) { + match event { + UserEvent::DrawCommandBatch(batch) => { + self.handle_draw_commands(batch); + } + UserEvent::WindowCommand(e) => { + self.handle_window_command(e); + } + UserEvent::SettingsChanged(SettingsChanged::Window(e)) => { + self.handle_window_settings_changed(e); + } + UserEvent::SettingsChanged(SettingsChanged::Renderer(e)) => { + self.handle_render_settings_changed(e); + } + UserEvent::ConfigsChanged(config) => { + self.handle_config_changed(*config); + } + _ => {} + } } pub fn draw_frame(&mut self, dt: f32) { @@ -438,7 +413,7 @@ impl WinitWindowWrapper { res } - pub fn try_create_window(&mut self, event_loop: &EventLoopWindowTarget) { + pub fn try_create_window(&mut self, event_loop: &ActiveEventLoop) { if self.ui_state != UIState::WaitingForWindowCreate { return; } @@ -522,7 +497,10 @@ impl WinitWindowWrapper { self.renderer.create_wgpu(window.clone()); log::info!("Showing window size: {:#?}, maximized: {}", size, maximized); - let is_wayland = matches!(window.raw_window_handle(), RawWindowHandle::Wayland(_)); + let is_wayland = matches!( + window.window_handle().unwrap().as_raw(), + RawWindowHandle::Wayland(_) + ); // On Wayland we can show the window now, since internally it's only shown after the first rendering // On the other platforms the window is shown after rendering to avoid flickering if is_wayland { @@ -570,7 +548,7 @@ impl WinitWindowWrapper { self.set_macos_option_as_meta(input_macos_option_key_is_meta); } - fn handle_draw_commands(&mut self, batch: Vec) { + pub fn handle_draw_commands(&mut self, batch: Vec) { tracy_zone!("handle_draw_commands"); let handle_draw_commands_result = self.renderer.handle_draw_commands(batch);