From 984d7c1fafd9a2903a38193b3099d9ea1c2e544e Mon Sep 17 00:00:00 2001 From: vlad Date: Sat, 11 Jan 2020 14:27:51 -0800 Subject: [PATCH 1/5] Add HasRawWindowHandle trait for Window --- Cargo.toml | 10 +++ src/sdl2/lib.rs | 3 + src/sdl2/raw_window_handle.rs | 112 ++++++++++++++++++++++++++++++++++ src/sdl2/video.rs | 1 - 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/sdl2/raw_window_handle.rs diff --git a/Cargo.toml b/Cargo.toml index cea4cb8cab8..e517ab98d91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,14 @@ optional = true [dev-dependencies] rand = "^0.7" +[dependencies.raw-window-handle] +version = "0.3.3" +optional = true + +[target.'cfg(target_os = "macos")'.dependencies.objc] +version = "0.2.7" +optional = true + [features] unsafe_textures = [] @@ -46,6 +54,8 @@ use_mac_framework = ["sdl2-sys/use_mac_framework"] bundled = ["sdl2-sys/bundled"] static-link= ["sdl2-sys/static-link"] +with-raw-window-handle = ["raw-window-handle", "objc", "use-bindgen"] + [[example]] name = "animation" diff --git a/src/sdl2/lib.rs b/src/sdl2/lib.rs index fb2108ceb38..7210d4b8c3e 100644 --- a/src/sdl2/lib.rs +++ b/src/sdl2/lib.rs @@ -103,3 +103,6 @@ pub mod gfx; mod common; // Export return types and such from the common module. pub use crate::common::IntegerOrSdlError; + +#[cfg(feature = "with-raw-window-handle")] +mod raw_window_handle; diff --git a/src/sdl2/raw_window_handle.rs b/src/sdl2/raw_window_handle.rs new file mode 100644 index 00000000000..2801da2bd97 --- /dev/null +++ b/src/sdl2/raw_window_handle.rs @@ -0,0 +1,112 @@ +#[cfg(target_os = "macos")] +extern crate objc; +#[cfg(feature = "with-raw-window-handle")] +extern crate raw_window_handle; + +use std::alloc::{alloc, dealloc, Layout}; + +use self::raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use crate::{ + sys::{SDL_bool::SDL_FALSE, SDL_GetWindowWMInfo, SDL_SYSWM_TYPE::*, SDL_SysWMinfo}, + video::Window +}; + +struct InfoHandle { + layout: Layout, + pub ptr: *mut SDL_SysWMinfo, +} + +impl InfoHandle { + pub fn new() -> InfoHandle { + let layout = Layout::new::(); + let ptr = unsafe { alloc(layout) as *mut SDL_SysWMinfo }; + InfoHandle { layout, ptr } + } +} + +impl Drop for InfoHandle { + fn drop(&mut self) { + unsafe { + dealloc(self.ptr as *mut u8, self.layout); + } + } +} + +unsafe impl HasRawWindowHandle for Window { + fn raw_window_handle(&self) -> RawWindowHandle { + let info_handle = InfoHandle::new(); + if unsafe { SDL_GetWindowWMInfo(self.raw(), info_handle.ptr) } == SDL_FALSE { + panic!("Couldn't get SDL window info: {}", crate::get_error()); + } + match unsafe { *info_handle.ptr }.subsystem { + #[cfg(target_os = "windows")] + SDL_SYSWM_WINDOWS => { + use self::raw_window_handle::windows::WindowsHandle; + RawWindowHandle::Windows(WindowsHandle { + hwnd: unsafe { (*info_handle.ptr).info.win }.window as *mut libc::c_void, + ..WindowsHandle::empty() + }) + }, + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + SDL_SYSWM_WAYLAND => { + use self::raw_window_handle::unix::WaylandHandle; + RawWindowHandle::Wayland(WaylandHandle { + surface: unsafe { (*info_handle.ptr).info.wl }.surface as *mut libc::c_void, + display: unsafe { (*info_handle.ptr).info.wl }.display as *mut libc::c_void, + ..WaylandHandle::empty() + }) + }, + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + SDL_SYSWM_X11 => { + use self::raw_window_handle::unix::XlibHandle; + RawWindowHandle::Xlib(XlibHandle { + window: unsafe { (*info_handle.ptr).info.x11 }.window as *mut c_void, + display: unsafe { (*info_handle.ptr).info.x11 }.display as *mut c_void, + ..XlibHandle::empty() + }) + }, + #[cfg(target_os = "macos")] + SDL_SYSWM_COCOA => { + use self::raw_window_handle::macos::MacOSHandle; + use self::objc::{msg_send, runtime::Object, sel, sel_impl}; + let ns_window = unsafe { (*info_handle.ptr).info.cocoa }.window as *mut libc::c_void; + let ns_view = unsafe { msg_send![ns_window as *mut Object, contentView] }; + RawWindowHandle::MacOS(MacOSHandle { + ns_window, + ns_view, + ..MacOSHandle::empty() + }) + }, + SDL_SYSWM_ANDROID | SDL_SYSWM_UIKIT => { + let window_system = match unsafe { (*info_handle.ptr).subsystem } { + SDL_SYSWM_ANDROID => "Android", + SDL_SYSWM_UIKIT => "iOS", + _ => unreachable!(), + }; + panic!("raw-window-handle support for {} not yet implemented", window_system); + }, + x => { + let window_system = match x { + SDL_SYSWM_DIRECTFB => "DirectFB", + SDL_SYSWM_MIR => "Mir", + SDL_SYSWM_WINRT => "WinRT", + SDL_SYSWM_VIVANTE => "Vivante", + _ => unreachable!(), + }; + panic!("{} window system is not supported, please file issue with raw-window-handle: https://github.com/rust-windowing/raw-window-handle/issues/new", window_system); + }, + } + } +} diff --git a/src/sdl2/video.rs b/src/sdl2/video.rs index 8a6a6e63ac0..c68fc7bb7ab 100644 --- a/src/sdl2/video.rs +++ b/src/sdl2/video.rs @@ -20,7 +20,6 @@ use crate::sys; pub use crate::sys::{VkInstance, VkSurfaceKHR}; - pub struct WindowSurfaceRef<'a>(&'a mut SurfaceRef, &'a Window); impl<'a> Deref for WindowSurfaceRef<'a> { From 1c387f030c9d31388e8ecad383030960fbbc159b Mon Sep 17 00:00:00 2001 From: Logan McGrath <1755866+lmcgrath@users.noreply.github.com> Date: Sat, 25 Jan 2020 12:15:38 -0800 Subject: [PATCH 2/5] No more bindgen! Using separate bindings for SDL_SysWMinfo --- Cargo.toml | 6 - src/sdl2/lib.rs | 4 +- src/sdl2/raw_window_handle.rs | 298 +++++++++++++++++++++++++++++----- src/sdl2/video.rs | 1 + 4 files changed, 256 insertions(+), 53 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e517ab98d91..56338e80d65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,10 +35,6 @@ rand = "^0.7" version = "0.3.3" optional = true -[target.'cfg(target_os = "macos")'.dependencies.objc] -version = "0.2.7" -optional = true - [features] unsafe_textures = [] @@ -54,8 +50,6 @@ use_mac_framework = ["sdl2-sys/use_mac_framework"] bundled = ["sdl2-sys/bundled"] static-link= ["sdl2-sys/static-link"] -with-raw-window-handle = ["raw-window-handle", "objc", "use-bindgen"] - [[example]] name = "animation" diff --git a/src/sdl2/lib.rs b/src/sdl2/lib.rs index 7210d4b8c3e..26f087ccfb3 100644 --- a/src/sdl2/lib.rs +++ b/src/sdl2/lib.rs @@ -104,5 +104,5 @@ mod common; // Export return types and such from the common module. pub use crate::common::IntegerOrSdlError; -#[cfg(feature = "with-raw-window-handle")] -mod raw_window_handle; +#[cfg(feature = "raw-window-handle")] +pub mod raw_window_handle; diff --git a/src/sdl2/raw_window_handle.rs b/src/sdl2/raw_window_handle.rs index 2801da2bd97..b76af8bc0cd 100644 --- a/src/sdl2/raw_window_handle.rs +++ b/src/sdl2/raw_window_handle.rs @@ -1,49 +1,21 @@ -#[cfg(target_os = "macos")] -extern crate objc; -#[cfg(feature = "with-raw-window-handle")] extern crate raw_window_handle; -use std::alloc::{alloc, dealloc, Layout}; - use self::raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; -use crate::{ - sys::{SDL_bool::SDL_FALSE, SDL_GetWindowWMInfo, SDL_SYSWM_TYPE::*, SDL_SysWMinfo}, - video::Window -}; - -struct InfoHandle { - layout: Layout, - pub ptr: *mut SDL_SysWMinfo, -} - -impl InfoHandle { - pub fn new() -> InfoHandle { - let layout = Layout::new::(); - let ptr = unsafe { alloc(layout) as *mut SDL_SysWMinfo }; - InfoHandle { layout, ptr } - } -} - -impl Drop for InfoHandle { - fn drop(&mut self) { - unsafe { - dealloc(self.ptr as *mut u8, self.layout); - } - } -} +use crate::{sys::SDL_Window, video::Window as VideoWindow}; -unsafe impl HasRawWindowHandle for Window { +unsafe impl HasRawWindowHandle for VideoWindow { fn raw_window_handle(&self) -> RawWindowHandle { - let info_handle = InfoHandle::new(); - if unsafe { SDL_GetWindowWMInfo(self.raw(), info_handle.ptr) } == SDL_FALSE { + use self::SDL_SYSWM_TYPE::*; + let mut wm_info = SDL_SysWMinfo::default(); + if unsafe { SDL_GetWindowWMInfo(self.raw(), &mut wm_info) } == SDL_bool::SDL_FALSE { panic!("Couldn't get SDL window info: {}", crate::get_error()); } - match unsafe { *info_handle.ptr }.subsystem { + match wm_info.subsystem { #[cfg(target_os = "windows")] SDL_SYSWM_WINDOWS => { use self::raw_window_handle::windows::WindowsHandle; RawWindowHandle::Windows(WindowsHandle { - hwnd: unsafe { (*info_handle.ptr).info.win }.window as *mut libc::c_void, + hwnd: unsafe { wm_info.info.win }.window as *mut libc::c_void, ..WindowsHandle::empty() }) }, @@ -57,8 +29,8 @@ unsafe impl HasRawWindowHandle for Window { SDL_SYSWM_WAYLAND => { use self::raw_window_handle::unix::WaylandHandle; RawWindowHandle::Wayland(WaylandHandle { - surface: unsafe { (*info_handle.ptr).info.wl }.surface as *mut libc::c_void, - display: unsafe { (*info_handle.ptr).info.wl }.display as *mut libc::c_void, + surface: unsafe { wm_info.info.wl }.surface as *mut libc::c_void, + display: unsafe { wm_info.info.wl }.display as *mut libc::c_void, ..WaylandHandle::empty() }) }, @@ -72,25 +44,22 @@ unsafe impl HasRawWindowHandle for Window { SDL_SYSWM_X11 => { use self::raw_window_handle::unix::XlibHandle; RawWindowHandle::Xlib(XlibHandle { - window: unsafe { (*info_handle.ptr).info.x11 }.window as *mut c_void, - display: unsafe { (*info_handle.ptr).info.x11 }.display as *mut c_void, + window: unsafe { wm_info.info.x11 }.window, + display: unsafe { wm_info.info.x11 }.display as *mut libc::c_void, ..XlibHandle::empty() }) }, #[cfg(target_os = "macos")] SDL_SYSWM_COCOA => { use self::raw_window_handle::macos::MacOSHandle; - use self::objc::{msg_send, runtime::Object, sel, sel_impl}; - let ns_window = unsafe { (*info_handle.ptr).info.cocoa }.window as *mut libc::c_void; - let ns_view = unsafe { msg_send![ns_window as *mut Object, contentView] }; RawWindowHandle::MacOS(MacOSHandle { - ns_window, - ns_view, + ns_window: unsafe { wm_info.info.cocoa }.window as *mut libc::c_void, + ns_view: 0 as *mut libc::c_void, // consumer of RawWindowHandle should determine this ..MacOSHandle::empty() }) }, SDL_SYSWM_ANDROID | SDL_SYSWM_UIKIT => { - let window_system = match unsafe { (*info_handle.ptr).subsystem } { + let window_system = match wm_info.subsystem { SDL_SYSWM_ANDROID => "Android", SDL_SYSWM_UIKIT => "iOS", _ => unreachable!(), @@ -110,3 +79,242 @@ unsafe impl HasRawWindowHandle for Window { } } } + +extern "C" { + fn SDL_GetWindowWMInfo(window: *mut SDL_Window, info: *mut SDL_SysWMinfo) -> SDL_bool; +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[allow(non_camel_case_types, dead_code)] +pub enum SDL_bool { + SDL_FALSE = 0, + SDL_TRUE = 1, +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +#[allow(non_camel_case_types, dead_code)] +pub struct SDL_version { + pub major: u8, + pub minor: u8, + pub patch: u8, +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[allow(non_camel_case_types, dead_code)] +pub enum SDL_SYSWM_TYPE { + SDL_SYSWM_UNKNOWN = 0, + SDL_SYSWM_WINDOWS = 1, + SDL_SYSWM_X11 = 2, + SDL_SYSWM_DIRECTFB = 3, + SDL_SYSWM_COCOA = 4, + SDL_SYSWM_UIKIT = 5, + SDL_SYSWM_WAYLAND = 6, + SDL_SYSWM_MIR = 7, + SDL_SYSWM_WINRT = 8, + SDL_SYSWM_ANDROID = 9, + SDL_SYSWM_VIVANTE = 10, + SDL_SYSWM_OS2 = 11, +} + +impl Default for SDL_SYSWM_TYPE { + fn default() -> Self { + SDL_SYSWM_TYPE::SDL_SYSWM_UNKNOWN + } +} + +#[repr(C)] +#[derive(Default, Copy, Clone)] +#[allow(non_camel_case_types)] +pub struct SDL_SysWMinfo { + pub version: SDL_version, + pub subsystem: SDL_SYSWM_TYPE, + + #[cfg(target_os = "windows")] + pub info: windows::WindowsSysWMinfo, + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + pub info: linux::LinuxSysWMinfo, + + #[cfg(target_os = "macos")] + pub info: macos::MacOSSysWMinfo, +} + +#[cfg(target_os = "windows")] +pub mod windows { + #[repr(C)] + #[derive(Copy, Clone)] + pub union WindowsSysWMinfo { + pub win: Handles, + pub dummy: [u8; 64usize], + _bindgen_union_align: [u64; 8usize], + } + + impl Default for WindowsSysWMinfo { + fn default() -> Self { + WindowsSysWMinfo { + win: Handles::default(), + } + } + } + + #[repr(C)] + #[derive(Debug, Copy, Clone, PartialEq)] + pub struct Handles { + pub window: *mut HWND, + pub hdc: *mut HDC, + pub hinstance: *mut HINSTANCE, + } + + impl Default for Handles { + fn default() -> Self { + Handles { + window: 0 as *mut HWND, + hdc: 0 as *mut HDC, + hinstance: 0 as *mut HINSTANCE, + } + } + } + + #[repr(C)] + #[derive(Debug, Default, Copy, Clone, PartialEq)] + pub struct HWND { + pub unused: libc::c_int, + } + + #[repr(C)] + #[derive(Debug, Default, Copy, Clone, PartialEq)] + pub struct HDC { + pub unused: libc::c_int, + } + + #[repr(C)] + #[derive(Debug, Default, Copy, Clone, PartialEq)] + pub struct HINSTANCE { + pub unused: libc::c_int, + } +} + +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", +))] +pub mod linux { + #[repr(C)] + #[derive(Copy, Clone)] + pub union LinuxSysWMinfo { + pub x11: X11Info, + pub wl: WaylandInfo, + pub dummy: [u8; 64usize], + _bindgen_union_align: [u32; 16usize], + } + + impl Default for LinuxSysWMinfo { + fn default() -> Self { + LinuxSysWMinfo { + wl: WaylandInfo::default(), + } + } + } + + #[repr(C)] + #[derive(Debug, Copy, Clone, PartialEq)] + pub struct X11Info { + pub display: *mut Display, + pub window: Window, + } + + #[repr(C)] + #[derive(Debug, Copy, Clone, PartialEq)] + pub struct WaylandInfo { + pub display: *mut WLDisplay, + pub surface: *mut WLSurface, + pub shell_surface: *mut WLShellSurface, + } + + impl Default for WaylandInfo { + fn default() -> Self { + WaylandInfo { + display: 0 as *mut WLDisplay, + surface: 0 as *mut WLSurface, + shell_surface: 0 as *mut WLShellSurface, + } + } + } + + #[repr(C)] + #[derive(Debug, Default, Copy, Clone, PartialEq)] + pub struct WLDisplay { + pub _address: u8, + } + + #[repr(C)] + #[derive(Debug, Default, Copy, Clone, PartialEq)] + pub struct WLSurface { + pub _address: u8, + } + + #[repr(C)] + #[derive(Debug, Default, Copy, Clone, PartialEq)] + pub struct WLShellSurface { + pub _address: u8, + } + + #[repr(C)] + #[derive(Debug, Copy, Clone)] + pub struct Display { + _unused: [u8; 0], + } + + pub type Window = libc::c_ulong; +} + +#[cfg(target_os = "macos")] +pub mod macos { + #[repr(C)] + #[derive(Copy, Clone)] + pub union MacOSSysWMinfo { + pub cocoa: CocoaInfo, + pub dummy: [u8; 64usize], + _bindgen_union_align: [u64; 8usize], + } + + impl Default for MacOSSysWMinfo { + fn default() -> Self { + MacOSSysWMinfo { + cocoa: CocoaInfo::default(), + } + } + } + + #[repr(C)] + #[derive(Debug, Copy, Clone, PartialEq)] + pub struct CocoaInfo { + pub window: *mut NSWindow, + } + + impl Default for CocoaInfo { + fn default() -> Self { + CocoaInfo { + window: 0 as *mut NSWindow, + } + } + } + + #[repr(C)] + #[derive(Debug, Copy, Clone)] + pub struct NSWindow { + _unused: [u8; 0], + } +} diff --git a/src/sdl2/video.rs b/src/sdl2/video.rs index c68fc7bb7ab..8a6a6e63ac0 100644 --- a/src/sdl2/video.rs +++ b/src/sdl2/video.rs @@ -20,6 +20,7 @@ use crate::sys; pub use crate::sys::{VkInstance, VkSurfaceKHR}; + pub struct WindowSurfaceRef<'a>(&'a mut SurfaceRef, &'a Window); impl<'a> Deref for WindowSurfaceRef<'a> { From b23c5cb9150b1a6eac8f3fcad73fbd0cf18b9198 Mon Sep 17 00:00:00 2001 From: Logan McGrath <1755866+lmcgrath@users.noreply.github.com> Date: Tue, 28 Jan 2020 21:38:53 -0800 Subject: [PATCH 3/5] tests and docs --- README.md | 16 ++++++++ changelog.md | 3 ++ src/sdl2/raw_window_handle.rs | 4 +- tests/raw_window_handle.rs | 70 +++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 tests/raw_window_handle.rs diff --git a/README.md b/README.md index 8cffaf4aaab..88d355a0394 100644 --- a/README.md +++ b/README.md @@ -543,6 +543,22 @@ fn main() { ``` +# Support for raw-window-handle + +`raw-window-handle` can be enabled using the feature name: + +```toml +[dependencies.sdl2] +version = "0.32" +features = ["raw-window-handle"] +``` + +### sdl2 with raw-window-handle on macOS: + +On macOS the `RawWindowHandle.ns_view` field is returned null. Libraries consuming the `RawWindowHandle` (such as +`wgpu-rs`) should determine a sane default for `ns_view`. If they do not, please file an issue with the associated +project. + # When things go wrong Rust, and Rust-SDL2, are both still heavily in development, and you may run into teething issues when using this. Before panicking, check that you're using diff --git a/changelog.md b/changelog.md index bd54b134d45..44c3d97e6b3 100644 --- a/changelog.md +++ b/changelog.md @@ -27,6 +27,9 @@ Ignore unknown bits in `SDL_Keysym`'s `mod` field (key modifiers) when construct [PR #898](https://github.com/Rust-SDL2/rust-sdl2/pull/898): Implements `TryFrom` for `PixelFormat` +[PR #962](https://github.com/Rust-SDL2/rust-sdl2/pull/962): +Added `raw-window-handle` support for Windows, Linux (X11 and Wayland) and macOS. + ### v0.32.2 [PR #868](https://github.com/Rust-SDL2/rust-sdl2/pull/868): diff --git a/src/sdl2/raw_window_handle.rs b/src/sdl2/raw_window_handle.rs index b76af8bc0cd..6dccae7b9e4 100644 --- a/src/sdl2/raw_window_handle.rs +++ b/src/sdl2/raw_window_handle.rs @@ -1,9 +1,9 @@ extern crate raw_window_handle; use self::raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; -use crate::{sys::SDL_Window, video::Window as VideoWindow}; +use crate::{sys::SDL_Window, video::Window}; -unsafe impl HasRawWindowHandle for VideoWindow { +unsafe impl HasRawWindowHandle for Window { fn raw_window_handle(&self) -> RawWindowHandle { use self::SDL_SYSWM_TYPE::*; let mut wm_info = SDL_SysWMinfo::default(); diff --git a/tests/raw_window_handle.rs b/tests/raw_window_handle.rs new file mode 100644 index 00000000000..d75ef7206b7 --- /dev/null +++ b/tests/raw_window_handle.rs @@ -0,0 +1,70 @@ +#[cfg(feature = "raw-window-handle")] +mod raw_window_handle_test { + extern crate raw_window_handle; + extern crate sdl2; + + use self::raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; + use self::sdl2::video::Window; + + #[cfg(target_os = "windows")] + #[test] + fn get_windows_handle() { + let window = new_hidden_window(); + match window.raw_window_handle() { + RawWindowHandle::Windows(windows_handle) => { + assert_ne!(windows_handle.hwnd, 0 as *mut libc::c_void); + println!("Successfully received Windows RawWindowHandle!"); + }, + x => assert!(false, "Received wrong RawWindowHandle type for Windows: {:?}", x), + } + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[test] + fn get_linux_handle() { + let window = new_hidden_window(); + match window.raw_window_handle() { + RawWindowHandle::Xlib(x11_handle) => { + assert_ne!(x11_handle.window, 0, "Window for X11 should not be 0"); + assert_ne!(x11_handle.display, 0 as *mut libc::c_void, "Display for X11 should not be null"); + println!("Successfully received linux X11 RawWindowHandle!"); + }, + RawWindowHandle::Wayland(wayland_handle) => { + assert_ne!(wayland_handle.surface, 0 as *mut libc::c_void, "Surface for Wayland should not be null"); + assert_ne!(wayland_handle.display, 0 as *mut libc::c_void, "Display for Wayland should not be null"); + println!("Successfully received linux Wayland RawWindowHandle!"); + }, + x => assert!(false, "Received wrong RawWindowHandle type for linux: {:?}", x), + } + } + + #[cfg(target_os = "macos")] + #[test] + fn get_macos_handle() { + let window = new_hidden_window(); + match window.raw_window_handle() { + RawWindowHandle::MacOS(macos_handle) => { + assert_ne!(macos_handle.ns_window, 0 as *mut libc::c_void, "ns_window should not be null"); + assert_eq!(macos_handle.ns_view, 0 as *mut libc::c_void, "nw_view should be null"); + println!("Successfully received macOS RawWindowHandle!"); + }, + x => assert!(false, "Received wrong RawWindowHandle type for macOS: {:?}", x), + }; + } + + pub fn new_hidden_window() -> Window { + let context = sdl2::init().unwrap(); + let video_subsystem = context.video().unwrap(); + video_subsystem + .window("Hello, World!", 800, 600) + .hidden() + .build() + .unwrap() + } +} From 75b1a16e1e0f37b2465acdd38b86a837c9e49704 Mon Sep 17 00:00:00 2001 From: Logan McGrath <1755866+lmcgrath@users.noreply.github.com> Date: Mon, 27 Jan 2020 20:49:55 -0800 Subject: [PATCH 4/5] adding an example --- Cargo.toml | 9 + README.md | 8 +- examples/raw-window-handle-with-wgpu/main.rs | 191 ++++++++++++++++++ .../raw-window-handle-with-wgpu/shader.frag | 7 + .../raw-window-handle-with-wgpu/shader.vert | 15 ++ 5 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 examples/raw-window-handle-with-wgpu/main.rs create mode 100644 examples/raw-window-handle-with-wgpu/shader.frag create mode 100644 examples/raw-window-handle-with-wgpu/shader.vert diff --git a/Cargo.toml b/Cargo.toml index 56338e80d65..3d5523c5b39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,11 @@ optional = true [dev-dependencies] rand = "^0.7" +wgpu = "^0.4.0" +glsl-to-spirv = "^0.1.7" + +[target.'cfg(target_os = "macos")'.dev-dependencies.objc] +version = "^0.2.7" [dependencies.raw-window-handle] version = "0.3.3" @@ -140,3 +145,7 @@ name = "ttf-demo" [[example]] name = "window-properties" + +[[example]] +required-features = ["raw-window-handle"] +name = "raw-window-handle-with-wgpu" diff --git a/README.md b/README.md index 88d355a0394..827758215e1 100644 --- a/README.md +++ b/README.md @@ -553,10 +553,16 @@ version = "0.32" features = ["raw-window-handle"] ``` +An example working with [`wgpu`](https://crates.io/crates/wgpu) is also available: + +```bash +cargo run --example raw-window-handle-with-wgpu --features raw-window-handle +``` + ### sdl2 with raw-window-handle on macOS: On macOS the `RawWindowHandle.ns_view` field is returned null. Libraries consuming the `RawWindowHandle` (such as -`wgpu-rs`) should determine a sane default for `ns_view`. If they do not, please file an issue with the associated +`wgpu`) should determine a sane default for `ns_view`. If they do not, please file an issue with the associated project. # When things go wrong diff --git a/examples/raw-window-handle-with-wgpu/main.rs b/examples/raw-window-handle-with-wgpu/main.rs new file mode 100644 index 00000000000..716bfb01a82 --- /dev/null +++ b/examples/raw-window-handle-with-wgpu/main.rs @@ -0,0 +1,191 @@ +/// Minimal example for getting sdl2 and wgpu working together with raw-window-handle. +/// +/// TODO: update wgpu and this example after https://github.com/gfx-rs/wgpu/pull/462 ships + +extern crate glsl_to_spirv; +extern crate libc; +#[cfg(target_os = "macos")] +extern crate objc; +extern crate raw_window_handle; +extern crate sdl2; +extern crate wgpu; + +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::macos::MacOSHandle; + +use sdl2::event::{Event, WindowEvent}; +use sdl2::keyboard::Keycode; +use sdl2::video::Window; + +/// sdl2 implements raw-window-handle correctly, but wgpu does not for macOS +/// wgpu wrongly expects an NSView to be provided by raw-window-handle, so we have to do a little more work +struct WindowWrapper<'a>(pub &'a Window); + +unsafe impl<'a> HasRawWindowHandle for WindowWrapper<'a> { + #[cfg(not(target_os = "macos"))] + /// all non-mac platforms work correctly, so return the handle directly + fn raw_window_handle(&self) -> RawWindowHandle { + self.0.raw_window_handle() + } + + #[cfg(target_os = "macos")] + /// do some work on macOS to get the root NSView for the NSWindow returned by sdl2 + fn raw_window_handle(&self) -> RawWindowHandle { + use objc::{msg_send, sel, sel_impl}; + use objc::runtime::Object; + let handle = self.0.raw_window_handle(); + match handle { + RawWindowHandle::MacOS(macos_handle) => { + RawWindowHandle::MacOS(MacOSHandle { + ns_window: macos_handle.ns_window, + ns_view: unsafe { msg_send![macos_handle.ns_window as *mut Object, contentView] }, + ..MacOSHandle::empty() + }) + } + _ => unreachable!() + } + } +} + +fn load_glsl(code: &str, ty: glsl_to_spirv::ShaderType) -> Result, String> { + let spirv = glsl_to_spirv::compile(&code, ty)?; + let result = wgpu::read_spirv(spirv).map_err(|e| e.to_string())?; + Ok(result) +} + +fn main() -> Result<(), String> { + let sdl_context = sdl2::init()?; + let video_subsystem = sdl_context.video()?; + let window = video_subsystem + .window("Raw Window Handle Example", 800, 600) + .position_centered() + .resizable() + .build() + .map_err(|e| e.to_string())?; + let (width, height) = window.size(); + + let adapter_opt = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::Default, + backends: wgpu::BackendBit::PRIMARY, + }); + let adapter = match adapter_opt { + Some(a) => a, + None => return Err(String::from("No adapter found")), + }; + + let (device, mut queue) = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: wgpu::Limits::default(), + }); + + let vs = include_str!("shader.vert"); + let vs_spirv = &load_glsl(vs, glsl_to_spirv::ShaderType::Vertex)?; + let vs_module = device.create_shader_module(vs_spirv); + + let fs = include_str!("shader.frag"); + let fs_spirv = &load_glsl(fs, glsl_to_spirv::ShaderType::Fragment)?; + let fs_module = device.create_shader_module(fs_spirv); + + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[] }); + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + bindings: &[], + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&bind_group_layout], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::None, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[wgpu::ColorStateDescriptor { + format: wgpu::TextureFormat::Bgra8UnormSrgb, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + // use the WindowWrapper to make sure wgpu will work on macOS + let surface = wgpu::Surface::create(&WindowWrapper(&window)); + + let mut sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width, + height, + present_mode: wgpu::PresentMode::Vsync, + }; + + let mut swap_chain = device.create_swap_chain(&surface, &sc_desc); + + let mut event_pump = sdl_context.event_pump()?; + 'running: loop { + for event in event_pump.poll_iter() { + match event { + Event::Window { + win_event: WindowEvent::Resized(width, height), + .. + } => { + sc_desc.width = width as u32; + sc_desc.height = height as u32; + swap_chain = device.create_swap_chain(&surface, &sc_desc); + } + Event::Quit { .. } + | Event::KeyDown { + keycode: Some(Keycode::Escape), + .. + } => { + break 'running; + } + _ => {} + } + } + + let frame = swap_chain.get_next_texture(); + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color::GREEN, + }], + depth_stencil_attachment: None, + }); + rpass.set_pipeline(&render_pipeline); + rpass.set_bind_group(0, &bind_group, &[]); + rpass.draw(0 .. 3, 0 .. 1); + } + + queue.submit(&[encoder.finish()]); + } + + Ok(()) +} \ No newline at end of file diff --git a/examples/raw-window-handle-with-wgpu/shader.frag b/examples/raw-window-handle-with-wgpu/shader.frag new file mode 100644 index 00000000000..74e14f410e3 --- /dev/null +++ b/examples/raw-window-handle-with-wgpu/shader.frag @@ -0,0 +1,7 @@ +#version 450 + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/examples/raw-window-handle-with-wgpu/shader.vert b/examples/raw-window-handle-with-wgpu/shader.vert new file mode 100644 index 00000000000..ac6dcc7c324 --- /dev/null +++ b/examples/raw-window-handle-with-wgpu/shader.vert @@ -0,0 +1,15 @@ +#version 450 + +out gl_PerVertex { + vec4 gl_Position; +}; + +const vec2 positions[3] = vec2[3]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); +} From aebb3596d9d9053af4dd2ff832762da6db62109a Mon Sep 17 00:00:00 2001 From: Logan McGrath <1755866+lmcgrath@users.noreply.github.com> Date: Fri, 31 Jan 2020 13:15:56 -0800 Subject: [PATCH 5/5] Never trust IDE auto-imports --- examples/raw-window-handle-with-wgpu/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/raw-window-handle-with-wgpu/main.rs b/examples/raw-window-handle-with-wgpu/main.rs index 716bfb01a82..73ca39fc493 100644 --- a/examples/raw-window-handle-with-wgpu/main.rs +++ b/examples/raw-window-handle-with-wgpu/main.rs @@ -11,7 +11,6 @@ extern crate sdl2; extern crate wgpu; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; -use raw_window_handle::macos::MacOSHandle; use sdl2::event::{Event, WindowEvent}; use sdl2::keyboard::Keycode; @@ -33,6 +32,7 @@ unsafe impl<'a> HasRawWindowHandle for WindowWrapper<'a> { fn raw_window_handle(&self) -> RawWindowHandle { use objc::{msg_send, sel, sel_impl}; use objc::runtime::Object; + use raw_window_handle::macos::MacOSHandle; let handle = self.0.raw_window_handle(); match handle { RawWindowHandle::MacOS(macos_handle) => { @@ -188,4 +188,4 @@ fn main() -> Result<(), String> { } Ok(()) -} \ No newline at end of file +}