Repository: tauri-apps/wry (language: rust)

Issue:
No transparency for webview in WGPU example on Windows
**Describe the bug**
I'm running the WGPU example on Windows and the sub view which displays the user agent has a 0.5 alpha but it has a grey background is not blending the triangle rendered by WGPU underneath it. 

**Steps To Reproduce**
Run `cargo run --example wgpu`.

**Expected behavior**
I expected the sub webview to blend over the triangle.

**Screenshots**
![Screenshot 2024-08-05 172504](https://github.com/user-attachments/assets/46740518-d330-433b-b12c-27bd888de92c)

**Platform and Versions (please complete the following information):**
OS: Windows 11
Rustc: 1.79.0 (129f3b996 2024-06-10)

**Additional context**
Came to this after looking at #677 and wanted to try it out, I don't think it's related to #692.
I tried the following commits also from around the time #677 when was completed, but I didn't get any webview rendered, just the red triangle.
- f62aa942121f80e1908bc736ae03dc584330bb91
- a9ad1c5c6a85001aac8de64ebb0395e8d647598a



Discussion / hints:
This is because winit set `WS_CLIPCHILDREN` by default, the current example uses winit@0.29 which doesn't have a way to disable that but wini@0.30 does, see https://docs.rs/winit/latest/x86_64-pc-windows-msvc/winit/platform/windows/trait.WindowAttributesExtWindows.html#tymethod.with_clip_children 

I will leave this open for a while until I have time to update the examples

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 09cb9afd2..b88917eae 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -181,8 +181,8 @@ libc = "0.2"
 [dev-dependencies]
 pollster = "0.3.0"
 tao = "0.31"
-wgpu = "0.19"
-winit = "0.29"
+wgpu = "23"
+winit = "0.30"
 getrandom = "0.2"
 http-range = "0.1"
 percent-encoding = "2.3"
diff --git a/examples/multiwebview.rs b/examples/multiwebview.rs
index f7ed2aae9..6540c1541 100644
--- a/examples/multiwebview.rs
+++ b/examples/multiwebview.rs
@@ -1,117 +1,112 @@
-// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
 use winit::{
-  event::{Event, WindowEvent},
-  event_loop::{ControlFlow, EventLoop},
-  window::WindowBuilder,
+  application::ApplicationHandler,
+  event::WindowEvent,
+  event_loop::{ActiveEventLoop, EventLoop},
+  window::{Window, WindowId},
 };
 use wry::{
   dpi::{LogicalPosition, LogicalSize},
   Rect, WebViewBuilder,
 };
 
-fn main() -> wry::Result<()> {
-  #[cfg(any(
-    target_os = "linux",
-    target_os = "dragonfly",
-    target_os = "freebsd",
-    target_os = "netbsd",
-    target_os = "openbsd",
-  ))]
-  {
-    use gtk::prelude::DisplayExtManual;
+#[derive(Default)]
+struct State {
+  window: Option<Window>,
+  webview1: Option<wry::WebView>,
+  webview2: Option<wry::WebView>,
+  webview3: Option<wry::WebView>,
+  webview4: Option<wry::WebView>,
+}
 
-    gtk::init()?;
-    if gtk::gdk::Display::default().unwrap().backend().is_wayland() {
-      panic!("This example doesn't support wayland!");
-    }
+impl ApplicationHandler for State {
+  fn resumed(&mut self, event_loop: &ActiveEventLoop) {
+    let mut attributes = Window::default_attributes();
+    attributes.inner_size = Some(LogicalSize::new(800, 800).into());
+    let window = event_loop.create_window(attributes).unwrap();
 
-    // we need to ignore this error here otherwise it will be catched by winit and will be
-    // make the example crash
-    winit::platform::x11::register_xlib_error_hook(Box::new(|_display, error| {
-      let error = error as *mut x11_dl::xlib::XErrorEvent;
-      (unsafe { (*error).error_code }) == 170
-    }));
-  }
+    let size = window.inner_size().to_logical::<u32>(window.scale_factor());
 
-  let event_loop = EventLoop::new().unwrap();
-  let window = WindowBuilder::new()
-    .with_inner_size(winit::dpi::LogicalSize::new(800, 800))
-    .build(&event_loop)
-    .unwrap();
-
-  let size = window.inner_size().to_logical::<u32>(window.scale_factor());
-
-  let webview = WebViewBuilder::new()
-    .with_bounds(Rect {
-      position: LogicalPosition::new(0, 0).into(),
-      size: LogicalSize::new(size.width / 2, size.height / 2).into(),
-    })
-    .with_url("https://tauri.app")
-    .build(&window)?;
-  let webview2 = WebViewBuilder::new()
-    .with_bounds(Rect {
-      position: LogicalPosition::new(size.width / 2, 0).into(),
-      size: LogicalSize::new(size.width / 2, size.height / 2).into(),
-    })
-    .with_url("https://github.com/tauri-apps/wry")
-    .build(&window)?;
-  let webview3 = WebViewBuilder::new()
-    .with_bounds(Rect {
-      position: LogicalPosition::new(0, size.height / 2).into(),
-      size: LogicalSize::new(size.width / 2, size.height / 2).into(),
-    })
-    .with_url("https://twitter.com/TauriApps")
-    .build(&window)?;
-  let webview4 = WebViewBuilder::new()
-    .with_bounds(Rect {
-      position: LogicalPosition::new(size.width / 2, size.height / 2).into(),
-      size: LogicalSize::new(size.width / 2, size.height / 2).into(),
-    })
-    .with_url("https://google.com")
-    .build(&window)?;
-
-  event_loop
-    .run(move |event, evl| {
-      evl.set_control_flow(ControlFlow::Poll);
-
-      #[cfg(any(
-        target_os = "linux",
-        target_os = "dragonfly",
-        target_os = "freebsd",
-        target_os = "netbsd",
-        target_os = "openbsd",
-      ))]
-      while gtk::events_pending() {
-        gtk::main_iteration_do(false);
-      }
+    let webview1 = WebViewBuilder::new()
+      .with_bounds(Rect {
+        position: LogicalPosition::new(0, 0).into(),
+        size: LogicalSize::new(size.width / 2, size.height / 2).into(),
+      })
+      .with_url("https://tauri.app")
+      .build_as_child(&window)
+      .unwrap();
+
+    let webview2 = WebViewBuilder::new()
+      .with_bounds(Rect {
+        position: LogicalPosition::new(size.width / 2, 0).into(),
+        size: LogicalSize::new(size.width / 2, size.height / 2).into(),
+      })
+      .with_url("https://github.com/tauri-apps/wry")
+      .build_as_child(&window)
+      .unwrap();
 
-      match event {
-        Event::WindowEvent {
-          event: WindowEvent::Resized(size),
-          ..
-        } => {
+    let webview3 = WebViewBuilder::new()
+      .with_bounds(Rect {
+        position: LogicalPosition::new(0, size.height / 2).into(),
+        size: LogicalSize::new(size.width / 2, size.height / 2).into(),
+      })
+      .with_url("https://twitter.com/TauriApps")
+      .build_as_child(&window)
+      .unwrap();
+
+    let webview4 = WebViewBuilder::new()
+      .with_bounds(Rect {
+        position: LogicalPosition::new(size.width / 2, size.height / 2).into(),
+        size: LogicalSize::new(size.width / 2, size.height / 2).into(),
+      })
+      .with_url("https://google.com")
+      .build_as_child(&window)
+      .unwrap();
+
+    self.window = Some(window);
+    self.webview1 = Some(webview1);
+    self.webview2 = Some(webview2);
+    self.webview3 = Some(webview3);
+    self.webview4 = Some(webview4);
+  }
+
+  fn window_event(
+    &mut self,
+    _event_loop: &ActiveEventLoop,
+    _window_id: WindowId,
+    event: WindowEvent,
+  ) {
+    match event {
+      WindowEvent::Resized(size) => {
+        if let (Some(window), Some(webview1), Some(webview2), Some(webview3), Some(webview4)) = (
+          &self.window,
+          &self.webview1,
+          &self.webview2,
+          &self.webview3,
+          &self.webview4,
+        ) {
           let size = size.to_logical::<u32>(window.scale_factor());
-          webview
+
+          webview1
             .set_bounds(Rect {
               position: LogicalPosition::new(0, 0).into(),
               size: LogicalSize::new(size.width / 2, size.height / 2).into(),
             })
             .unwrap();
+
           webview2
             .set_bounds(Rect {
               position: LogicalPosition::new(size.width / 2, 0).into(),
               size: LogicalSize::new(size.width / 2, size.height / 2).into(),
             })
             .unwrap();
+
           webview3
             .set_bounds(Rect {
               position: LogicalPosition::new(0, size.height / 2).into(),
               size: LogicalSize::new(size.width / 2, size.height / 2).into(),
             })
             .unwrap();
+
           webview4
             .set_bounds(Rect {
               position: LogicalPosition::new(size.width / 2, size.height / 2).into(),
@@ -119,14 +114,55 @@ fn main() -> wry::Result<()> {
             })
             .unwrap();
         }
-        Event::WindowEvent {
-          event: WindowEvent::CloseRequested,
-          ..
-        } => evl.exit(),
-        _ => {}
       }
-    })
-    .unwrap();
+      WindowEvent::CloseRequested => {
+        std::process::exit(0);
+      }
+      _ => {}
+    }
+  }
+
+  fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
+    #[cfg(any(
+      target_os = "linux",
+      target_os = "dragonfly",
+      target_os = "freebsd",
+      target_os = "netbsd",
+      target_os = "openbsd",
+    ))]
+    {
+      while gtk::events_pending() {
+        gtk::main_iteration_do(false);
+      }
+    }
+  }
+}
+
+fn main() -> wry::Result<()> {
+  #[cfg(any(
+    target_os = "linux",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "netbsd",
+    target_os = "openbsd",
+  ))]
+  {
+    use gtk::prelude::DisplayExtManual;
+
+    gtk::init()?;
+    if gtk::gdk::Display::default().unwrap().backend().is_wayland() {
+      panic!("This example doesn't support wayland!");
+    }
+
+    winit::platform::x11::register_xlib_error_hook(Box::new(|_display, error| {
+      let error = error as *mut x11_dl::xlib::XErrorEvent;
+      (unsafe { (*error).error_code }) == 170
+    }));
+  }
+
+  let event_loop = EventLoop::new().unwrap();
+  let mut state = State::default();
+  event_loop.run_app(&mut state).unwrap();
 
   Ok(())
 }
