Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions platforms/winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,10 @@ version = "0.30.5"
default-features = false
features = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]

[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
softbuffer = { version = "0.4.0", default-features = false, features = [
"x11",
"x11-dlopen",
"wayland",
"wayland-dlopen",
] }
11 changes: 10 additions & 1 deletion platforms/winit/examples/mixed_handlers.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[path = "util/fill.rs"]
mod fill;

use accesskit::{
Action, ActionRequest, ActivationHandler, Live, Node, NodeId, Rect, Role, Tree, TreeUpdate,
};
Expand Down Expand Up @@ -205,8 +208,12 @@ impl ApplicationHandler<AccessKitEvent> for Application {
adapter.process_event(&window.window, &event);
match event {
WindowEvent::CloseRequested => {
fill::cleanup_window(&window.window);
self.window = None;
}
WindowEvent::RedrawRequested => {
fill::fill_window(&window.window);
}
WindowEvent::KeyboardInput {
event:
KeyEvent {
Expand Down Expand Up @@ -270,7 +277,9 @@ impl ApplicationHandler<AccessKitEvent> for Application {
}

fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_none() {
if let Some(window) = self.window.as_ref() {
window.window.request_redraw();
} else {
event_loop.exit();
}
}
Expand Down
11 changes: 10 additions & 1 deletion platforms/winit/examples/simple.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[path = "util/fill.rs"]
mod fill;

use accesskit::{Action, ActionRequest, Live, Node, NodeId, Rect, Role, Tree, TreeUpdate};
use accesskit_winit::{Adapter, Event as AccessKitEvent, WindowEvent as AccessKitWindowEvent};
use std::error::Error;
Expand Down Expand Up @@ -182,8 +185,12 @@ impl ApplicationHandler<AccessKitEvent> for Application {
adapter.process_event(&window.window, &event);
match event {
WindowEvent::CloseRequested => {
fill::cleanup_window(&window.window);
self.window = None;
}
WindowEvent::RedrawRequested => {
fill::fill_window(&window.window);
}
WindowEvent::KeyboardInput {
event:
KeyEvent {
Expand Down Expand Up @@ -246,7 +253,9 @@ impl ApplicationHandler<AccessKitEvent> for Application {
}

fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_none() {
if let Some(window) = self.window.as_ref() {
window.window.request_redraw();
} else {
event_loop.exit();
}
}
Expand Down
123 changes: 123 additions & 0 deletions platforms/winit/examples/util/fill.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Adapted from winit's examples/util/fill.rs.

//! 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.

pub use platform::cleanup_window;
pub use platform::fill_window;

#[cfg(not(any(target_os = "android", target_os = "ios")))]
mod platform {
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
use std::mem::ManuallyDrop;
use std::num::NonZeroU32;

use softbuffer::{Context, Surface};
use winit::window::{Window, WindowId};

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.
//
// A static, thread-local map of graphics contexts to open windows.
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) };
}

/// The graphics context used to draw to a window.
struct GraphicsContext {
/// The global softbuffer context.
context: RefCell<Context<&'static Window>>,

/// The hash map of window IDs to surfaces.
surfaces: HashMap<WindowId, Surface<&'static Window, &'static Window>>,
}

impl GraphicsContext {
fn new(w: &Window) -> Self {
Self {
context: RefCell::new(
Context::new(unsafe { mem::transmute::<&'_ Window, &'static Window>(w) })
.expect("Failed to create a softbuffer context"),
),
surfaces: HashMap::new(),
}
}

fn create_surface(
&mut self,
window: &Window,
) -> &mut Surface<&'static Window, &'static Window> {
self.surfaces.entry(window.id()).or_insert_with(|| {
Surface::new(&self.context.borrow(), unsafe {
mem::transmute::<&'_ Window, &'static Window>(window)
})
.expect("Failed to create a softbuffer surface")
})
}

fn destroy_surface(&mut self, window: &Window) {
self.surfaces.remove(&window.id());
}
}

pub fn fill_window(window: &Window) {
GC.with(|gc| {
let size = window.inner_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.
const DARK_GRAY: u32 = 0xff181818;

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(DARK_GRAY);
buffer
.present()
.expect("Failed to present the softbuffer buffer");
})
}

pub fn cleanup_window(window: &Window) {
GC.with(|gc| {
let mut gc = gc.borrow_mut();
if let Some(context) = gc.as_mut() {
context.destroy_surface(window);
}
});
}
}

#[cfg(any(target_os = "android", target_os = "ios"))]
mod platform {
pub fn fill_window(_window: &winit::window::Window) {
// No-op on mobile platforms.
}

pub fn cleanup_window(_window: &winit::window::Window) {
// No-op on mobile platforms.
}
}