mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
Add rendering callbacks to sixtyfps::Window
This API allows specifying a callback that will be invoked when setting up graphics (great for compiling shaders), before rendering a frame (but after the clearning of the surface background), after rendering a frame (before swapbuffers) and when releasing graphics resources.
This commit is contained in:
parent
54d9ebdc19
commit
8959eac3d0
9 changed files with 295 additions and 25 deletions
|
@ -28,6 +28,7 @@ as well as the [Rust migration guide for the `sixtyfps` crate](api/sixtyfps-rs/m
|
|||
### Added
|
||||
|
||||
- `TextEdit::font-size` and `LineEdit::font-size` have been added to control the size of these widgets.
|
||||
- Added `sixtyfps::Window::set_rendering_notifier` to get a callback before and after a new frame is being rendered.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -128,6 +128,9 @@ fn gen_corelib(
|
|||
.map(|x| x.to_string())
|
||||
.collect();
|
||||
|
||||
let mut private_exported_types: std::collections::HashSet<String> =
|
||||
config.export.include.iter().cloned().collect();
|
||||
|
||||
config.export.exclude = [
|
||||
"SharedString",
|
||||
"SharedVector",
|
||||
|
@ -157,8 +160,10 @@ fn gen_corelib(
|
|||
"sixtyfps_color_darker",
|
||||
"sixtyfps_image_size",
|
||||
"sixtyfps_image_path",
|
||||
"TimerMode", // included in generated_public.h
|
||||
"IntSize", // included in generated_public.h
|
||||
"TimerMode", // included in generated_public.h
|
||||
"IntSize", // included in generated_public.h
|
||||
"RenderingState", // included in generated_public.h
|
||||
"SetRenderingNotifierError", // included in generated_public.h
|
||||
]
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
|
@ -201,6 +206,7 @@ fn gen_corelib(
|
|||
.insert("StateInfo".to_owned(), " using Instant = uint64_t;".into());
|
||||
properties_config.structure.derive_eq = true;
|
||||
properties_config.structure.derive_neq = true;
|
||||
private_exported_types.extend(properties_config.export.include.iter().cloned());
|
||||
cbindgen::Builder::new()
|
||||
.with_config(properties_config)
|
||||
.with_src(crate_dir.join("properties.rs"))
|
||||
|
@ -261,6 +267,7 @@ fn gen_corelib(
|
|||
"sixtyfps_windowrc_set_focus_item",
|
||||
"sixtyfps_windowrc_set_component",
|
||||
"sixtyfps_windowrc_show_popup",
|
||||
"sixtyfps_windowrc_set_rendering_notifier",
|
||||
"sixtyfps_new_path_elements",
|
||||
"sixtyfps_new_path_events",
|
||||
"sixtyfps_color_brighter",
|
||||
|
@ -289,6 +296,9 @@ fn gen_corelib(
|
|||
// Property<> fields uses the public `sixtyfps::Blah` type
|
||||
special_config.namespaces =
|
||||
Some(vec!["sixtyfps".into(), "cbindgen_private".into(), "types".into()]);
|
||||
|
||||
private_exported_types.extend(special_config.export.include.iter().cloned());
|
||||
|
||||
cbindgen::Builder::new()
|
||||
.with_config(special_config)
|
||||
.with_src(crate_dir.join("graphics.rs"))
|
||||
|
@ -309,8 +319,15 @@ fn gen_corelib(
|
|||
let mut public_config = config.clone();
|
||||
public_config.namespaces = Some(vec!["sixtyfps".into()]);
|
||||
public_config.export.item_types = vec![cbindgen::ItemType::Enums, cbindgen::ItemType::Structs];
|
||||
public_config.export.include = vec!["TimerMode".into(), "IntSize".into()];
|
||||
public_config.export.exclude.clear();
|
||||
// Previously included types are now excluded (to avoid duplicates)
|
||||
public_config.export.exclude = private_exported_types.into_iter().collect();
|
||||
public_config.export.exclude.push("Point".into());
|
||||
public_config.export.include = vec![
|
||||
"TimerMode".into(),
|
||||
"IntSize".into(),
|
||||
"RenderingState".into(),
|
||||
"SetRenderingNotifierError".into(),
|
||||
];
|
||||
|
||||
public_config.export.body.insert(
|
||||
"IntSize".to_owned(),
|
||||
|
@ -324,6 +341,8 @@ fn gen_corelib(
|
|||
.with_config(public_config)
|
||||
.with_src(crate_dir.join("timers.rs"))
|
||||
.with_src(crate_dir.join("graphics.rs"))
|
||||
.with_src(crate_dir.join("window.rs"))
|
||||
.with_src(crate_dir.join("api.rs"))
|
||||
.with_after_include(format!(
|
||||
r"
|
||||
/// This macro expands to the to the numeric value of the major version of SixtyFPS you're
|
||||
|
|
|
@ -141,6 +141,23 @@ public:
|
|||
cbindgen_private::sixtyfps_windowrc_show_popup(&inner, &popup, p, &parent_item);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
std::optional<SetRenderingNotifierError> set_rendering_notifier(F callback) const
|
||||
{
|
||||
auto actual_cb = [](RenderingState state, void *data) {
|
||||
(*reinterpret_cast<F *>(data))(state);
|
||||
};
|
||||
SetRenderingNotifierError err;
|
||||
if (cbindgen_private::sixtyfps_windowrc_set_rendering_notifier(
|
||||
&inner, actual_cb,
|
||||
[](void *user_data) { delete reinterpret_cast<F *>(user_data); },
|
||||
new F(std::move(callback)), &err)) {
|
||||
return {};
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
cbindgen_private::WindowRcOpaque inner;
|
||||
};
|
||||
|
@ -314,6 +331,16 @@ public:
|
|||
/// De-registers the window from the windowing system, therefore hiding it.
|
||||
void hide() { inner.hide(); }
|
||||
|
||||
/// This function allows registering a callback that's invoked during the different phases of
|
||||
/// rendering. This allows custom rendering on top or below of the scene.
|
||||
/// On success, the function returns a std::optional without value. On error, the function
|
||||
/// returns the error code as value in the std::optional.
|
||||
template<typename F>
|
||||
std::optional<SetRenderingNotifierError> set_rendering_notifier(F &&callback) const
|
||||
{
|
||||
return inner.set_rendering_notifier(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
/// \private
|
||||
private_api::WindowRc &window_handle() { return inner; }
|
||||
/// \private
|
||||
|
|
|
@ -16,7 +16,7 @@ enum OpenGLContextState {
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
Current(glutin::WindowedContext<glutin::PossiblyCurrent>),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
Current(Rc<winit::window::Window>),
|
||||
Current { window: Rc<winit::window::Window>, canvas: web_sys::HtmlCanvasElement },
|
||||
}
|
||||
|
||||
pub struct OpenGLContext(RefCell<Option<OpenGLContextState>>);
|
||||
|
@ -29,7 +29,14 @@ impl OpenGLContext {
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
OpenGLContextState::Current(context) => context.window(),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
OpenGLContextState::Current(window) => window.as_ref(),
|
||||
OpenGLContextState::Current { window, .. } => window.as_ref(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn html_canvas_element(&self) -> std::cell::Ref<web_sys::HtmlCanvasElement> {
|
||||
std::cell::Ref::map(self.0.borrow(), |state| match state.as_ref().unwrap() {
|
||||
OpenGLContextState::Current { canvas, .. } => canvas,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -41,7 +48,7 @@ impl OpenGLContext {
|
|||
let current_ctx = unsafe { not_current_ctx.make_current().unwrap() };
|
||||
OpenGLContextState::Current(current_ctx)
|
||||
}
|
||||
state @ OpenGLContextState::Current(_) => state,
|
||||
state @ OpenGLContextState::Current { .. } => state,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -60,12 +67,12 @@ impl OpenGLContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with_current_context<T>(&self, cb: impl FnOnce() -> T) -> T {
|
||||
if matches!(self.0.borrow().as_ref().unwrap(), OpenGLContextState::Current(_)) {
|
||||
cb()
|
||||
pub fn with_current_context<T>(&self, cb: impl FnOnce(&Self) -> T) -> T {
|
||||
if matches!(self.0.borrow().as_ref().unwrap(), OpenGLContextState::Current { .. }) {
|
||||
cb(self)
|
||||
} else {
|
||||
self.make_current();
|
||||
let result = cb();
|
||||
let result = cb(self);
|
||||
self.make_not_current();
|
||||
result
|
||||
}
|
||||
|
@ -261,7 +268,15 @@ impl OpenGLContext {
|
|||
|
||||
let renderer =
|
||||
femtovg::renderer::OpenGl::new_from_html_canvas(&window.canvas()).unwrap();
|
||||
(Self(RefCell::new(Some(OpenGLContextState::Current(window)))), renderer)
|
||||
(Self(RefCell::new(Some(OpenGLContextState::Current { window, canvas }))), renderer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn get_proc_address(&self, name: &str) -> *const std::ffi::c_void {
|
||||
match &self.0.borrow().as_ref().unwrap() {
|
||||
OpenGLContextState::NotCurrent(_) => std::ptr::null(),
|
||||
OpenGLContextState::Current(current_ctx) => current_ctx.get_proc_address(name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ use std::rc::{Rc, Weak};
|
|||
|
||||
use super::{ItemGraphicsCache, TextureCache};
|
||||
use crate::event_loop::WinitWindow;
|
||||
use crate::glcontext::OpenGLContext;
|
||||
use const_field_offset::FieldOffsets;
|
||||
use corelib::api::{GraphicsAPI, RenderingNotifier, RenderingState, SetRenderingNotifierError};
|
||||
use corelib::component::ComponentRc;
|
||||
use corelib::graphics::*;
|
||||
use corelib::input::KeyboardModifiers;
|
||||
|
@ -38,6 +40,8 @@ pub struct GLWindow {
|
|||
|
||||
fps_counter: Option<Rc<FPSCounter>>,
|
||||
|
||||
rendering_notifier: RefCell<Option<Box<dyn RenderingNotifier>>>,
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
canvas_id: String,
|
||||
}
|
||||
|
@ -61,16 +65,17 @@ impl GLWindow {
|
|||
graphics_cache: Default::default(),
|
||||
texture_cache: Default::default(),
|
||||
fps_counter: FPSCounter::new(),
|
||||
rendering_notifier: Default::default(),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
canvas_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn with_current_context<T>(&self, cb: impl FnOnce() -> T) -> T {
|
||||
fn with_current_context<T>(&self, cb: impl FnOnce(&OpenGLContext) -> T) -> Option<T> {
|
||||
match &*self.map_state.borrow() {
|
||||
GraphicsWindowBackendState::Unmapped => cb(),
|
||||
GraphicsWindowBackendState::Unmapped => None,
|
||||
GraphicsWindowBackendState::Mapped(window) => {
|
||||
window.opengl_context.with_current_context(cb)
|
||||
Some(window.opengl_context.with_current_context(cb))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +113,38 @@ impl GLWindow {
|
|||
pub fn default_font_properties(&self) -> FontRequest {
|
||||
self.self_weak.upgrade().unwrap().default_font_properties()
|
||||
}
|
||||
|
||||
fn release_graphics_resources(&self) {
|
||||
// Release GL textures and other GPU bound resources.
|
||||
self.with_current_context(|context| {
|
||||
self.graphics_cache.borrow_mut().clear();
|
||||
self.texture_cache.borrow_mut().clear();
|
||||
|
||||
self.invoke_rendering_notifier(RenderingState::RenderingTeardown, context);
|
||||
});
|
||||
}
|
||||
|
||||
/// Invoke any registered rendering notifiers about the state the backend renderer is currently in.
|
||||
fn invoke_rendering_notifier(&self, state: RenderingState, opengl_context: &OpenGLContext) {
|
||||
if let Some(callback) = self.rendering_notifier.borrow_mut().as_mut() {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let api = GraphicsAPI::NativeOpenGL {
|
||||
get_proc_address: &|name| opengl_context.get_proc_address(name),
|
||||
};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let canvas_element_id = opengl_context.html_canvas_element().id();
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let api = GraphicsAPI::WebGL {
|
||||
canvas_element_id: canvas_element_id.as_str(),
|
||||
context_type: "webgl",
|
||||
};
|
||||
callback.notify(state, &api)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_rendering_notifier(&self) -> bool {
|
||||
self.rendering_notifier.borrow().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl WinitWindow for GLWindow {
|
||||
|
@ -127,7 +164,7 @@ impl WinitWindow for GLWindow {
|
|||
fn draw(self: Rc<Self>) {
|
||||
let runtime_window = self.self_weak.upgrade().unwrap();
|
||||
let scale_factor = runtime_window.scale_factor();
|
||||
runtime_window.draw_contents(|components| {
|
||||
runtime_window.clone().draw_contents(|components| {
|
||||
let window = match self.borrow_mapped_window() {
|
||||
Some(window) => window,
|
||||
None => return, // caller bug, doesn't make sense to call draw() when not mapped
|
||||
|
@ -151,6 +188,18 @@ impl WinitWindow for GLWindow {
|
|||
size.height,
|
||||
crate::to_femtovg_color(&window.clear_color),
|
||||
);
|
||||
// For the BeforeRendering rendering notifier callback it's important that this happens *after* clearing
|
||||
// the back buffer, in order to allow the callback to provide its own rendering of the background.
|
||||
// femtovg's clear_rect() will merely schedule a clear call, so flush right away to make it immediate.
|
||||
if self.has_rendering_notifier() {
|
||||
canvas.flush();
|
||||
canvas.set_size(size.width, size.height, 1.0);
|
||||
|
||||
self.invoke_rendering_notifier(
|
||||
RenderingState::BeforeRendering,
|
||||
&window.opengl_context,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut renderer = crate::GLItemRenderer {
|
||||
|
@ -184,6 +233,8 @@ impl WinitWindow for GLWindow {
|
|||
|
||||
drop(renderer);
|
||||
|
||||
self.invoke_rendering_notifier(RenderingState::AfterRendering, &window.opengl_context);
|
||||
|
||||
window.opengl_context.swap_buffers();
|
||||
window.opengl_context.make_not_current();
|
||||
});
|
||||
|
@ -250,7 +301,7 @@ impl PlatformWindow for GLWindow {
|
|||
})
|
||||
.peekable();
|
||||
if cache_entries_to_clear.peek().is_some() {
|
||||
self.with_current_context(|| {
|
||||
self.with_current_context(|_| {
|
||||
cache_entries_to_clear.for_each(drop);
|
||||
});
|
||||
}
|
||||
|
@ -258,6 +309,20 @@ impl PlatformWindow for GLWindow {
|
|||
}
|
||||
}
|
||||
|
||||
/// This function is called through the public API to register a callback that the backend needs to invoke during
|
||||
/// different phases of rendering.
|
||||
fn set_rendering_notifier(
|
||||
&self,
|
||||
callback: Box<dyn RenderingNotifier>,
|
||||
) -> std::result::Result<(), SetRenderingNotifierError> {
|
||||
let mut notifier = self.rendering_notifier.borrow_mut();
|
||||
if notifier.replace(callback).is_some() {
|
||||
Err(SetRenderingNotifierError::AlreadySet)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn show_popup(&self, popup: &ComponentRc, position: Point) {
|
||||
let runtime_window = self.self_weak.upgrade().unwrap();
|
||||
let size = runtime_window.set_active_popup(PopupWindow {
|
||||
|
@ -383,6 +448,8 @@ impl PlatformWindow for GLWindow {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
self.invoke_rendering_notifier(RenderingState::RenderingSetup, &opengl_context);
|
||||
|
||||
opengl_context.make_not_current();
|
||||
|
||||
let canvas = Rc::new(RefCell::new(canvas));
|
||||
|
@ -443,10 +510,7 @@ impl PlatformWindow for GLWindow {
|
|||
|
||||
fn hide(self: Rc<Self>) {
|
||||
// Release GL textures and other GPU bound resources.
|
||||
self.with_current_context(|| {
|
||||
self.graphics_cache.borrow_mut().clear();
|
||||
self.texture_cache.borrow_mut().clear();
|
||||
});
|
||||
self.release_graphics_resources();
|
||||
|
||||
self.map_state.replace(GraphicsWindowBackendState::Unmapped);
|
||||
/* FIXME:
|
||||
|
@ -621,6 +685,12 @@ impl PlatformWindow for GLWindow {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for GLWindow {
|
||||
fn drop(&mut self) {
|
||||
self.release_graphics_resources();
|
||||
}
|
||||
}
|
||||
|
||||
struct MappedWindow {
|
||||
canvas: Option<CanvasRc>,
|
||||
opengl_context: crate::OpenGLContext,
|
||||
|
@ -632,7 +702,7 @@ impl Drop for MappedWindow {
|
|||
fn drop(&mut self) {
|
||||
if let Some(canvas) = self.canvas.take().map(|canvas| Rc::try_unwrap(canvas).ok()) {
|
||||
// The canvas must be destructed with a GL context current, in order to clean up correctly
|
||||
self.opengl_context.with_current_context(|| {
|
||||
self.opengl_context.with_current_context(|_| {
|
||||
drop(canvas);
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -123,6 +123,7 @@ mod the_backend {
|
|||
_items: &mut dyn Iterator<Item = Pin<sixtyfps_corelib::items::ItemRef<'a>>>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn show_popup(&self, _popup: &ComponentRc, _position: sixtyfps_corelib::graphics::Point) {
|
||||
todo!()
|
||||
}
|
||||
|
|
|
@ -224,8 +224,8 @@ impl WinitWindow for SimulatorWindow {
|
|||
|
||||
let size = self.opengl_context.window().inner_size();
|
||||
|
||||
self.opengl_context.with_current_context(|| {
|
||||
self.opengl_context.ensure_resized();
|
||||
self.opengl_context.with_current_context(|opengl_context| {
|
||||
opengl_context.ensure_resized();
|
||||
|
||||
{
|
||||
let mut canvas = self.canvas.borrow_mut();
|
||||
|
@ -294,7 +294,7 @@ impl WinitWindow for SimulatorWindow {
|
|||
canvas.flush();
|
||||
canvas.delete_image(image_id);
|
||||
|
||||
self.opengl_context.swap_buffers();
|
||||
opengl_context.swap_buffers();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,83 @@ use std::rc::Rc;
|
|||
use crate::component::ComponentVTable;
|
||||
use crate::window::WindowRc;
|
||||
|
||||
/// This enum describes a low-level access to specific graphcis APIs used
|
||||
/// by the renderer.
|
||||
#[derive(Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum GraphicsAPI<'a> {
|
||||
/// The rendering is done using OpenGL.
|
||||
NativeOpenGL {
|
||||
/// Use this function pointer to obtain access to the OpenGL implementation - similar to `eglGetProcAddress`.
|
||||
get_proc_address: &'a dyn Fn(&str) -> *const std::ffi::c_void,
|
||||
},
|
||||
/// The rendering is done on a HTML Canvas element using WebGL.
|
||||
WebGL {
|
||||
/// The DOM element id of the HTML Canvas element used for rendering.
|
||||
canvas_element_id: &'a str,
|
||||
/// The drawing context type used on the HTML Canvas element for rendering. This is the argument to the
|
||||
/// `getContext` function on the HTML Canvas element.
|
||||
context_type: &'a str,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for GraphicsAPI<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
GraphicsAPI::NativeOpenGL { .. } => write!(f, "GraphicsAPI::NativeOpenGL"),
|
||||
GraphicsAPI::WebGL { context_type, .. } => {
|
||||
write!(f, "GraphicsAPI::WebGL(context_type = {})", context_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This enum describes the different rendering states, that will be provided
|
||||
/// to the parameter of the callback for `set_rendering_notifier` on the `Window`.
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
#[non_exhaustive]
|
||||
pub enum RenderingState {
|
||||
/// The window has been created and the graphics adapter/context initialized. When OpenGL
|
||||
/// is used for rendering, the context will be current.
|
||||
RenderingSetup,
|
||||
/// The scene of items is about to be rendered. When OpenGL
|
||||
/// is used for rendering, the context will be current.
|
||||
BeforeRendering,
|
||||
/// The scene of items was rendered, but the back buffer was not sent for display presentation
|
||||
/// yet (for example GL swap buffers). When OpenGL is used for rendering, the context will be current.
|
||||
AfterRendering,
|
||||
/// The window will be destroyed and/or graphics resources need to be released due to other
|
||||
/// constraints.
|
||||
RenderingTeardown,
|
||||
}
|
||||
|
||||
/// Internal trait that's used to map rendering state callbacks to either a Rust-API provided
|
||||
/// impl FnMut or a struct that invokes a C callback and implements Drop to release the closure
|
||||
/// on the C++ side.
|
||||
pub trait RenderingNotifier {
|
||||
/// Called to notify that rendering has reached a certain state.
|
||||
fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI);
|
||||
}
|
||||
|
||||
impl<F: FnMut(RenderingState, &GraphicsAPI)> RenderingNotifier for F {
|
||||
fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI) {
|
||||
self(state, graphics_api)
|
||||
}
|
||||
}
|
||||
|
||||
/// This enum describes the different error scenarios that may occur when the applicaton
|
||||
/// registers a rendering notifier on a [`sixtyfps::Window`].
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
#[non_exhaustive]
|
||||
pub enum SetRenderingNotifierError {
|
||||
/// The rendering backend does not support rendering notifiers.
|
||||
Unsupported,
|
||||
/// There is already a rendering notifier set, multiple notifiers are not supported.
|
||||
AlreadySet,
|
||||
}
|
||||
|
||||
/// This type represents a window towards the windowing system, that's used to render the
|
||||
/// scene of a component. It provides API to control windowing system specific aspects such
|
||||
/// as the position on the screen.
|
||||
|
@ -33,6 +110,15 @@ impl Window {
|
|||
pub fn hide(&self) {
|
||||
self.0.hide();
|
||||
}
|
||||
|
||||
/// This function allows registering a callback that's invoked during the different phases of
|
||||
/// rendering. This allows custom rendering on top or below of the scene.
|
||||
pub fn set_rendering_notifier(
|
||||
&self,
|
||||
callback: impl FnMut(RenderingState, &GraphicsAPI) + 'static,
|
||||
) -> std::result::Result<(), SetRenderingNotifierError> {
|
||||
self.0.set_rendering_notifier(Box::new(callback))
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::window::WindowHandleAccess for Window {
|
||||
|
|
|
@ -30,6 +30,15 @@ pub trait PlatformWindow {
|
|||
/// implementation typically uses this to free the underlying graphics resources cached via [`crate::graphics::RenderingCache`].
|
||||
fn free_graphics_resources<'a>(&self, items: &mut dyn Iterator<Item = Pin<ItemRef<'a>>>);
|
||||
|
||||
/// This function is called through the public API to register a callback that the backend needs to invoke during
|
||||
/// different phases of rendering.
|
||||
fn set_rendering_notifier(
|
||||
&self,
|
||||
_callback: Box<dyn crate::api::RenderingNotifier>,
|
||||
) -> std::result::Result<(), crate::api::SetRenderingNotifierError> {
|
||||
Err(crate::api::SetRenderingNotifierError::Unsupported)
|
||||
}
|
||||
|
||||
/// Show a popup at the given position
|
||||
fn show_popup(&self, popup: &ComponentRc, position: Point);
|
||||
|
||||
|
@ -571,6 +580,7 @@ pub mod ffi {
|
|||
#![allow(unsafe_code)]
|
||||
|
||||
use super::*;
|
||||
use crate::api::{RenderingNotifier, RenderingState, SetRenderingNotifierError};
|
||||
use crate::slice::Slice;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
|
@ -678,4 +688,45 @@ pub mod ffi {
|
|||
let window = &*(handle as *const WindowRc);
|
||||
window.close_popup();
|
||||
}
|
||||
|
||||
/// C binding to the set_rendering_notifier() API of Window
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_windowrc_set_rendering_notifier(
|
||||
handle: *const WindowRcOpaque,
|
||||
callback: extern "C" fn(rendering_state: RenderingState, user_data: *mut c_void),
|
||||
drop_user_data: extern "C" fn(user_data: *mut c_void),
|
||||
user_data: *mut c_void,
|
||||
error: *mut SetRenderingNotifierError,
|
||||
) -> bool {
|
||||
struct CNotifier {
|
||||
callback: extern "C" fn(rendering_state: RenderingState, user_data: *mut c_void),
|
||||
drop_user_data: extern "C" fn(*mut c_void),
|
||||
user_data: *mut c_void,
|
||||
}
|
||||
|
||||
impl Drop for CNotifier {
|
||||
fn drop(&mut self) {
|
||||
(self.drop_user_data)(self.user_data)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderingNotifier for CNotifier {
|
||||
fn notify(&mut self, state: RenderingState, _graphics_api: &crate::api::GraphicsAPI) {
|
||||
(self.callback)(state, self.user_data)
|
||||
}
|
||||
}
|
||||
|
||||
let window = &*(handle as *const WindowRc);
|
||||
match window.set_rendering_notifier(Box::new(CNotifier {
|
||||
callback,
|
||||
drop_user_data,
|
||||
user_data,
|
||||
})) {
|
||||
Ok(()) => true,
|
||||
Err(err) => {
|
||||
*error = err;
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue