Repository: Rust-SDL2/rust-sdl2 (language: rust)

Issue:
Support raw-window-handle 0.5
Presently, the latest version of `wgpu` (0.14.0) uses `raw-window-handle` 0.5, so the window handles produced from `sdl2` can't be consumed by `wgpu`


Discussion / hints:

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index a2a28af3ce..e60b273a63 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,12 +31,12 @@ optional = true
 
 [dev-dependencies]
 rand = "0.7"
-wgpu = { version = "0.12", features = ["spirv"] }
+wgpu = { version = "0.14", features = ["spirv"] }
 pollster = "0.2.4"
 env_logger = "0.9.0"
 
 [dependencies.raw-window-handle]
-version = "0.4.2"
+version = "0.5.0"
 optional = true
 
 [features]
diff --git a/examples/raw-window-handle-with-wgpu/main.rs b/examples/raw-window-handle-with-wgpu/main.rs
index d058a5573b..576304e06b 100644
--- a/examples/raw-window-handle-with-wgpu/main.rs
+++ b/examples/raw-window-handle-with-wgpu/main.rs
@@ -4,7 +4,7 @@ extern crate sdl2;
 extern crate wgpu;
 
 use std::borrow::Cow;
-use wgpu::{SurfaceError, SurfaceTexture};
+use wgpu::SurfaceError;
 
 use sdl2::event::{Event, WindowEvent};
 use sdl2::keyboard::Keycode;
@@ -19,6 +19,7 @@ fn main() -> Result<(), String> {
         .window("Raw Window Handle Example", 800, 600)
         .position_centered()
         .resizable()
+        .metal_view()
         .build()
         .map_err(|e| e.to_string())?;
     let (width, height) = window.size();
@@ -47,7 +48,7 @@ fn main() -> Result<(), String> {
         Err(e) => return Err(e.to_string()),
     };
 
-    let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor {
+    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
         label: Some("shader"),
         source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
     });
@@ -75,11 +76,11 @@ fn main() -> Result<(), String> {
             entry_point: "vs_main",
         },
         fragment: Some(wgpu::FragmentState {
-            targets: &[wgpu::ColorTargetState {
+            targets: &[Some(wgpu::ColorTargetState {
                 format: wgpu::TextureFormat::Bgra8UnormSrgb,
                 blend: None,
                 write_mask: wgpu::ColorWrites::ALL,
-            }],
+            })],
             module: &shader,
             entry_point: "fs_main",
         }),
@@ -104,10 +105,11 @@ fn main() -> Result<(), String> {
 
     let mut config = wgpu::SurfaceConfiguration {
         usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
-        format: surface.get_preferred_format(&adapter).unwrap(),
+        format: surface.get_supported_formats(&adapter)[0],
         width,
         height,
-        present_mode: wgpu::PresentMode::Mailbox,
+        present_mode: wgpu::PresentMode::Fifo,
+        alpha_mode: wgpu::CompositeAlphaMode::Auto,
     };
     surface.configure(&device, &config);
 
@@ -137,10 +139,10 @@ fn main() -> Result<(), String> {
             }
         }
 