diff --git a/examples/wgpu.rs b/examples/wgpu.rs
index 5fe3f9aa6..cc261f024 100644
--- a/examples/wgpu.rs
+++ b/examples/wgpu.rs
@@ -1,194 +1,249 @@
 use std::borrow::Cow;
+use std::sync::Arc;
 use winit::{
-  event::{Event, WindowEvent},
-  event_loop::{ControlFlow, EventLoop},
-  window::Window,
+  application::ApplicationHandler,
+  event::WindowEvent,
+  event_loop::{ActiveEventLoop, EventLoop},
+  window::{Window, WindowId},
 };
 use wry::{
   dpi::{LogicalPosition, LogicalSize},
   Rect, WebViewBuilder,
 };
 
-async fn run(event_loop: EventLoop<()>, window: Window) {
-  let size = window.inner_size();
+#[derive(Default)]
+struct State {
+  window: Option<Arc<Window>>,
+  webview: Option<wry::WebView>,
+  gfx_state: Option<GfxState>,
+}
+
+struct GfxState {
+  surface: wgpu::Surface<'static>,
+  device: wgpu::Device,
+  queue: wgpu::Queue,
+  config: wgpu::SurfaceConfiguration,
+  render_pipeline: wgpu::RenderPipeline,
+}
 
-  let instance = wgpu::Instance::default();
+impl GfxState {
+  fn new(window: Arc<Window>) -> Self {
+    let instance = wgpu::Instance::default();
+    let window_size = window.inner_size();
+    let surface = instance.create_surface(window).unwrap();
 
-  let surface = instance.create_surface(&window).unwrap();
-  let adapter = instance
-    .request_adapter(&wgpu::RequestAdapterOptions {
+    let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
       power_preference: wgpu::PowerPreference::default(),
       force_fallback_adapter: false,
-      // Request an adapter which can render to our surface
       compatible_surface: Some(&surface),
-    })
-    .await
+    }))
     .expect("Failed to find an appropriate adapter");
 
