From 85174a79b752a5d6d77a998f583f305a0c3eeea1 Mon Sep 17 00:00:00 2001 From: Nick Hudkins Date: Thu, 12 Sep 2024 16:22:15 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=AA=9F=F0=9F=AB=B7Raw=20Window=20Handle?= =?UTF-8?q?=20Support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 121 +++++++++++++++++++++++++++---- Cargo.toml | 10 +++ crates/notan_app/Cargo.toml | 1 + crates/notan_app/src/backend.rs | 3 + crates/notan_app/src/empty.rs | 5 ++ crates/notan_winit/src/window.rs | 6 ++ examples/raw_window_handle.rs | 51 +++++++++++++ 7 files changed, 184 insertions(+), 13 deletions(-) create mode 100644 examples/raw_window_handle.rs diff --git a/Cargo.lock b/Cargo.lock index 85341aa9..5efbb5e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,6 +360,36 @@ dependencies = [ "cc", ] +[[package]] +name = "cocoa" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" +dependencies = [ + "bitflags 2.4.0", + "block", + "cocoa-foundation", + "core-foundation 0.10.0", + "core-graphics 0.24.0", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" +dependencies = [ + "bitflags 2.4.0", + "block", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "libc", + "objc", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -413,7 +443,17 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ - "core-foundation-sys 0.8.4", + "core-foundation-sys 0.8.7", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys 0.8.7", "libc", ] @@ -425,9 +465,9 @@ checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" @@ -436,9 +476,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", + "core-foundation 0.9.3", + "core-graphics-types 0.1.2", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.4.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "foreign-types 0.5.0", "libc", ] @@ -449,7 +502,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.4.0", + "core-foundation 0.10.0", "libc", ] @@ -480,7 +544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c" dependencies = [ "alsa", - "core-foundation-sys 0.8.4", + "core-foundation-sys 0.8.7", "coreaudio-rs", "dasp_sample", "jni 0.19.0", @@ -851,7 +915,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", ] [[package]] @@ -860,6 +945,12 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -1056,7 +1147,7 @@ dependencies = [ "bitflags 1.3.2", "cfg_aliases 0.1.1", "cgl", - "core-foundation", + "core-foundation 0.9.3", "dispatch", "glutin_egl_sys", "glutin_glx_sys", @@ -1629,6 +1720,7 @@ name = "notan" version = "0.12.1" dependencies = [ "bytemuck", + "cocoa", "egui_demo_lib", "notan_app", "notan_audio", @@ -1646,6 +1738,8 @@ dependencies = [ "notan_random", "notan_text", "notan_utils", + "objc", + "raw-window-handle", ] [[package]] @@ -1667,6 +1761,7 @@ dependencies = [ "notan_utils", "parking_lot", "platter2", + "raw-window-handle", "serde", "wasm-bindgen-futures", "web-sys", @@ -3173,7 +3268,7 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82b2391658b02c27719fc5a0a73d6e696285138e8b12fba9d4baa70451023c71" dependencies = [ - "core-foundation", + "core-foundation 0.9.3", "home", "jni 0.21.1", "log", @@ -3374,8 +3469,8 @@ dependencies = [ "android-activity", "bitflags 1.3.2", "cfg_aliases 0.1.1", - "core-foundation", - "core-graphics", + "core-foundation 0.9.3", + "core-graphics 0.22.3", "dispatch", "instant", "libc", diff --git a/Cargo.toml b/Cargo.toml index eacb6a46..0d1f4d26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,6 +79,12 @@ notan_text = { workspace = true, optional = true } notan_audio = { workspace = true, optional = true } notan_extra = { workspace = true, optional = true } notan_random = { workspace = true, optional = true } +raw-window-handle = "0.5.2" + +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.26.0" +objc = "0.2.7" + [features] default = ["backend", "log", "draw", "random", "glsl-to-spirv"] @@ -330,6 +336,10 @@ required-features = ["draw"] name = "log_basic" required-features = ["log"] +[[example]] +name = "raw_window_handle" +required-features = ["draw"] + [[example]] name = "renderer_instancing_cubes" required-features = ["random"] diff --git a/crates/notan_app/Cargo.toml b/crates/notan_app/Cargo.toml index e9015ccf..c0696d66 100644 --- a/crates/notan_app/Cargo.toml +++ b/crates/notan_app/Cargo.toml @@ -27,6 +27,7 @@ serde = { workspace = true, optional = true } downcast-rs = "1.2.0" indexmap = "2.0.2" futures = "0.3.28" +raw-window-handle = "0.5.2" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] platter2 = "0.1.6" diff --git a/crates/notan_app/src/backend.rs b/crates/notan_app/src/backend.rs index 054b90aa..c8762ae7 100644 --- a/crates/notan_app/src/backend.rs +++ b/crates/notan_app/src/backend.rs @@ -4,6 +4,7 @@ use downcast_rs::{impl_downcast, Downcast}; use futures::prelude::*; use futures::Future; use notan_graphics::DeviceBackend; +use raw_window_handle::RawWindowHandle; #[cfg(feature = "audio")] use notan_audio::AudioBackend; @@ -213,4 +214,6 @@ pub trait WindowBackend { /// Returns if touch as mouse is enabled fn touch_as_mouse(&self) -> bool; + + fn raw_window_handle(&self) -> Option; } diff --git a/crates/notan_app/src/empty.rs b/crates/notan_app/src/empty.rs index ad6859b5..50b503e7 100644 --- a/crates/notan_app/src/empty.rs +++ b/crates/notan_app/src/empty.rs @@ -3,6 +3,7 @@ use crate::{ App, Backend, BackendSystem, CursorIcon, EventIterator, FrameState, InitializeFn, WindowBackend, }; use notan_graphics::prelude::*; +use raw_window_handle::RawWindowHandle; use std::any::Any; #[cfg(feature = "audio")] @@ -137,6 +138,10 @@ impl WindowBackend for EmptyWindowBackend { fn touch_as_mouse(&self) -> bool { self.touch_as_mouse } + + fn raw_window_handle(&self) -> Option { + None + } } #[derive(Default)] diff --git a/crates/notan_winit/src/window.rs b/crates/notan_winit/src/window.rs index e414a0db..71824809 100644 --- a/crates/notan_winit/src/window.rs +++ b/crates/notan_winit/src/window.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use crate::gl_manager::GlManager; use notan_app::WindowConfig; use notan_app::{CursorIcon, WindowBackend}; +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition}; use winit::event_loop::EventLoop; use winit::window::Fullscreen::Borderless; @@ -26,6 +27,11 @@ pub struct WinitWindowBackend { } impl WindowBackend for WinitWindowBackend { + + fn raw_window_handle(&self) -> Option { + Some(self.window().raw_window_handle()) + } + fn capture_cursor(&self) -> bool { self.captured } diff --git a/examples/raw_window_handle.rs b/examples/raw_window_handle.rs new file mode 100644 index 00000000..42ec4367 --- /dev/null +++ b/examples/raw_window_handle.rs @@ -0,0 +1,51 @@ +use notan::draw::*; +use notan::prelude::*; +use raw_window_handle::{AppKitWindowHandle, RawWindowHandle}; + +#[notan_main] +fn main() -> Result<(), String> { + let win = WindowConfig::default() + .set_transparent(true) + .set_decorations(false); + notan::init_with(setup) + .add_config(win) + .add_config(DrawConfig) // Simple way to add the draw extension + .draw(draw) + .build() +} + +fn setup(app: &mut App) { + let raw_window_handle = app.window().raw_window_handle().unwrap(); + + match raw_window_handle { + RawWindowHandle::AppKit(AppKitWindowHandle { ns_view, .. }) => { + #[cfg(target_os = "macos")] + unsafe { + use cocoa::appkit::NSWindow; + use cocoa::base::NO; + use objc::runtime::Object; + use objc::{msg_send, sel, sel_impl}; + + // I told you it was unsafe. + let ns_view: *mut Object = ns_view as *mut Object; + let ns_window: *mut Object = msg_send![ns_view, window]; + + // NOTE: remove shadow to stop artifacts in transparent window + ns_window.setHasShadow_(NO); + ns_window.setLevel_(1001); + println!("{:?}", raw_window_handle) + } + } + _ => { + println!("{:?}", raw_window_handle) + } + } +} + +fn draw(gfx: &mut Graphics) { + let mut draw = gfx.create_draw(); + draw.clear(Color::TRANSPARENT); + draw.triangle((400.0, 100.0), (100.0, 500.0), (700.0, 500.0)) + .color(Color::MAGENTA); + gfx.render(&draw); +}