-        let mut frame = match surface.get_current_texture() {
+        let frame = match surface.get_current_texture() {
             Ok(frame) => frame,
             Err(err) => {
-                let reason = match (err) {
+                let reason = match err {
                     SurfaceError::Timeout => "Timeout",
                     SurfaceError::Outdated => "Outdated",
                     SurfaceError::Lost => "Lost",
@@ -159,14 +161,14 @@ fn main() -> Result<(), String> {
 
         {
             let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
-                color_attachments: &[wgpu::RenderPassColorAttachment {
+                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                     view: &output,
                     resolve_target: None,
                     ops: wgpu::Operations {
                         load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
                         store: true,
                     },
-                }],
+                })],
                 depth_stencil_attachment: None,
                 label: None,
             });
diff --git a/examples/raw-window-handle-with-wgpu/shader.wgsl b/examples/raw-window-handle-with-wgpu/shader.wgsl
index a1ef447d6c..05878d4dd6 100644
--- a/examples/raw-window-handle-with-wgpu/shader.wgsl
+++ b/examples/raw-window-handle-with-wgpu/shader.wgsl
@@ -1,11 +1,11 @@
-[[stage(vertex)]]
-fn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4<f32> {
+@vertex
+fn vs_main(@builtin(vertex_index) in_vertex_index: u32) ->  @builtin(position) vec4<f32> {
     let x = f32(i32(in_vertex_index) - 1);
     let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
     return vec4<f32>(x, y, 0.0, 1.0);
 }
 
-[[stage(fragment)]]
-fn fs_main() -> [[location(0)]] vec4<f32> {
+@fragment
+fn fs_main() -> @location(0) vec4<f32> {
     return vec4<f32>(1.0, 0.0, 0.0, 1.0);
 }
diff --git a/src/sdl2/raw_window_handle.rs b/src/sdl2/raw_window_handle.rs
index 14a3a83209..66babfc344 100644
--- a/src/sdl2/raw_window_handle.rs
+++ b/src/sdl2/raw_window_handle.rs
@@ -1,6 +1,8 @@
 extern crate raw_window_handle;
 
-use self::raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
+use self::raw_window_handle::{
+    HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
+};
 use crate::{sys::SDL_Window, video::Window};
 
 unsafe impl HasRawWindowHandle for Window {
@@ -11,8 +13,8 @@ unsafe impl HasRawWindowHandle for Window {
         // Check if running on web before continuing,
         // since SDL_GetWindowWMInfo will fail on emscripten
         if cfg!(target_os = "emscripten") {
-            use self::raw_window_handle::WebHandle;
-            let mut handle = WebHandle::empty();
+            use self::raw_window_handle::WebWindowHandle;
+            let mut handle = WebWindowHandle::empty();
             handle.id = 1;
             return RawWindowHandle::Web(handle);
         }
@@ -31,18 +33,19 @@ unsafe impl HasRawWindowHandle for Window {
         match wm_info.subsystem {
             #[cfg(target_os = "windows")]
             SDL_SYSWM_WINDOWS => {
-                use self::raw_window_handle::Win32Handle;
+                use self::raw_window_handle::Win32WindowHandle;
 
-                let mut handle = Win32Handle::empty();
+                let mut handle = Win32WindowHandle::empty();
                 handle.hwnd = unsafe { wm_info.info.win }.window as *mut libc::c_void;
+                handle.hinstance = unsafe { wm_info.info.win }.hinstance as *mut libc::c_void;
 
                 RawWindowHandle::Win32(handle)
             }
             #[cfg(target_os = "windows")]
             SDL_SYSWM_WINRT => {
-                use self::raw_window_handle::WinRtHandle;
+                use self::raw_window_handle::WinRtWindowHandle;
 
-                let mut handle = WinRtHandle::empty();
+                let mut handle = WinRtWindowHandle::empty();
                 handle.core_window = unsafe { wm_info.info.winrt }.core_window;
 
                 RawWindowHandle::WinRt(handle)
@@ -55,11 +58,10 @@ unsafe impl HasRawWindowHandle for Window {
                 target_os = "openbsd",
             ))]
             SDL_SYSWM_WAYLAND => {
-                use self::raw_window_handle::WaylandHandle;
+                use self::raw_window_handle::WaylandWindowHandle;
 
-                let mut handle = WaylandHandle::empty();
+                let mut handle = WaylandWindowHandle::empty();
                 handle.surface = unsafe { wm_info.info.wl }.surface as *mut libc::c_void;
-                handle.display = unsafe { wm_info.info.wl }.display as *mut libc::c_void;
 
                 RawWindowHandle::Wayland(handle)
             }
@@ -71,27 +73,30 @@ unsafe impl HasRawWindowHandle for Window {
                 target_os = "openbsd",
             ))]
             SDL_SYSWM_X11 => {
-                use self::raw_window_handle::XlibHandle;
+                use self::raw_window_handle::XlibWindowHandle;
 
-                let mut handle = XlibHandle::empty();
+                let mut handle = XlibWindowHandle::empty();
                 handle.window = unsafe { wm_info.info.x11 }.window;
-                handle.display = unsafe { wm_info.info.x11 }.display as *mut libc::c_void;
 
                 RawWindowHandle::Xlib(handle)
             }
             #[cfg(target_os = "macos")]
             SDL_SYSWM_COCOA => {
-                use self::raw_window_handle::AppKitHandle;
+                use self::raw_window_handle::AppKitWindowHandle;
 
-                let mut handle = AppKitHandle::empty();
+                let mut handle = AppKitWindowHandle::empty();
                 handle.ns_window = unsafe { wm_info.info.cocoa }.window as *mut libc::c_void;
-                handle.ns_view = 0 as *mut libc::c_void; // consumer of RawWindowHandle should determine this
+                handle.ns_view = if self.context().metal_view.is_null() {
+                    panic!("metal_view not initialized, please call WindowBuilder::metal_view() when building the window");
+                } else {
+                    self.context().metal_view
+                };
 
                 RawWindowHandle::AppKit(handle)
             }
             #[cfg(any(target_os = "ios"))]
             SDL_SYSWM_UIKIT => {
-                use self::raw_window_handle::UiKitHandle;
+                use self::raw_window_handle::UiKitWindowHandle;
 
                 let mut handle = UiKitHandle::empty();
                 handle.ui_window = unsafe { wm_info.info.uikit }.window as *mut libc::c_void;
@@ -101,9 +106,9 @@ unsafe impl HasRawWindowHandle for Window {
             }
             #[cfg(any(target_os = "android"))]
             SDL_SYSWM_ANDROID => {
-                use self::raw_window_handle::AndroidNdkHandle;
+                use self::raw_window_handle::AndroidNdkWindowHandle;
 
-                let mut handle = AndroidNdkHandle::empty();
+                let mut handle = AndroidNdkWindowHandle::empty();
                 handle.a_native_window =
                     unsafe { wm_info.info.android }.window as *mut libc::c_void;
 
@@ -122,6 +127,106 @@ unsafe impl HasRawWindowHandle for Window {
     }
 }
 
+unsafe impl HasRawDisplayHandle for Window {
+    #[doc(alias = "SDL_GetVersion")]
+    fn raw_display_handle(&self) -> RawDisplayHandle {
+        use self::SDL_SYSWM_TYPE::*;
+
+        // Check if running on web before continuing,
+        // since SDL_GetWindowWMInfo will fail on emscripten
+        if cfg!(target_os = "emscripten") {
+            use self::raw_window_handle::WebDisplayHandle;
+            let handle = WebDisplayHandle::empty();
+            return RawDisplayHandle::Web(handle);
+        }
+
+        let mut wm_info: SDL_SysWMinfo = unsafe { std::mem::zeroed() };
+
+        // Make certain to retrieve version before querying `SDL_GetWindowWMInfo`
+        // as that gives an error on certain systems
+        unsafe {
+            sys::SDL_GetVersion(&mut wm_info.version);
+            if SDL_GetWindowWMInfo(self.raw(), &mut wm_info) == SDL_bool::SDL_FALSE {
+                panic!("Couldn't get SDL window info: {}", crate::get_error());
+            }
+        }
+
+        match wm_info.subsystem {
+            #[cfg(target_os = "windows")]
+            SDL_SYSWM_WINDOWS | SDL_SYSWM_WINRT => {
+                use self::raw_window_handle::WindowsDisplayHandle;
+
+                let mut handle = WindowsDisplayHandle::empty();
+
+                RawDisplayHandle::Windows(handle)
+            }
+            #[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::WaylandDisplayHandle;
+
+                let mut handle = WaylandDisplayHandle::empty();
+                handle.display = unsafe { wm_info.info.wl }.display as *mut libc::c_void;
+
+                RawDisplayHandle::Wayland(handle)
+            }
+            #[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::XlibDisplayHandle;
+
+                let mut handle = XlibDisplayHandle::empty();
+                handle.display = unsafe { wm_info.info.x11 }.display as *mut libc::c_void;
+
+                RawDisplayHandle::Xlib(handle)
+            }
+            #[cfg(target_os = "macos")]
+            SDL_SYSWM_COCOA => {
+                use self::raw_window_handle::AppKitDisplayHandle;
+                let handle = AppKitDisplayHandle::empty();
+                RawDisplayHandle::AppKit(handle)
+            }
+            #[cfg(any(target_os = "ios"))]
+            SDL_SYSWM_UIKIT => {
+                use self::raw_window_handle::UiKitDisplayHandle;
+
+                let mut handle = UiKitDisplayHandle::empty();
+
+                RawDisplayHandle::UiKit(handle)
+            }
+            #[cfg(any(target_os = "android"))]
+            SDL_SYSWM_ANDROID => {
+                use self::raw_window_handle::AndroidDisplayHandle;
+
+                let mut handle = AndroidDisplayHandle::empty();
+                handle.a_native_window =
+                    unsafe { wm_info.info.android }.window as *mut libc::c_void;
+
+                RawDisplayHandle::Android(handle)
+            }
+            x => {
+                let window_system = match x {
+                    SDL_SYSWM_DIRECTFB => "DirectFB",
+                    SDL_SYSWM_MIR => "Mir",
+                    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);
+            }
+        }
+    }
+}
+
 extern "C" {
     fn SDL_GetWindowWMInfo(window: *mut SDL_Window, info: *mut SDL_SysWMinfo) -> SDL_bool;
 }
diff --git a/src/sdl2/video.rs b/src/sdl2/video.rs
index c2d3abb24e..fdc304fd48 100644
--- a/src/sdl2/video.rs
+++ b/src/sdl2/video.rs
@@ -547,23 +547,34 @@ impl GLContext {
 pub struct WindowContext {
     subsystem: VideoSubsystem,
     raw: *mut sys::SDL_Window,
+    pub(crate) metal_view: sys::SDL_MetalView,
 }
 
 impl Drop for WindowContext {
     #[inline]
     #[doc(alias = "SDL_DestroyWindow")]
     fn drop(&mut self) {
-        unsafe { sys::SDL_DestroyWindow(self.raw) };
+        unsafe {
+            if !self.metal_view.is_null() {
+                sys::SDL_Metal_DestroyView(self.metal_view);
+            }
+            sys::SDL_DestroyWindow(self.raw)
+        };
     }
 }
 
 impl WindowContext {
     #[inline]
     /// Unsafe if the `*mut SDL_Window` is used after the `WindowContext` is dropped
-    pub unsafe fn from_ll(subsystem: VideoSubsystem, raw: *mut sys::SDL_Window) -> WindowContext {
+    pub unsafe fn from_ll(
+        subsystem: VideoSubsystem,
+        raw: *mut sys::SDL_Window,
+        metal_view: sys::SDL_MetalView,
+    ) -> WindowContext {
         WindowContext {
             subsystem: subsystem.clone(),
             raw,
+            metal_view,
         }
     }
 }
@@ -1098,6 +1109,7 @@ pub struct WindowBuilder {
     x: WindowPos,
     y: WindowPos,
     window_flags: u32,
+    create_metal_view: bool,
     /// The window builder cannot be built on a non-main thread, so prevent cross-threaded moves and references.
     /// `!Send` and `!Sync`,
     subsystem: VideoSubsystem,
@@ -1114,6 +1126,7 @@ impl WindowBuilder {
             y: WindowPos::Undefined,
             window_flags: 0,
             subsystem: v.clone(),
+            create_metal_view: false,
         }
     }
 
@@ -1143,11 +1156,16 @@ impl WindowBuilder {
                 raw_height,
                 self.window_flags,
             );
+            let mut metal_view = 0 as sys::SDL_MetalView;
+            #[cfg(target_os = "macos")]
+            if self.create_metal_view {
+                metal_view = sys::SDL_Metal_CreateView(raw);
+            }
 
             if raw.is_null() {
                 Err(SdlError(get_error()))
             } else {
-                Ok(Window::from_ll(self.subsystem.clone(), raw))
+                Ok(Window::from_ll(self.subsystem.clone(), raw, metal_view))
             }
         }
     }
@@ -1243,6 +1261,14 @@ impl WindowBuilder {
         self.window_flags |= sys::SDL_WindowFlags::SDL_WINDOW_ALLOW_HIGHDPI as u32;
         self
     }
+
+    /// Create a SDL_MetalView when constructing the window.
+    /// This is required when using the raw_window_handle feature on MacOS.
+    /// Has no effect no other platforms.
+    pub fn metal_view(&mut self) -> &mut WindowBuilder {
+        self.create_metal_view = true;
+        self
+    }
 }
 
 impl From<Window> for CanvasBuilder {
@@ -1261,8 +1287,12 @@ impl Window {
     }
 
     #[inline]
-    pub unsafe fn from_ll(subsystem: VideoSubsystem, raw: *mut sys::SDL_Window) -> Window {
-        let context = WindowContext::from_ll(subsystem, raw);
+    pub unsafe fn from_ll(
+        subsystem: VideoSubsystem,
+        raw: *mut sys::SDL_Window,
+        metal_view: sys::SDL_MetalView,
+    ) -> Window {
+        let context = WindowContext::from_ll(subsystem, raw, metal_view);
         context.into()
     }
 


Test patch:
diff --git a/tests/raw_window_handle.rs b/tests/raw_window_handle.rs
index 20ec5e7692..2124e9f816 100644
--- a/tests/raw_window_handle.rs
+++ b/tests/raw_window_handle.rs
@@ -3,7 +3,9 @@ mod raw_window_handle_test {
     extern crate raw_window_handle;
     extern crate sdl2;
 
-    use self::raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
+    use self::raw_window_handle::{
+        HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
+    };
     use self::sdl2::video::Window;
 
     #[cfg(target_os = "windows")]
@@ -11,7 +13,7 @@ mod raw_window_handle_test {
     fn get_windows_handle() {
         let window = new_hidden_window();
         match window.raw_window_handle() {
-            RawWindowHandle::Windows(windows_handle) => {
+            RawWindowHandle::Win32(windows_handle) => {
                 assert_ne!(windows_handle.hwnd, 0 as *mut libc::c_void);
                 println!("Successfully received Windows RawWindowHandle!");
             }
@@ -21,6 +23,14 @@ mod raw_window_handle_test {
                 x
             ),
         }
+        match window.raw_display_handle() {
+            RawDisplayHandle::Windows(_) => {}
+            x => assert!(
+                false,
+                "Received wrong RawDisplayHandle type for Windows: {:?}",
+                x
+            ),
+        }
     }
 
     #[cfg(any(
@@ -36,10 +46,6 @@ mod raw_window_handle_test {
         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) => {
@@ -47,15 +53,30 @@ mod raw_window_handle_test {
                     wayland_handle.surface, 0 as *mut libc::c_void,
                     "Surface for Wayland should not be null"
                 );
+                println!("Successfully received linux Wayland RawWindowHandle!");
+            }
+            x => assert!(
+                false,
+                "Received wrong RawWindowHandle type for linux: {:?}",
+                x
+            ),
+        }
+        match window.raw_display_handle() {
+            RawDisplayHandle::Xlib(x11_display) => {
+                assert_ne!(
+                    x11_display.display, 0 as *mut libc::c_void,
+                    "Display for X11 should not be null"
+                );
+            }
+            RawDisplayHandle::Wayland(wayland_display) => {
                 assert_ne!(
-                    wayland_handle.display, 0 as *mut libc::c_void,
+                    wayland_display.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: {:?}",
+                "Received wrong RawDisplayHandle type for linux: {:?}",
                 x
             ),
         }
@@ -66,14 +87,14 @@ mod raw_window_handle_test {
     fn get_macos_handle() {
         let window = new_hidden_window();
         match window.raw_window_handle() {
-            RawWindowHandle::MacOS(macos_handle) => {
+            RawWindowHandle::AppKit(macos_handle) => {
                 assert_ne!(
                     macos_handle.ns_window, 0 as *mut libc::c_void,
                     "ns_window should not be null"
                 );
-                assert_eq!(
+                assert_ne!(
                     macos_handle.ns_view, 0 as *mut libc::c_void,
-                    "nw_view should be null"
+                    "nw_view should not be null"
                 );
                 println!("Successfully received macOS RawWindowHandle!");
             }
@@ -83,6 +104,14 @@ mod raw_window_handle_test {
                 x
             ),
         };
+        match window.raw_display_handle() {
+            RawDisplayHandle::AppKit(_) => {}
+            x => assert!(
+                false,
+                "Received wrong RawDisplayHandle type for macOS: {:?}",
+                x
+            ),
+        }
     }
 
     pub fn new_hidden_window() -> Window {
@@ -91,6 +120,7 @@ mod raw_window_handle_test {
         video_subsystem
             .window("Hello, World!", 800, 600)
             .hidden()
+            .metal_view()
             .build()
             .unwrap()
     }