-  // Create the logical device and command queue
-  let (device, queue) = adapter
-    .request_device(
+    let (device, queue) = pollster::block_on(adapter.request_device(
       &wgpu::DeviceDescriptor {
         label: None,
         required_features: wgpu::Features::empty(),
-        // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
-        required_limits: wgpu::Limits::downlevel_webgl2_defaults()
-          .using_resolution(adapter.limits()),
+        required_limits:
+          wgpu::Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()),
+        memory_hints: wgpu::MemoryHints::Performance,
       },
       None,
-    )
-    .await
+    ))
     .expect("Failed to create device");
 
-  // Load the shaders from disk
-  let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
-    label: None,
-    source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(
-      r#"
-@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);
-}
+    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
+            label: None,
+            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(
+                r#"
+                @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);
+                }
+
+                @fragment
+                fn fs_main() -> @location(0) vec4<f32> {
+                    return vec4<f32>(1.0, 0.0, 0.0, 1.0);
+                }
+                "#,
+            )),
+        });
+
+    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+      label: None,
+      bind_group_layouts: &[],
+      push_constant_ranges: &[],
+    });
+
+    let swapchain_capabilities = surface.get_capabilities(&adapter);
+    let swapchain_format = swapchain_capabilities.formats[0];
+
+    let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
+      label: None,
+      layout: Some(&pipeline_layout),
+      vertex: wgpu::VertexState {
+        module: &shader,
+        entry_point: Some("vs_main"),
+        buffers: &[],
+        compilation_options: Default::default(),
+      },
+      fragment: Some(wgpu::FragmentState {
+        module: &shader,
+        entry_point: Some("fs_main"),
+        targets: &[Some(swapchain_format.into())],
+        compilation_options: Default::default(),
+      }),
+      primitive: wgpu::PrimitiveState::default(),
+      depth_stencil: None,
+      multisample: wgpu::MultisampleState::default(),
+      multiview: None,
+      cache: None,
+    });
+
+    let config = wgpu::SurfaceConfiguration {
+      usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+      format: swapchain_format,
+      width: window_size.width,
+      height: window_size.height,
+      present_mode: wgpu::PresentMode::Fifo,
+      desired_maximum_frame_latency: 2,
+      alpha_mode: swapchain_capabilities.alpha_modes[0],
+      view_formats: vec![],
+    };
 
-@fragment
-fn fs_main() -> @location(0) vec4<f32> {
-    return vec4<f32>(1.0, 0.0, 0.0, 1.0);
+    surface.configure(&device, &config);
+
+    Self {
+      surface,
+      device,
+      queue,
+      config,
+      render_pipeline,
+    }
+  }
+
+  fn render(&mut self) {
+    let frame = self
+      .surface
+      .get_current_texture()
+      .expect("Failed to acquire next swap chain texture");
+    let view = frame
+      .texture
+      .create_view(&wgpu::TextureViewDescriptor::default());
+    let mut encoder = self
+      .device
+      .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+    {
+      let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+        label: None,
+        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
+          view: &view,
+          resolve_target: None,
+          ops: wgpu::Operations {
+            load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
+            store: wgpu::StoreOp::Store,
+          },
+        })],
+        depth_stencil_attachment: None,
+        timestamp_writes: None,
+        occlusion_query_set: None,
+      });
+      render_pass.set_pipeline(&self.render_pipeline);
+      render_pass.draw(0..3, 0..1);
+    }
+
+    self.queue.submit(Some(encoder.finish()));
+    frame.present();
+  }
 }
-"#,
-    )),
-  });
-
-  let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
-    label: None,
-    bind_group_layouts: &[],
-    push_constant_ranges: &[],
-  });
-
-  let swapchain_capabilities = surface.get_capabilities(&adapter);
-  let swapchain_format = swapchain_capabilities.formats[0];
-
-  let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
-    label: None,
-    layout: Some(&pipeline_layout),
-    vertex: wgpu::VertexState {
-      module: &shader,
-      entry_point: "vs_main",
-      buffers: &[],
-    },
-    fragment: Some(wgpu::FragmentState {
-      module: &shader,
-      entry_point: "fs_main",
-      targets: &[Some(swapchain_format.into())],
-    }),
-    primitive: wgpu::PrimitiveState::default(),
-    depth_stencil: None,
-    multisample: wgpu::MultisampleState::default(),
-    multiview: None,
-  });
-
-  let mut config = wgpu::SurfaceConfiguration {
-    usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
-    format: swapchain_format,
-    width: size.width,
-    height: size.height,
-    present_mode: wgpu::PresentMode::Fifo,
-    desired_maximum_frame_latency: 2,
-    alpha_mode: swapchain_capabilities.alpha_modes[0],
-    view_formats: vec![],
-  };
-
-  surface.configure(&device, &config);
-
-  let _webview = WebViewBuilder::new()
-    .with_bounds(Rect {
-      position: LogicalPosition::new(100, 100).into(),
-      size: LogicalSize::new(200, 200).into(),
-    })
-    .with_transparent(true)
-    .with_html(
-      r#"<html>
-          <body style="background-color:rgba(87,87,87,0.5);"></body>
-          <script>
-            window.onload = function() {
-              document.body.innerText = `hello, ${navigator.userAgent}`;
-            };
-          </script>
-        </html>"#,
-    )
-    .build_as_child(&window)
-    .unwrap();
-
-  event_loop
-    .run(|event, evl| {
-      evl.set_control_flow(ControlFlow::Poll);
-
-      match event {
-        Event::WindowEvent {
-          event: WindowEvent::Resized(size),
-          ..
-        } => {
-          // Reconfigure the surface with the new size
-          config.width = size.width;
-          config.height = size.height;
-          surface.configure(&device, &config);
-          // On macos the window needs to be redrawn manually after resizing
-          window.request_redraw();
-        }
-        Event::WindowEvent {
-          event: WindowEvent::RedrawRequested,
-          ..
-        } => {
-          let frame = surface
-            .get_current_texture()
-            .expect("Failed to acquire next swap chain texture");
-          let view = frame
-            .texture
-            .create_view(&wgpu::TextureViewDescriptor::default());
-          let mut encoder =
-            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
-          {
-            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
-              label: None,
-              color_attachments: &[Some(wgpu::RenderPassColorAttachment {
-                view: &view,
-                resolve_target: None,
-                ops: wgpu::Operations {
-                  load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
-                  store: wgpu::StoreOp::Store,
-                },
-              })],
-              depth_stencil_attachment: None,
-              timestamp_writes: None,
-              occlusion_query_set: None,
-            });
-            rpass.set_pipeline(&render_pipeline);
-            rpass.draw(0..3, 0..1);
-          }
 
-          queue.submit(Some(encoder.finish()));
-          frame.present();
+impl ApplicationHandler for State {
+  fn resumed(&mut self, event_loop: &ActiveEventLoop) {
+    let mut attributes = Window::default_attributes();
+    attributes.transparent = true;
+    #[cfg(windows)]
+    {
+      use winit::platform::windows::WindowAttributesExtWindows;
+      attributes = attributes.with_clip_children(false);
+    }
+
+    let window = Arc::new(event_loop.create_window(attributes).unwrap());
+
+    let gfx_state = GfxState::new(Arc::clone(&window));
+
+    let webview = WebViewBuilder::new()
+      .with_bounds(Rect {
+        position: LogicalPosition::new(100, 100).into(),
+        size: LogicalSize::new(200, 200).into(),
+      })
+      .with_transparent(true)
+      .with_html(
+        r#"<html>
+                    <body style="background-color:rgba(87,87,87,0.5);"></body>
+                    <script>
+                        window.onload = function() {
+                            document.body.innerText = `hello, ${navigator.userAgent}`;
+                        };
+                    </script>
+                </html>"#,
+      )
+      .build_as_child(&window)
+      .unwrap();
+
+    window.request_redraw();
+
+    self.window = Some(window);
+    self.webview = Some(webview);
+    self.gfx_state = Some(gfx_state);
+  }
+
+  fn window_event(
+    &mut self,
+    _event_loop: &ActiveEventLoop,
+    _window_id: WindowId,
+    event: WindowEvent,
+  ) {
+    match event {
+      WindowEvent::Resized(size) => {
+        if let Some(gfx_state) = &mut self.gfx_state {
+          gfx_state.config.width = size.width;
+          gfx_state.config.height = size.height;
+          gfx_state
+            .surface
+            .configure(&gfx_state.device, &gfx_state.config);
+
+          if let Some(window) = &self.window {
+            window.request_redraw();
+          }
         }
-        Event::WindowEvent {
-          event: WindowEvent::CloseRequested,
-          ..
-        } => evl.exit(),
-        _ => {}
       }
+      WindowEvent::RedrawRequested => {
+        if let Some(gfx_state) = &mut self.gfx_state {
+          gfx_state.render();
+        }
+      }
+      WindowEvent::CloseRequested => {
+        std::process::exit(0);
+      }
+      _ => {}
+    }
+  }
 
-      #[cfg(any(
-        target_os = "linux",
-        target_os = "dragonfly",
-        target_os = "freebsd",
-        target_os = "netbsd",
-        target_os = "openbsd",
-      ))]
+  fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
+    #[cfg(any(
+      target_os = "linux",
+      target_os = "dragonfly",
+      target_os = "freebsd",
+      target_os = "netbsd",
+      target_os = "openbsd",
+    ))]
+    {
       while gtk::events_pending() {
         gtk::main_iteration_do(false);
       }
-    })
-    .unwrap();
+    }
+  }
 }
 
 fn main() {
@@ -207,8 +262,6 @@ fn main() {
       panic!("This example doesn't support wayland!");
     }
 
-    // we need to ignore this error here otherwise it will be catched by winit and will be
-    // make the example crash
     winit::platform::x11::register_xlib_error_hook(Box::new(|_display, error| {
       let error = error as *mut x11_dl::xlib::XErrorEvent;
       (unsafe { (*error).error_code }) == 170
@@ -216,9 +269,6 @@ fn main() {
   }
 
   let event_loop = EventLoop::new().unwrap();
-  let window = winit::window::WindowBuilder::new()
-    .with_transparent(true)
-    .build(&event_loop)
-    .unwrap();
-  pollster::block_on(run(event_loop, window));
+  let mut state = State::default();
+  event_loop.run_app(&mut state).unwrap();
 }
diff --git a/examples/winit.rs b/examples/winit.rs
index dcbdad522..729948700 100644
--- a/examples/winit.rs
+++ b/examples/winit.rs
@@ -1,15 +1,75 @@
-// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
 use dpi::{LogicalPosition, LogicalSize};
 use winit::{
-  event::{Event, WindowEvent},
-  event_loop::{ControlFlow, EventLoop},
-  window::WindowBuilder,
+  application::ApplicationHandler,
+  event::WindowEvent,
+  event_loop::{ActiveEventLoop, EventLoop},
+  window::{Window, WindowId},
 };
 use wry::{Rect, WebViewBuilder};
 
+#[derive(Default)]
+struct State {
+  window: Option<Window>,
+  webview: Option<wry::WebView>,
+}
+
+impl ApplicationHandler for State {
+  fn resumed(&mut self, event_loop: &ActiveEventLoop) {
+    let mut attributes = Window::default_attributes();
+    attributes.inner_size = Some(LogicalSize::new(800, 800).into());
+    let window = event_loop.create_window(attributes).unwrap();
+
+    let webview = WebViewBuilder::new()
+      .with_url("https://tauri.app")
+      .build_as_child(&window)
+      .unwrap();
+
+    self.window = Some(window);
+    self.webview = Some(webview);
+  }
+
+  fn window_event(
+    &mut self,
+    _event_loop: &ActiveEventLoop,
+    _window_id: WindowId,
+    event: WindowEvent,
+  ) {
+    match event {
+      WindowEvent::Resized(size) => {
+        let window = self.window.as_ref().unwrap();
+        let webview = self.webview.as_ref().unwrap();
+
+        let size = size.to_logical::<u32>(window.scale_factor());
+        webview
+          .set_bounds(Rect {
+            position: LogicalPosition::new(0, 0).into(),
+            size: LogicalSize::new(size.width, size.height).into(),
+          })
+          .unwrap();
+      }
+      WindowEvent::CloseRequested => {
+        std::process::exit(0);
+      }
+      _ => {}
+    }
+  }
+
+  fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
+    #[cfg(any(
+      target_os = "linux",
+      target_os = "dragonfly",
+      target_os = "freebsd",
+      target_os = "netbsd",
+      target_os = "openbsd",
+    ))]
+    {
+      while gtk::events_pending() {
+        gtk::main_iteration_do(false);
+      }
+    }
+  }
+}
+
 fn main() -> wry::Result<()> {
   #[cfg(any(
     target_os = "linux",
@@ -26,8 +86,6 @@ fn main() -> wry::Result<()> {
       panic!("This example doesn't support wayland!");
     }
 
-    // we need to ignore this error here otherwise it will be catched by winit and will be
-    // make the example crash
     winit::platform::x11::register_xlib_error_hook(Box::new(|_display, error| {
       let error = error as *mut x11_dl::xlib::XErrorEvent;
       (unsafe { (*error).error_code }) == 170
@@ -35,51 +93,8 @@ fn main() -> wry::Result<()> {
   }
 
   let event_loop = EventLoop::new().unwrap();
-  let window = WindowBuilder::new()
-    .with_inner_size(winit::dpi::LogicalSize::new(800, 800))
-    .build(&event_loop)
-    .unwrap();
-
-  let webview = WebViewBuilder::new()
-    .with_url("https://tauri.app")
-    .build_as_child(&window)?;
-
-  event_loop
-    .run(move |event, evl| {
-      evl.set_control_flow(ControlFlow::Poll);
-
-      #[cfg(any(
-        target_os = "linux",
-        target_os = "dragonfly",
-        target_os = "freebsd",
-        target_os = "netbsd",
-        target_os = "openbsd",
-      ))]
-      while gtk::events_pending() {
-        gtk::main_iteration_do(false);
-      }
-
-      match event {
-        Event::WindowEvent {
-          event: WindowEvent::Resized(size),
-          ..
-        } => {
-          let size = size.to_logical::<u32>(window.scale_factor());
-          webview
-            .set_bounds(Rect {
-              position: LogicalPosition::new(0, 0).into(),
-              size: LogicalSize::new(size.width, size.height).into(),
-            })
-            .unwrap();
-        }
-        Event::WindowEvent {
-          event: WindowEvent::CloseRequested,
-          ..
-        } => evl.exit(),
-        _ => {}
-      }
-    })
-    .unwrap();
+  let mut state = State::default();
+  event_loop.run_app(&mut state).unwrap();
 
   Ok(())
 }
