chore: avoid unsafe in examples

By using `OwnedDisplayHandle`, which uses reference-counting, instead of
`DisplayHandle<'static>` (where the user has to guarantee that the
Application doesn't outlive the event loop).
This commit is contained in:
Mads Marquart
2026-06-14 20:36:22 +02:00
committed by GitHub
parent fb78ecbb03
commit 7fe8c206ae
11 changed files with 202 additions and 254 deletions

View File

@@ -76,9 +76,13 @@ winit-core.workspace = true
[dev-dependencies] [dev-dependencies]
image = { workspace = true, features = ["png"] } image = { workspace = true, features = ["png"] }
softbuffer.workspace = true
tracing = { workspace = true, features = ["log"] } tracing = { workspace = true, features = ["log"] }
tracing-subscriber = { workspace = true, features = ["env-filter"] } tracing-subscriber = { workspace = true, features = ["env-filter"] }
# Launching a window without drawing to it has unpredictable results varying from platform to
# platform. We use the `softbuffer` crate in our examples because of its ease of use to avoid
# confusion around this. `glutin` or `wgpu` could also be used to fill the window buffer, but they
# are more complicated to set up.
softbuffer.workspace = true
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
winit-android.workspace = true winit-android.workspace = true

View File

@@ -15,7 +15,6 @@ use std::time::Instant;
use std::{fmt, mem}; use std::{fmt, mem};
use cursor_icon::CursorIcon; use cursor_icon::CursorIcon;
use rwh_06::{DisplayHandle, HasDisplayHandle};
use softbuffer::{Context, Surface}; use softbuffer::{Context, Surface};
use tracing::{error, info}; use tracing::{error, info};
#[cfg(web_platform)] #[cfg(web_platform)]
@@ -25,7 +24,7 @@ use winit::cursor::{Cursor, CustomCursor, CustomCursorSource};
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize}; use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::error::RequestError; use winit::error::RequestError;
use winit::event::{DeviceEvent, DeviceId, MouseButton, MouseScrollDelta, WindowEvent}; use winit::event::{DeviceEvent, DeviceId, MouseButton, MouseScrollDelta, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
use winit::icon::{Icon, RgbaIcon}; use winit::icon::{Icon, RgbaIcon};
use winit::keyboard::{Key, ModifiersState}; use winit::keyboard::{Key, ModifiersState};
use winit::monitor::Fullscreen; use winit::monitor::Fullscreen;
@@ -45,9 +44,6 @@ use winit_core::application::macos::ApplicationHandlerExtMacOS;
#[path = "util/tracing.rs"] #[path = "util/tracing.rs"]
mod tracing_init; mod tracing_init;
#[path = "util/fill.rs"]
mod fill;
/// The amount of points to around the window for drag resize direction calculations. /// The amount of points to around the window for drag resize direction calculations.
const BORDER_SIZE: f64 = 20.; const BORDER_SIZE: f64 = 20.;
@@ -94,20 +90,12 @@ struct Application {
/// Drawing context. /// Drawing context.
/// ///
/// With OpenGL it could be EGLDisplay. /// With OpenGL it could be EGLDisplay.
context: Option<Context<DisplayHandle<'static>>>, context: Context<OwnedDisplayHandle>,
} }
impl Application { impl Application {
fn new(event_loop: &EventLoop, receiver: Receiver<Action>, sender: Sender<Action>) -> Self { fn new(event_loop: &EventLoop, receiver: Receiver<Action>, sender: Sender<Action>) -> Self {
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe. let context = Context::new(event_loop.owned_display_handle()).unwrap();
let context = Some(
Context::new(unsafe {
std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(
event_loop.display_handle().unwrap(),
)
})
.unwrap(),
);
// You'll have to choose an icon size at your own discretion. On X11, the desired size // You'll have to choose an icon size at your own discretion. On X11, the desired size
// varies by WM, and on Windows, you still have to account for screen scaling. Here // varies by WM, and on Windows, you still have to account for screen scaling. Here
@@ -596,9 +584,7 @@ impl ApplicationHandlerExtMacOS for Application {
/// State of the window. /// State of the window.
struct WindowState { struct WindowState {
/// Render surface. /// Render surface.
/// surface: Surface<OwnedDisplayHandle, Arc<dyn Window>>,
/// NOTE: This surface must be dropped before the `Window`.
surface: Surface<DisplayHandle<'static>, Arc<dyn Window>>,
/// The actual winit Window. /// The actual winit Window.
window: Arc<dyn Window>, window: Arc<dyn Window>,
/// The window theme we're drawing with. /// The window theme we're drawing with.
@@ -639,9 +625,7 @@ impl WindowState {
fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> { fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> {
let window: Arc<dyn Window> = Arc::from(window); let window: Arc<dyn Window> = Arc::from(window);
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus let surface = Surface::new(&app.context, Arc::clone(&window))?;
// it doesn't outlive it.
let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
let theme = window.theme().unwrap_or(Theme::Dark); let theme = window.theme().unwrap_or(Theme::Dark);
info!("Theme: {theme:?}"); info!("Theme: {theme:?}");
@@ -928,35 +912,40 @@ impl WindowState {
return Ok(()); return Ok(());
} }
if self.animated_fill_color {
fill::fill_window_with_animated_color(&*self.window, self.start_time);
return Ok(());
}
let mut buffer = self.surface.buffer_mut()?; let mut buffer = self.surface.buffer_mut()?;
// Draw a different color inside the safe area if self.animated_fill_color {
let surface_size = self.window.surface_size(); // Fill the entire buffer with a single color.
let insets = self.window.safe_area(); let time = self.start_time.elapsed().as_secs_f32() * 1.5;
for y in 0..surface_size.height { let blue = (time.sin() * 255.0) as u32;
for x in 0..surface_size.width { let green = ((time.cos() * 255.0) as u32) << 8;
let index = y as usize * surface_size.width as usize + x as usize; let red = ((1.0 - time.sin() * 255.0) as u32) << 16;
if insets.left <= x let color = red | green | blue;
&& x <= (surface_size.width - insets.right) buffer.fill(color);
&& insets.top <= y } else {
&& y <= (surface_size.height - insets.bottom) // Draw a different color inside the safe area
{ let surface_size = self.window.surface_size();
// In safe area let insets = self.window.safe_area();
buffer[index] = match self.theme { for y in 0..surface_size.height {
Theme::Light => 0xffe8e8e8, // Light gray for x in 0..surface_size.width {
Theme::Dark => 0xff525252, // Medium gray let index = y as usize * surface_size.width as usize + x as usize;
}; if insets.left <= x
} else { && x <= (surface_size.width - insets.right)
// Outside safe area && insets.top <= y
buffer[index] = match self.theme { && y <= (surface_size.height - insets.bottom)
Theme::Light => 0xffffffff, // White {
Theme::Dark => 0xff181818, // Dark gray // In safe area
}; buffer[index] = match self.theme {
Theme::Light => 0xffe8e8e8, // Light gray
Theme::Dark => 0xff525252, // Medium gray
};
} else {
// Outside safe area
buffer[index] = match self.theme {
Theme::Light => 0xffffffff, // White
Theme::Dark => 0xff181818, // Dark gray
};
}
} }
} }
} }

View File

@@ -3,11 +3,12 @@
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
use std::collections::HashMap; use std::collections::HashMap;
use softbuffer::{Context, Surface};
use tracing::info; use tracing::info;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::dpi::{LogicalPosition, LogicalSize, Position}; use winit::dpi::{LogicalPosition, LogicalSize, Position};
use winit::event::{ElementState, KeyEvent, WindowEvent}; use winit::event::{ElementState, KeyEvent, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
use winit::raw_window_handle::HasRawWindowHandle; use winit::raw_window_handle::HasRawWindowHandle;
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::{Window, WindowAttributes, WindowId};
@@ -16,18 +17,20 @@ fn main() -> Result<(), impl std::error::Error> {
#[derive(Debug)] #[derive(Debug)]
struct WindowData { struct WindowData {
window: Box<dyn Window>, surface: Surface<OwnedDisplayHandle, Box<dyn Window>>,
color: u32, color: u32,
} }
impl WindowData { impl WindowData {
fn new(window: Box<dyn Window>, color: u32) -> Self { fn new(context: &Context<OwnedDisplayHandle>, window: Box<dyn Window>, color: u32) -> Self {
Self { window, color } let surface = Surface::new(context, window).unwrap();
Self { surface, color }
} }
} }
#[derive(Default, Debug)] #[derive(Debug)]
struct Application { struct Application {
context: Context<OwnedDisplayHandle>,
parent_window_id: Option<WindowId>, parent_window_id: Option<WindowId>,
windows: HashMap<WindowId, WindowData>, windows: HashMap<WindowId, WindowData>,
} }
@@ -42,7 +45,7 @@ fn main() -> Result<(), impl std::error::Error> {
info!("Parent window id: {:?})", window.id()); info!("Parent window id: {:?})", window.id());
self.parent_window_id = Some(window.id()); self.parent_window_id = Some(window.id());
self.windows.insert(window.id(), WindowData::new(window, 0xffbbbbbb)); self.windows.insert(window.id(), WindowData::new(&self.context, window, 0xffbbbbbb));
} }
fn window_event( fn window_event(
@@ -73,18 +76,24 @@ fn main() -> Result<(), impl std::error::Error> {
0xff000000 + 3_u32.pow((child_index + 2).rem_euclid(16) as u32); 0xff000000 + 3_u32.pow((child_index + 2).rem_euclid(16) as u32);
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap(); let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap();
let child_window = let child_window = spawn_child_window(
spawn_child_window(parent_window.window.as_ref(), event_loop, child_index); parent_window.surface.window().as_ref(),
event_loop,
child_index,
);
let child_id = child_window.id(); let child_id = child_window.id();
info!("Child window created with id: {child_id:?}"); info!("Child window created with id: {child_id:?}");
self.windows.insert(child_id, WindowData::new(child_window, child_color)); self.windows.insert(
child_id,
WindowData::new(&self.context, child_window, child_color),
);
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
if let Some(window) = self.windows.get(&window_id) { if let Some(window) = self.windows.get_mut(&window_id) {
if window_id == self.parent_window_id.unwrap() { if window_id == self.parent_window_id.unwrap() {
fill::fill_window(window.window.as_ref()); fill::fill(&mut window.surface);
} else { } else {
fill::fill_window_with_color(window.window.as_ref(), window.color); fill::fill_with_color(&mut window.surface, window.color);
} }
} }
}, },
@@ -118,7 +127,8 @@ fn main() -> Result<(), impl std::error::Error> {
} }
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
event_loop.run_app(Application::default()) let context = Context::new(event_loop.owned_display_handle()).unwrap();
event_loop.run_app(Application { context, parent_window_id: None, windows: HashMap::new() })
} }
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))] #[cfg(not(any(x11_platform, macos_platform, windows_platform)))]

View File

@@ -4,12 +4,13 @@ use std::thread;
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
use std::time; use std::time;
use softbuffer::{Context, Surface};
use tracing::{info, warn}; use tracing::{info, warn};
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time as time; use web_time as time;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent}; use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop, OwnedDisplayHandle};
use winit::keyboard::{Key, NamedKey}; use winit::keyboard::{Key, NamedKey};
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::{Window, WindowAttributes, WindowId};
@@ -52,7 +53,7 @@ struct ControlFlowDemo {
request_redraw: bool, request_redraw: bool,
wait_cancelled: bool, wait_cancelled: bool,
close_requested: bool, close_requested: bool,
window: Option<Box<dyn Window>>, surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
} }
impl ApplicationHandler for ControlFlowDemo { impl ApplicationHandler for ControlFlowDemo {
@@ -69,7 +70,10 @@ impl ApplicationHandler for ControlFlowDemo {
let window_attributes = WindowAttributes::default().with_title( let window_attributes = WindowAttributes::default().with_title(
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.", "Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
); );
self.window = Some(event_loop.create_window(window_attributes).unwrap()); let window = event_loop.create_window(window_attributes).unwrap();
let context = Context::new(event_loop.owned_display_handle()).unwrap();
let surface = Surface::new(&context, window).unwrap();
self.surface = Some(surface);
} }
fn window_event( fn window_event(
@@ -112,9 +116,9 @@ impl ApplicationHandler for ControlFlowDemo {
_ => (), _ => (),
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
let window = self.window.as_ref().unwrap(); let surface = self.surface.as_mut().unwrap();
window.pre_present_notify(); surface.window().pre_present_notify();
fill::fill_window(window.as_ref()); fill::fill(surface);
}, },
_ => (), _ => (),
} }
@@ -122,7 +126,7 @@ impl ApplicationHandler for ControlFlowDemo {
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
if self.request_redraw && !self.wait_cancelled && !self.close_requested { if self.request_redraw && !self.wait_cancelled && !self.close_requested {
self.window.as_ref().unwrap().request_redraw(); self.surface.as_ref().unwrap().window().request_redraw();
} }
match self.mode { match self.mode {

View File

@@ -1,9 +1,10 @@
use std::error::Error; use std::error::Error;
use softbuffer::{Context, Surface};
use tracing::info; use tracing::info;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::{Window, WindowAttributes, WindowId};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -16,27 +17,24 @@ fn main() -> Result<(), Box<dyn Error>> {
let event_loop = EventLoop::new()?; let event_loop = EventLoop::new()?;
let app = Application::new(); let app = Application::default();
Ok(event_loop.run_app(app)?) Ok(event_loop.run_app(app)?)
} }
/// Application state and event handling. /// Application state and event handling.
#[derive(Debug)] #[derive(Default, Debug)]
struct Application { struct Application {
window: Option<Box<dyn Window>>, surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
}
impl Application {
fn new() -> Self {
Self { window: None }
}
} }
impl ApplicationHandler for Application { impl ApplicationHandler for Application {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = let window_attributes =
WindowAttributes::default().with_title("Drag and drop files on me!"); WindowAttributes::default().with_title("Drag and drop files on me!");
self.window = Some(event_loop.create_window(window_attributes).unwrap()); let window = event_loop.create_window(window_attributes).unwrap();
let context = Context::new(event_loop.owned_display_handle()).unwrap();
let surface = Surface::new(&context, window).unwrap();
self.surface = Some(surface);
} }
fn window_event( fn window_event(
@@ -53,9 +51,9 @@ impl ApplicationHandler for Application {
info!("{event:?}"); info!("{event:?}");
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
let window = self.window.as_ref().unwrap(); let surface = self.surface.as_mut().unwrap();
window.pre_present_notify(); surface.window().pre_present_notify();
fill::fill_window(window.as_ref()); fill::fill(surface);
}, },
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
event_loop.exit(); event_loop.exit();

View File

@@ -9,10 +9,11 @@ use std::cmp;
use std::error::Error; use std::error::Error;
use dpi::{LogicalPosition, PhysicalSize}; use dpi::{LogicalPosition, PhysicalSize};
use softbuffer::{Context, Surface};
use tracing::{error, info}; use tracing::{error, info};
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::{Ime, WindowEvent}; use winit::event::{Ime, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
use winit::keyboard::{Key, ModifiersState, NamedKey}; use winit::keyboard::{Key, ModifiersState, NamedKey};
#[cfg(web_platform)] #[cfg(web_platform)]
use winit::platform::web::WindowAttributesWeb; use winit::platform::web::WindowAttributesWeb;
@@ -30,7 +31,7 @@ const IME_CURSOR_SIZE: PhysicalSize<u32> = PhysicalSize::new(20, 20);
#[derive(Debug)] #[derive(Debug)]
struct App { struct App {
window: Option<Box<dyn Window>>, surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
input_state: TextInputState, input_state: TextInputState,
modifiers: ModifiersState, modifiers: ModifiersState,
} }
@@ -73,14 +74,12 @@ impl ApplicationHandler for App {
#[cfg(web_platform)] #[cfg(web_platform)]
let window_attributes = WindowAttributes::default() let window_attributes = WindowAttributes::default()
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true))); .with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
self.window = match event_loop.create_window(window_attributes) { let window = event_loop.create_window(window_attributes).expect("failed creating window");
Ok(window) => Some(window),
Err(err) => { let context =
error!("error creating window: {err}"); Context::new(event_loop.owned_display_handle()).expect("failed creating context");
event_loop.exit(); let surface = Surface::new(&context, window).expect("failed creating surface");
return; self.surface = Some(surface);
},
};
// Allow IME out of the box. // Allow IME out of the box.
let enable_request = ImeEnableRequest::new( let enable_request = ImeEnableRequest::new(
@@ -101,11 +100,13 @@ impl ApplicationHandler for App {
match event { match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
info!("Close was requested; stopping"); info!("Close was requested; stopping");
self.window = None; self.surface = None;
event_loop.exit(); event_loop.exit();
}, },
WindowEvent::SurfaceResized(_) => { WindowEvent::SurfaceResized(surface_size) => {
self.window.as_ref().expect("resize event without a window").request_redraw(); let surface = self.surface.as_mut().expect("resize event without a surface");
fill::resize(surface, surface_size);
surface.window().request_redraw();
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
// Redraw the application. // Redraw the application.
@@ -114,13 +115,13 @@ impl ApplicationHandler for App {
// this event rather than in AboutToWait, since rendering in here allows // this event rather than in AboutToWait, since rendering in here allows
// the program to gracefully handle redraws requested by the OS. // the program to gracefully handle redraws requested by the OS.
let window = self.window.as_ref().expect("redraw request without a window"); let surface = self.surface.as_mut().expect("redraw event without a surface");
// Notify that you're about to draw. // Notify that you're about to draw.
window.pre_present_notify(); surface.window().pre_present_notify();
// Draw. // Draw.
fill::fill_window(window.as_ref()); fill::fill(surface);
// For contiguous redraw loop you can request a redraw from here. // For contiguous redraw loop you can request a redraw from here.
// window.request_redraw(); // window.request_redraw();
@@ -214,14 +215,14 @@ impl App {
} }
fn handle_ime_event(&mut self, event: Ime) { fn handle_ime_event(&mut self, event: Ime) {
let window = self.window.as_ref().expect("IME request without a window"); let surface = self.surface.as_ref().expect("IME request without a window");
match event { match event {
Ime::Enabled => info!("IME enabled for Window={:?}", window.id()), Ime::Enabled => info!("IME enabled for Window={:?}", surface.window().id()),
Ime::Preedit(text, caret_pos) => info!("Preedit: {text}, with caret at {caret_pos:?}"), Ime::Preedit(text, caret_pos) => info!("Preedit: {text}, with caret at {caret_pos:?}"),
Ime::Commit(text) => { Ime::Commit(text) => {
self.input_state.append_text(&text); self.input_state.append_text(&text);
let request_data = self.get_ime_update(); let request_data = self.get_ime_update();
window.request_ime_update(ImeRequest::Update(request_data)).unwrap(); surface.window().request_ime_update(ImeRequest::Update(request_data)).unwrap();
self.print_input_state(); self.print_input_state();
}, },
Ime::DeleteSurrounding { before_bytes, after_bytes } => { Ime::DeleteSurrounding { before_bytes, after_bytes } => {
@@ -246,7 +247,7 @@ impl App {
error!("Buggy IME tried to delete with indices not on char boundary."); error!("Buggy IME tried to delete with indices not on char boundary.");
} }
}, },
Ime::Disabled => info!("IME disabled for Window={:?}", window.id()), Ime::Disabled => info!("IME disabled for Window={:?}", surface.window().id()),
} }
} }
@@ -312,7 +313,7 @@ impl App {
} }
fn window(&self) -> &dyn Window { fn window(&self) -> &dyn Window {
self.window.as_ref().unwrap().as_ref() self.surface.as_ref().unwrap().window().as_ref()
} }
} }
@@ -355,7 +356,7 @@ Use CTRL+h to cycle content hint permutations.
); );
let app = App { let app = App {
window: None, surface: None,
input_state: TextInputState { input_state: TextInputState {
ime_enabled: true, ime_enabled: true,
contents: String::new(), contents: String::new(),

View File

@@ -7,11 +7,12 @@ fn main() -> std::process::ExitCode {
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use softbuffer::{Context, Surface};
use tracing::info; use tracing::info;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::WindowEvent;
use winit::event_loop::pump_events::{EventLoopExtPumpEvents, PumpStatus}; use winit::event_loop::pump_events::{EventLoopExtPumpEvents, PumpStatus};
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::{Window, WindowAttributes, WindowId};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -21,13 +22,16 @@ fn main() -> std::process::ExitCode {
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct PumpDemo { struct PumpDemo {
window: Option<Box<dyn Window>>, surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
} }
impl ApplicationHandler for PumpDemo { impl ApplicationHandler for PumpDemo {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = WindowAttributes::default().with_title("A fantastic window!"); let window_attributes = WindowAttributes::default().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap()); let window = event_loop.create_window(window_attributes).unwrap();
let context = Context::new(event_loop.owned_display_handle()).unwrap();
self.surface = Some(Surface::new(&context, window).unwrap());
} }
fn window_event( fn window_event(
@@ -38,16 +42,16 @@ fn main() -> std::process::ExitCode {
) { ) {
info!("{event:?}"); info!("{event:?}");
let window = match self.window.as_ref() { let surface = match self.surface.as_mut() {
Some(window) => window, Some(surface) => surface,
None => return, None => return,
}; };
match event { match event {
WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
fill::fill_window(window.as_ref()); fill::fill(surface);
window.request_redraw(); surface.window().request_redraw();
}, },
_ => (), _ => (),
} }

View File

@@ -5,11 +5,12 @@
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
use std::time::Duration; use std::time::Duration;
use softbuffer::{Context, Surface};
use tracing::info; use tracing::info;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::WindowEvent;
use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand; use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand;
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::{Window, WindowAttributes, WindowId};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
@@ -17,27 +18,30 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[path = "util/tracing.rs"] #[path = "util/tracing.rs"]
mod tracing; mod tracing;
#[derive(Default, Debug)] #[derive(Debug)]
struct App { struct App {
context: Context<OwnedDisplayHandle>,
idx: usize, idx: usize,
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
window_id: Option<WindowId>, window_id: Option<WindowId>,
window: Option<Box<dyn Window>>,
} }
impl ApplicationHandler for App { impl ApplicationHandler for App {
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) { fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
if let Some(window) = self.window.as_ref() { if let Some(surface) = self.surface.as_ref() {
window.request_redraw(); surface.window().request_redraw();
} }
} }
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = WindowAttributes::default() let window_attributes = WindowAttributes::default()
.with_title("Fantastic window number one!") .with_title(format!("Fantastic window number {}!", self.idx))
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0)); .with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
let window = event_loop.create_window(window_attributes).unwrap(); let window = event_loop.create_window(window_attributes).unwrap();
self.window_id = Some(window.id()); self.window_id = Some(window.id());
self.window = Some(window);
let surface = Surface::new(&self.context, window).unwrap();
self.surface = Some(surface);
} }
fn window_event( fn window_event(
@@ -53,19 +57,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
return; return;
} }
let window = match self.window.as_mut() { let Some(surface) = self.surface.as_mut() else {
Some(window) => window, return;
None => return,
}; };
match event { match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
info!("Window {} CloseRequested", self.idx); info!("Window {} CloseRequested", self.idx);
fill::cleanup_window(window.as_ref()); self.surface = None;
self.window = None;
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
fill::fill_window(window.as_ref()); fill::fill(surface);
}, },
_ => (), _ => (),
} }
@@ -76,7 +78,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
let mut app = App { idx: 1, ..Default::default() }; let context = Context::new(event_loop.owned_display_handle()).unwrap();
let mut app = App { context, idx: 1, surface: None, window_id: None };
event_loop.run_app_on_demand(&mut app)?; event_loop.run_app_on_demand(&mut app)?;
info!("Finished first loop"); info!("Finished first loop");

