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]
image = { workspace = true, features = ["png"] }
softbuffer.workspace = true
tracing = { workspace = true, features = ["log"] }
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]
winit-android.workspace = true

View File

@@ -15,7 +15,6 @@ use std::time::Instant;
use std::{fmt, mem};
use cursor_icon::CursorIcon;
use rwh_06::{DisplayHandle, HasDisplayHandle};
use softbuffer::{Context, Surface};
use tracing::{error, info};
#[cfg(web_platform)]
@@ -25,7 +24,7 @@ use winit::cursor::{Cursor, CustomCursor, CustomCursorSource};
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::error::RequestError;
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::keyboard::{Key, ModifiersState};
use winit::monitor::Fullscreen;
@@ -45,9 +44,6 @@ use winit_core::application::macos::ApplicationHandlerExtMacOS;
#[path = "util/tracing.rs"]
mod tracing_init;
#[path = "util/fill.rs"]
mod fill;
/// The amount of points to around the window for drag resize direction calculations.
const BORDER_SIZE: f64 = 20.;
@@ -94,20 +90,12 @@ struct Application {
/// Drawing context.
///
/// With OpenGL it could be EGLDisplay.
context: Option<Context<DisplayHandle<'static>>>,
context: Context<OwnedDisplayHandle>,
}
impl Application {
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 = Some(
Context::new(unsafe {
std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(
event_loop.display_handle().unwrap(),
)
})
.unwrap(),
);
let context = Context::new(event_loop.owned_display_handle()).unwrap();
// 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
@@ -596,9 +584,7 @@ impl ApplicationHandlerExtMacOS for Application {
/// State of the window.
struct WindowState {
/// Render surface.
///
/// NOTE: This surface must be dropped before the `Window`.
surface: Surface<DisplayHandle<'static>, Arc<dyn Window>>,
surface: Surface<OwnedDisplayHandle, Arc<dyn Window>>,
/// The actual winit Window.
window: Arc<dyn Window>,
/// 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>> {
let window: Arc<dyn Window> = Arc::from(window);
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
// it doesn't outlive it.
let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
let surface = Surface::new(&app.context, Arc::clone(&window))?;
let theme = window.theme().unwrap_or(Theme::Dark);
info!("Theme: {theme:?}");
@@ -928,35 +912,40 @@ impl WindowState {
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()?;
// Draw a different color inside the safe area
let surface_size = self.window.surface_size();
let insets = self.window.safe_area();
for y in 0..surface_size.height {
for x in 0..surface_size.width {
let index = y as usize * surface_size.width as usize + x as usize;
if insets.left <= x
&& x <= (surface_size.width - insets.right)
&& insets.top <= y
&& y <= (surface_size.height - insets.bottom)
{
// 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
};
if self.animated_fill_color {
// Fill the entire buffer with a single color.
let time = self.start_time.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;
buffer.fill(color);
} else {
// Draw a different color inside the safe area
let surface_size = self.window.surface_size();
let insets = self.window.safe_area();
for y in 0..surface_size.height {
for x in 0..surface_size.width {
let index = y as usize * surface_size.width as usize + x as usize;
if insets.left <= x
&& x <= (surface_size.width - insets.right)
&& insets.top <= y
&& y <= (surface_size.height - insets.bottom)
{
// 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> {
use std::collections::HashMap;
use softbuffer::{Context, Surface};
use tracing::info;
use winit::application::ApplicationHandler;
use winit::dpi::{LogicalPosition, LogicalSize, Position};
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::window::{Window, WindowAttributes, WindowId};
@@ -16,18 +17,20 @@ fn main() -> Result<(), impl std::error::Error> {
#[derive(Debug)]
struct WindowData {
window: Box<dyn Window>,
surface: Surface<OwnedDisplayHandle, Box<dyn Window>>,
color: u32,
}
impl WindowData {
fn new(window: Box<dyn Window>, color: u32) -> Self {
Self { window, color }
fn new(context: &Context<OwnedDisplayHandle>, window: Box<dyn Window>, color: u32) -> Self {
let surface = Surface::new(context, window).unwrap();
Self { surface, color }
}
}
#[derive(Default, Debug)]
#[derive(Debug)]
struct Application {
context: Context<OwnedDisplayHandle>,
parent_window_id: Option<WindowId>,
windows: HashMap<WindowId, WindowData>,
}
@@ -42,7 +45,7 @@ fn main() -> Result<(), impl std::error::Error> {
info!("Parent window id: {:?})", 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(
@@ -73,18 +76,24 @@ fn main() -> Result<(), impl std::error::Error> {
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 child_window =
spawn_child_window(parent_window.window.as_ref(), event_loop, child_index);
let child_window = spawn_child_window(
parent_window.surface.window().as_ref(),
event_loop,
child_index,
);
let child_id = child_window.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 => {
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() {
fill::fill_window(window.window.as_ref());
fill::fill(&mut window.surface);
} 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();
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)))]

View File

@@ -4,12 +4,13 @@ use std::thread;
#[cfg(not(web_platform))]
use std::time;
use softbuffer::{Context, Surface};
use tracing::{info, warn};
#[cfg(web_platform)]
use web_time as time;
use winit::application::ApplicationHandler;
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::window::{Window, WindowAttributes, WindowId};
@@ -52,7 +53,7 @@ struct ControlFlowDemo {
request_redraw: bool,
wait_cancelled: bool,
close_requested: bool,
window: Option<Box<dyn Window>>,
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
}
impl ApplicationHandler for ControlFlowDemo {
@@ -69,7 +70,10 @@ impl ApplicationHandler for ControlFlowDemo {
let window_attributes = WindowAttributes::default().with_title(
"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(
@@ -112,9 +116,9 @@ impl ApplicationHandler for ControlFlowDemo {
_ => (),
},
WindowEvent::RedrawRequested => {
let window = self.window.as_ref().unwrap();
window.pre_present_notify();
fill::fill_window(window.as_ref());
let surface = self.surface.as_mut().unwrap();
surface.window().pre_present_notify();
fill::fill(surface);
},
_ => (),
}
@@ -122,7 +126,7 @@ impl ApplicationHandler for ControlFlowDemo {
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
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 {

View File

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

View File

@@ -9,10 +9,11 @@ use std::cmp;
use std::error::Error;
use dpi::{LogicalPosition, PhysicalSize};
use softbuffer::{Context, Surface};
use tracing::{error, info};
use winit::application::ApplicationHandler;
use winit::event::{Ime, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
use winit::keyboard::{Key, ModifiersState, NamedKey};
#[cfg(web_platform)]
use winit::platform::web::WindowAttributesWeb;
@@ -30,7 +31,7 @@ const IME_CURSOR_SIZE: PhysicalSize<u32> = PhysicalSize::new(20, 20);
#[derive(Debug)]
struct App {
window: Option<Box<dyn Window>>,
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
input_state: TextInputState,
modifiers: ModifiersState,
}
@@ -73,14 +74,12 @@ impl ApplicationHandler for App {
#[cfg(web_platform)]
let window_attributes = WindowAttributes::default()
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
self.window = match event_loop.create_window(window_attributes) {
Ok(window) => Some(window),
Err(err) => {
error!("error creating window: {err}");
event_loop.exit();
return;
},
};
let window = event_loop.create_window(window_attributes).expect("failed creating window");
let context =
Context::new(event_loop.owned_display_handle()).expect("failed creating context");
let surface = Surface::new(&context, window).expect("failed creating surface");
self.surface = Some(surface);
// Allow IME out of the box.
let enable_request = ImeEnableRequest::new(
@@ -101,11 +100,13 @@ impl ApplicationHandler for App {
match event {
WindowEvent::CloseRequested => {
info!("Close was requested; stopping");
self.window = None;
self.surface = None;
event_loop.exit();
},
WindowEvent::SurfaceResized(_) => {
self.window.as_ref().expect("resize event without a window").request_redraw();
WindowEvent::SurfaceResized(surface_size) => {
let surface = self.surface.as_mut().expect("resize event without a surface");
fill::resize(surface, surface_size);
surface.window().request_redraw();
},
WindowEvent::RedrawRequested => {
// Redraw the application.
@@ -114,13 +115,13 @@ impl ApplicationHandler for App {
// this event rather than in AboutToWait, since rendering in here allows
// 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.
window.pre_present_notify();
surface.window().pre_present_notify();
// Draw.
fill::fill_window(window.as_ref());
fill::fill(surface);
// For contiguous redraw loop you can request a redraw from here.
// window.request_redraw();
@@ -214,14 +215,14 @@ impl App {
}
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 {
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::Commit(text) => {
self.input_state.append_text(&text);
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();
},
Ime::DeleteSurrounding { before_bytes, after_bytes } => {
@@ -246,7 +247,7 @@ impl App {
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 {
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 {
window: None,
surface: None,
input_state: TextInputState {
ime_enabled: true,
contents: String::new(),

View File

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

View File

@@ -5,11 +5,12 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
use std::time::Duration;
use softbuffer::{Context, Surface};
use tracing::info;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
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};
#[path = "util/fill.rs"]
@@ -17,27 +18,30 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[path = "util/tracing.rs"]
mod tracing;
#[derive(Default, Debug)]
#[derive(Debug)]
struct App {
context: Context<OwnedDisplayHandle>,
idx: usize,
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
window_id: Option<WindowId>,
window: Option<Box<dyn Window>>,
}
impl ApplicationHandler for App {
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
if let Some(surface) = self.surface.as_ref() {
surface.window().request_redraw();
}
}
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
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));
let window = event_loop.create_window(window_attributes).unwrap();
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(
@@ -53,19 +57,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
return;
}
let window = match self.window.as_mut() {
Some(window) => window,
None => return,
let Some(surface) = self.surface.as_mut() else {
return;
};
match event {
WindowEvent::CloseRequested => {
info!("Window {} CloseRequested", self.idx);
fill::cleanup_window(window.as_ref());
self.window = None;
self.surface = None;
},
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 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)?;
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;
#[cfg(not(web_platform))]
use std::time::Instant;
use softbuffer::{Context, Surface};
#[cfg(web_platform)]
use web_time::Instant;
use winit::window::{Window, WindowId};
use rwh_06::{HasDisplayHandle, HasWindowHandle};
use softbuffer::Surface;
use winit::window::Window;
thread_local! {
// NOTE: You should never do things like that, create context and drop it before
// you drop the event loop. We do this for brevity to not blow up examples. We use
// ManuallyDrop to prevent destructors from running.
/// Resize the surface.
pub fn resize(
surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle>,
surface_size: dpi::PhysicalSize<u32>,
) {
// Handle zero-sized buffers.
//
// A static, thread-local map of graphics contexts to open windows.
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) };
// FIXME(madsmtm): This should be done by softbuffer internally in the future:
// 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.
struct GraphicsContext {
/// The global softbuffer context.
context: RefCell<Context<&'static dyn Window>>,
/// Fill the window buffer with a solid color.
pub fn fill_with_color(
surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle + AsRef<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.
surfaces: HashMap<WindowId, Surface<&'static dyn Window, &'static dyn Window>>,
}
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");
})
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)]
pub fn fill_window(window: &dyn Window) {
fill_window_with_color(window, 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);
}
});
pub fn fill(
surface: &mut Surface<impl HasDisplayHandle, impl HasWindowHandle + AsRef<dyn Window>>,
) {
fill_with_color(surface, 0xff181818);
}

View File

@@ -2,10 +2,11 @@
use std::error::Error;
use tracing::{error, info};
use softbuffer::{Context, Surface};
use tracing::info;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
#[cfg(web_platform)]
use winit::platform::web::WindowAttributesWeb;
use winit::window::{Window, WindowAttributes, WindowId};
@@ -17,7 +18,7 @@ mod tracing;
#[derive(Default, Debug)]
struct App {
window: Option<Box<dyn Window>>,
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
}
impl ApplicationHandler for App {
@@ -27,14 +28,12 @@ impl ApplicationHandler for App {
#[cfg(web_platform)]
let window_attributes = WindowAttributes::default()
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
self.window = match event_loop.create_window(window_attributes) {
Ok(window) => Some(window),
Err(err) => {
error!("error creating window: {err}");
event_loop.exit();
return;
},
}
let window = event_loop.create_window(window_attributes).expect("failed creating window");
let context =
Context::new(event_loop.owned_display_handle()).expect("failed creating context");
let surface = Surface::new(&context, window).expect("failed creating surface");
self.surface = Some(surface);
}
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");
event_loop.exit();
},
WindowEvent::SurfaceResized(_) => {
self.window.as_ref().expect("resize event without a window").request_redraw();
WindowEvent::SurfaceResized(surface_size) => {
let surface = self.surface.as_mut().expect("resize event without a surface");
fill::resize(surface, surface_size);
surface.window().request_redraw();
},
WindowEvent::RedrawRequested => {
// Redraw the application.
@@ -54,13 +55,15 @@ impl ApplicationHandler for App {
// this event rather than in AboutToWait, since rendering in here allows
// 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.
window.pre_present_notify();
surface.window().pre_present_notify();
// 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.
// window.request_redraw();

View File

@@ -3,9 +3,10 @@ use std::error::Error;
#[cfg(x11_platform)]
fn main() -> Result<(), Box<dyn Error>> {
use softbuffer::{Context, Surface};
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle};
use winit::platform::x11::WindowAttributesX11;
use winit::window::{Window, WindowAttributes, WindowId};
@@ -17,7 +18,7 @@ fn main() -> Result<(), Box<dyn Error>> {
#[derive(Debug)]
pub struct XEmbedDemo {
parent_window_id: u32,
window: Option<Box<dyn Window>>,
surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
}
impl ApplicationHandler for XEmbedDemo {
@@ -29,7 +30,10 @@ fn main() -> Result<(), Box<dyn Error>> {
WindowAttributesX11::default().with_embed_parent_window(self.parent_window_id);
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(
@@ -38,19 +42,19 @@ fn main() -> Result<(), Box<dyn Error>> {
_window_id: WindowId,
event: WindowEvent,
) {
let window = self.window.as_ref().unwrap();
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
window.pre_present_notify();
fill::fill_window(window.as_ref());
let surface = self.surface.as_mut().unwrap();
surface.window().pre_present_notify();
fill::fill(surface);
},
_ => (),
}
}
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();
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))]