diff --git a/src/lib.rs b/src/lib.rs
index 38d3f26f5..abce41501 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,14 +15,32 @@
 //!
 //! ```no_run
 //! # use wry::{WebViewBuilder, raw_window_handle};
-//! # use winit::{window::WindowBuilder, event_loop::EventLoop};
-//! let event_loop = EventLoop::new().unwrap();
-//! let window = WindowBuilder::new().build(&event_loop).unwrap();
+//! # use winit::{application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowId}};
 //!
-//! let webview = WebViewBuilder::new()
-//!   .with_url("https://tauri.app")
-//!   .build(&window)
-//!   .unwrap();
+//! #[derive(Default)]
+//! struct App {
+//!   window: Option<Window>,
+//!   webview: Option<wry::WebView>,
+//! }
+//!
+//! impl ApplicationHandler for App {
+//!   fn resumed(&mut self, event_loop: &ActiveEventLoop) {
+//!     let window = event_loop.create_window(Window::default_attributes()).unwrap();
+//!     let webview = WebViewBuilder::new()
+//!       .with_url("https://tauri.app")
+//!       .build(&window)
+//!       .unwrap();
+//!
+//!     self.window = Some(window);
+//!     self.webview = Some(webview);
+//!   }
+//!
+//!   fn window_event(&mut self, _event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {}
+//! }
+//!
+//! let event_loop = EventLoop::new().unwrap();
+//! let mut app = App::default();
+//! event_loop.run_app(&mut app).unwrap();
 //! ```
 //!
 //! If you also want to support Wayland too, then we recommend you use [`WebViewBuilderExtUnix::new_gtk`] on Linux.