View File

@@ -1,115 +1,43 @@
//! Fill the window buffer with a solid color.
//!
//! Launching a window without drawing to it has unpredictable results varying from platform to
//! platform. In order to have well-defined examples, this module provides an easy way to
//! fill the window buffer with a solid color.
//!
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
//! also be used to fill the window buffer, but they are more complicated to use.
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
use std::mem::ManuallyDrop;
use std::num::NonZeroU32; use std::num::NonZeroU32;
#[cfg(not(web_platform))]
use std::time::Instant;
use softbuffer::{Context, Surface}; use rwh_06::{HasDisplayHandle, HasWindowHandle};
#[cfg(web_platform)] use softbuffer::Surface;
use web_time::Instant; use winit::window::Window;
use winit::window::{Window, WindowId};
thread_local! { /// Resize the surface.
// NOTE: You should never do things like that, create context and drop it before pub fn resize(
// you drop the event loop. We do this for brevity to not blow up examples. We use surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle>,
// ManuallyDrop to prevent destructors from running. surface_size: dpi::PhysicalSize<u32>,
) {
// Handle zero-sized buffers.
// //
// A static, thread-local map of graphics contexts to open windows. // FIXME(madsmtm): This should be done by softbuffer internally in the future:
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) }; // https://github.com/rust-windowing/softbuffer/issues/238
let (Some(width), Some(height)) =
(NonZeroU32::new(surface_size.width), NonZeroU32::new(surface_size.height))
else {
return;
};
surface.resize(width, height).expect("Failed to resize the softbuffer surface");
} }
/// The graphics context used to draw to a window. /// Fill the window buffer with a solid color.
struct GraphicsContext { pub fn fill_with_color(
/// The global softbuffer context. surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle + AsRef<dyn Window>>,
context: RefCell<Context<&'static dyn Window>>, color: u32,
) {
let surface_size = surface.window().as_ref().surface_size();
resize(surface, surface_size);
/// The hash map of window IDs to surfaces. let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer");
surfaces: HashMap<WindowId, Surface<&'static dyn Window, &'static dyn Window>>, buffer.fill(color);
} buffer.present().expect("Failed to present the softbuffer buffer");
impl GraphicsContext {
fn new(w: &dyn Window) -> Self {
Self {
context: RefCell::new(
Context::new(unsafe { mem::transmute::<&'_ dyn Window, &'static dyn Window>(w) })
.expect("Failed to create a softbuffer context"),
),
surfaces: HashMap::new(),
}
}
fn create_surface(
&mut self,
window: &dyn Window,
) -> &mut Surface<&'static dyn Window, &'static dyn Window> {
self.surfaces.entry(window.id()).or_insert_with(|| {
Surface::new(&self.context.borrow(), unsafe {
mem::transmute::<&'_ dyn Window, &'static dyn Window>(window)
})
.expect("Failed to create a softbuffer surface")
})
}
fn destroy_surface(&mut self, window: &dyn Window) {
self.surfaces.remove(&window.id());
}
}
pub fn fill_window_with_color(window: &dyn Window, color: u32) {
GC.with(|gc| {
let size = window.surface_size();
let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
else {
return;
};
// Either get the last context used or create a new one.
let mut gc = gc.borrow_mut();
let surface = gc.get_or_insert_with(|| GraphicsContext::new(window)).create_surface(window);
// Fill a buffer with a solid color
surface.resize(width, height).expect("Failed to resize the softbuffer surface");
let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer");
buffer.fill(color);
buffer.present().expect("Failed to present the softbuffer buffer");
})
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn fill_window(window: &dyn Window) { pub fn fill(
fill_window_with_color(window, 0xff181818); surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle + AsRef<dyn Window>>,
} ) {
fill_with_color(surface, 0xff181818);
#[allow(dead_code)]
pub fn fill_window_with_animated_color(window: &dyn Window, start: Instant) {
let time = start.elapsed().as_secs_f32() * 1.5;
let blue = (time.sin() * 255.0) as u32;
let green = ((time.cos() * 255.0) as u32) << 8;
let red = ((1.0 - time.sin() * 255.0) as u32) << 16;
let color = red | green | blue;
fill_window_with_color(window, color);
}
#[allow(dead_code)]
pub fn cleanup_window(window: &dyn Window) {
GC.with(|gc| {
let mut gc = gc.borrow_mut();
if let Some(context) = gc.as_mut() {
context.destroy_surface(window);
}
});
} }

View File

@@ -2,10 +2,11 @@
use std::error::Error; use std::error::Error;
use tracing::{error, info}; use softbuffer::{Context, Surface};
use tracing::info;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
#[cfg(web_platform)] #[cfg(web_platform)]
use winit::platform::web::WindowAttributesWeb; use winit::platform::web::WindowAttributesWeb;
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::{Window, WindowAttributes, WindowId};
@@ -17,7 +18,7 @@ mod tracing;
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct App { struct App {
window: Option<Box<dyn Window>>, surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
} }
impl ApplicationHandler for App { impl ApplicationHandler for App {
@@ -27,14 +28,12 @@ impl ApplicationHandler for App {
#[cfg(web_platform)] #[cfg(web_platform)]
let window_attributes = WindowAttributes::default() let window_attributes = WindowAttributes::default()
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true))); .with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
self.window = match event_loop.create_window(window_attributes) { let window = event_loop.create_window(window_attributes).expect("failed creating window");
Ok(window) => Some(window),
Err(err) => { let context =
error!("error creating window: {err}"); Context::new(event_loop.owned_display_handle()).expect("failed creating context");
event_loop.exit(); let surface = Surface::new(&context, window).expect("failed creating surface");
return; self.surface = Some(surface);
},
}
} }
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) { fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) {
@@ -44,8 +43,10 @@ impl ApplicationHandler for App {
info!("Close was requested; stopping"); info!("Close was requested; stopping");
event_loop.exit(); event_loop.exit();
}, },
WindowEvent::SurfaceResized(_) => { WindowEvent::SurfaceResized(surface_size) => {
self.window.as_ref().expect("resize event without a window").request_redraw(); let surface = self.surface.as_mut().expect("resize event without a surface");
fill::resize(surface, surface_size);
surface.window().request_redraw();
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
// Redraw the application. // Redraw the application.
@@ -54,13 +55,15 @@ impl ApplicationHandler for App {
// this event rather than in AboutToWait, since rendering in here allows // this event rather than in AboutToWait, since rendering in here allows
// the program to gracefully handle redraws requested by the OS. // the program to gracefully handle redraws requested by the OS.
let window = self.window.as_ref().expect("redraw request without a window"); let surface = self.surface.as_mut().expect("redraw event without a surface");
// Notify that you're about to draw. // Notify that you're about to draw.
window.pre_present_notify(); surface.window().pre_present_notify();
// Draw. // Draw.
fill::fill_window(window.as_ref()); let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer");
buffer.fill(0xff181818);
buffer.present().expect("Failed to present the softbuffer buffer");
// For contiguous redraw loop you can request a redraw from here. // For contiguous redraw loop you can request a redraw from here.
// window.request_redraw(); // window.request_redraw();

View File

@@ -3,9 +3,10 @@ use std::error::Error;
#[cfg(x11_platform)] #[cfg(x11_platform)]
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
use softbuffer::{Context, Surface};
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
use winit::platform::x11::WindowAttributesX11; use winit::platform::x11::WindowAttributesX11;
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::{Window, WindowAttributes, WindowId};
@@ -17,7 +18,7 @@ fn main() -> Result<(), Box<dyn Error>> {
#[derive(Debug)] #[derive(Debug)]
pub struct XEmbedDemo { pub struct XEmbedDemo {
parent_window_id: u32, parent_window_id: u32,
window: Option<Box<dyn Window>>, surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
} }
impl ApplicationHandler for XEmbedDemo { impl ApplicationHandler for XEmbedDemo {
@@ -29,7 +30,10 @@ fn main() -> Result<(), Box<dyn Error>> {
WindowAttributesX11::default().with_embed_parent_window(self.parent_window_id); WindowAttributesX11::default().with_embed_parent_window(self.parent_window_id);
window_attributes = window_attributes.with_platform_attributes(Box::new(x11_attrs)); window_attributes = window_attributes.with_platform_attributes(Box::new(x11_attrs));
self.window = Some(event_loop.create_window(window_attributes).unwrap()); let window = event_loop.create_window(window_attributes).unwrap();
let context = Context::new(event_loop.owned_display_handle()).unwrap();
let surface = Surface::new(&context, window).unwrap();
self.surface = Some(surface);
} }
fn window_event( fn window_event(
@@ -38,19 +42,19 @@ fn main() -> Result<(), Box<dyn Error>> {
_window_id: WindowId, _window_id: WindowId,
event: WindowEvent, event: WindowEvent,
) { ) {
let window = self.window.as_ref().unwrap();
match event { match event {
WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
window.pre_present_notify(); let surface = self.surface.as_mut().unwrap();
fill::fill_window(window.as_ref()); surface.window().pre_present_notify();
fill::fill(surface);
}, },
_ => (), _ => (),
} }
} }
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) { fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
self.window.as_ref().unwrap().request_redraw(); self.surface.as_ref().unwrap().window().request_redraw();
} }
} }
@@ -63,7 +67,7 @@ fn main() -> Result<(), Box<dyn Error>> {
tracing::init(); tracing::init();
let event_loop = EventLoop::new()?; let event_loop = EventLoop::new()?;
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?) Ok(event_loop.run_app(XEmbedDemo { parent_window_id, surface: None })?)
} }
#[cfg(not(x11_platform))] #[cfg(not(x11_platform))]