diff --git a/winit/Cargo.toml b/winit/Cargo.toml index d1836b50db..af8c94877c 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -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 diff --git a/winit/examples/application.rs b/winit/examples/application.rs index 4098ebff68..ae7dd8119c 100644 --- a/winit/examples/application.rs +++ b/winit/examples/application.rs @@ -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: Context, } impl Application { fn new(event_loop: &EventLoop, receiver: Receiver, sender: Sender) -> 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<'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 @@ -605,9 +593,7 @@ impl ApplicationHandlerExtMacOS for Application { /// State of the window. struct WindowState { /// Render surface. - /// - /// NOTE: This surface must be dropped before the `Window`. - surface: Surface, Arc>, + surface: Surface>, /// The actual winit Window. window: Arc, /// The window theme we're drawing with. @@ -648,9 +634,7 @@ impl WindowState { fn new(app: &Application, window: Box) -> Result> { let window: Arc = 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:?}"); @@ -937,35 +921,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 + }; + } } } } diff --git a/winit/examples/child_window.rs b/winit/examples/child_window.rs index 4077bca36b..228802a46b 100644 --- a/winit/examples/child_window.rs +++ b/winit/examples/child_window.rs @@ -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, + surface: Surface>, color: u32, } impl WindowData { - fn new(window: Box, color: u32) -> Self { - Self { window, color } + fn new(context: &Context, window: Box, color: u32) -> Self { + let surface = Surface::new(context, window).unwrap(); + Self { surface, color } } } - #[derive(Default, Debug)] + #[derive(Debug)] struct Application { + context: Context, parent_window_id: Option, windows: HashMap, } @@ -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)))] diff --git a/winit/examples/control_flow.rs b/winit/examples/control_flow.rs index 04e6a04d62..ff9917bfe3 100644 --- a/winit/examples/control_flow.rs +++ b/winit/examples/control_flow.rs @@ -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>, + surface: Option>>, } 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 { diff --git a/winit/examples/dnd.rs b/winit/examples/dnd.rs index c8d0332656..903034f927 100644 --- a/winit/examples/dnd.rs +++ b/winit/examples/dnd.rs @@ -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> { 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>, -} - -impl Application { - fn new() -> Self { - Self { window: None } - } + surface: Option>>, } 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(); diff --git a/winit/examples/ime.rs b/winit/examples/ime.rs index 1aee54f111..fd1ffbd407 100644 --- a/winit/examples/ime.rs +++ b/winit/examples/ime.rs @@ -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 = PhysicalSize::new(20, 20); #[derive(Debug)] struct App { - window: Option>, + surface: Option>>, 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(), diff --git a/winit/examples/pump_events.rs b/winit/examples/pump_events.rs index 181fb366af..782c7b6788 100644 --- a/winit/examples/pump_events.rs +++ b/winit/examples/pump_events.rs @@ -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>, + surface: Option>>, } 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(); }, _ => (), } diff --git a/winit/examples/run_on_demand.rs b/winit/examples/run_on_demand.rs index a9df3cff9e..dcfe8435d1 100644 --- a/winit/examples/run_on_demand.rs +++ b/winit/examples/run_on_demand.rs @@ -5,11 +5,12 @@ fn main() -> Result<(), Box> { 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> { #[path = "util/tracing.rs"] mod tracing; - #[derive(Default, Debug)] + #[derive(Debug)] struct App { + context: Context, idx: usize, + surface: Option>>, window_id: Option, - window: Option>, } 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> { 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> { 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"); diff --git a/winit/examples/util/fill.rs b/winit/examples/util/fill.rs index ff3ca198d8..7f51db3462 100644 --- a/winit/examples/util/fill.rs +++ b/winit/examples/util/fill.rs @@ -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, + surface_size: dpi::PhysicalSize, +) { + // Handle zero-sized buffers. // - // A static, thread-local map of graphics contexts to open windows. - static GC: ManuallyDrop>> = const { ManuallyDrop::new(RefCell::new(None)) }; -} - -/// The graphics context used to draw to a window. -struct GraphicsContext { - /// The global softbuffer context. - context: RefCell>, - - /// The hash map of window IDs to surfaces. - surfaces: HashMap>, -} - -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"); - }) + // 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"); } -#[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); +/// Fill the window buffer with a solid color. +pub fn fill_with_color( + surface: &mut Surface>, + color: u32, +) { + let surface_size = surface.window().as_ref().surface_size(); + resize(surface, surface_size); + + 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 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>, +) { + fill_with_color(surface, 0xff181818); } diff --git a/winit/examples/window.rs b/winit/examples/window.rs index 7d9fc59f4c..f0a8ba9c92 100644 --- a/winit/examples/window.rs +++ b/winit/examples/window.rs @@ -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>, + surface: Option>>, } 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(); diff --git a/winit/examples/x11_embed.rs b/winit/examples/x11_embed.rs index b25d6ff39d..ca7a3d31ab 100644 --- a/winit/examples/x11_embed.rs +++ b/winit/examples/x11_embed.rs @@ -3,9 +3,10 @@ use std::error::Error; #[cfg(x11_platform)] fn main() -> Result<(), Box> { + 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> { #[derive(Debug)] pub struct XEmbedDemo { parent_window_id: u32, - window: Option>, + surface: Option>>, } impl ApplicationHandler for XEmbedDemo { @@ -29,7 +30,10 @@ fn main() -> Result<(), Box> { 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> { _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> { 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))]