@@ -53,18 +71,36 @@
 //!
 //! ```no_run
 //! # use wry::{WebViewBuilder, raw_window_handle, Rect, dpi::*};
-//! # use winit::{window::WindowBuilder, event_loop::EventLoop};
-//! let event_loop = EventLoop::new().unwrap();
-//! let window = WindowBuilder::new().build(&event_loop).unwrap();
+//! # use winit::{application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowId}};
 //!
-//! let webview = WebViewBuilder::new()
-//!   .with_url("https://tauri.app")
-//!   .with_bounds(Rect {
-//!     position: LogicalPosition::new(100, 100).into(),
-//!     size: LogicalSize::new(200, 200).into(),
-//!   })
-//!   .build_as_child(&window)
-//!   .unwrap();
+//! #[derive(Default)]
+//! struct App {
+//!   window: Option<Window>,
+//!   webview: Option<wry::WebView>,
+//! }
+//!
+//! impl ApplicationHandler for App {
+//!   fn resumed(&mut self, event_loop: &ActiveEventLoop) {
+//!     let window = event_loop.create_window(Window::default_attributes()).unwrap();
+//!     let webview = WebViewBuilder::new()
+//!       .with_url("https://tauri.app")
+//!       .with_bounds(Rect {
+//!         position: LogicalPosition::new(100, 100).into(),
+//!         size: LogicalSize::new(200, 200).into(),
+//!       })
+//!       .build_as_child(&window)
+//!       .unwrap();
+//!
+//!     self.window = Some(window);
+//!     self.webview = Some(webview);
+//!   }
+//!
+//!   fn window_event(&mut self, _event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {}
+//! }
+//!
+//! let event_loop = EventLoop::new().unwrap();
+//! let mut app = App::default();
+//! event_loop.run_app(&mut app).unwrap();
 //! ```
 //!
 //! If you want to support X11 and Wayland at the same time, we recommend using
@@ -107,24 +143,41 @@
 //! your windowing library event loop.
 //!
 //! ```no_run
-//! # use winit::{event_loop::EventLoop, window::Window};
-//! # use wry::{WebView, WebViewAttributes};
-//! #[cfg(target_os = "linux")]
-//! gtk::init().unwrap(); // <----- IMPORTANT
-//! let event_loop = EventLoop::new().unwrap();
+//! # use wry::{WebViewBuilder, raw_window_handle};
+//! # use winit::{application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowId}};
 //!
-//! let window = Window::new(&event_loop).unwrap();
-//! let webview = WebView::new(&window, WebViewAttributes::default());
+//! #[derive(Default)]
+//! struct App {
+//!   window: Option<Window>,
+//!   webview: Option<wry::WebView>,
+//! }
 //!
-//! event_loop.run(|_e, _evl|{
-//!   // process winit events
+//! impl ApplicationHandler for App {
+//!   fn resumed(&mut self, event_loop: &ActiveEventLoop) {
+//!     let window = event_loop.create_window(Window::default_attributes()).unwrap();
+//!     let webview = WebViewBuilder::new()
+//!       .with_url("https://tauri.app")
+//!       .build(&window)
+//!       .unwrap();
 //!
-//!   // then advance gtk event loop  <----- IMPORTANT
-//!   #[cfg(target_os = "linux")]
-//!   while gtk::events_pending() {
-//!     gtk::main_iteration_do(false);
+//!     self.window = Some(window);
+//!     self.webview = Some(webview);
 //!   }
-//! }).unwrap();
+//!
+//!   fn window_event(&mut self, _event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {}
+//!
+//!   // Advance GTK event loop <!----- IMPORTANT
+//!   fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
+//!     #[cfg(target_os = "linux")]
+//!     while gtk::events_pending() {
+//!       gtk::main_iteration_do(false);
+//!     }
+//!   }
+//! }
+//!
+//! let event_loop = EventLoop::new().unwrap();
+//! let mut app = App::default();
+//! event_loop.run_app(&mut app).unwrap();
 //! ```
 //!
 //! ## Android
@@ -695,12 +748,7 @@ impl<'a> WebViewBuilder<'a> {
   /// `window.onload`.
   ///
   /// ## Example
-  /// ```no_run
-  /// # use wry::{WebViewBuilder, raw_window_handle, Rect, dpi::*};
-  /// # use winit::{window::WindowBuilder, event_loop::EventLoop};
-  /// let event_loop = EventLoop::new().unwrap();
-  /// let window = WindowBuilder::new().build(&event_loop).unwrap();
-  ///
+  /// ```ignore
   /// let webview = WebViewBuilder::new()
   ///   .with_initialization_script("console.log('Running inside main frame only')")
   ///   .with_url("https://tauri.app")
@@ -723,12 +771,7 @@ impl<'a> WebViewBuilder<'a> {
   /// Same as [`with_initialization_script`](Self::with_initialization_script) but with option to inject into main frame only or sub frames.
   ///
   /// ## Example
-  /// ```no_run
-  /// # use wry::{WebViewBuilder, raw_window_handle, Rect, dpi::*};
-  /// # use winit::{window::WindowBuilder, event_loop::EventLoop};
-  /// let event_loop = EventLoop::new().unwrap();
-  /// let window = WindowBuilder::new().build(&event_loop).unwrap();
-  ///
+  /// ```ignore
   /// let webview = WebViewBuilder::new()
   ///   .with_initialization_script_for_main_only("console.log('Running inside main frame only')", true)
   ///   .with_initialization_script_for_main_only("console.log('Running  main frame and sub frames')", false)


Test patch: