penpot/render-wasm/src/globals.rs
2026-05-12 12:14:38 +02:00

139 lines
3.6 KiB
Rust

use macros::wasm_error;
use crate::mem;
use crate::render::{gpu_state::GpuState, RenderState};
use crate::state::{State, TextEditorState};
static mut DESIGN_STATE: *mut State = std::ptr::null_mut();
/// Design State.
pub(crate) fn get_design_state() -> &'static mut State {
unsafe {
debug_assert!(!DESIGN_STATE.is_null(), "Design State is null");
&mut *DESIGN_STATE
}
}
/// GPU State.
static mut GPU_STATE: *mut GpuState = std::ptr::null_mut();
#[inline(always)]
pub(crate) fn get_gpu_state() -> &'static mut GpuState {
unsafe {
debug_assert!(!GPU_STATE.is_null(), "GPU State is null");
&mut *GPU_STATE
}
}
/// Render State.
static mut RENDER_STATE: *mut RenderState = std::ptr::null_mut();
#[inline(always)]
pub(crate) fn get_render_state() -> &'static mut RenderState {
unsafe {
debug_assert!(!RENDER_STATE.is_null(), "Render State is null");
&mut *RENDER_STATE
}
}
/// Text Editor State
static mut TEXT_EDITOR_STATE: *mut TextEditorState = std::ptr::null_mut();
#[inline(always)]
pub(crate) fn get_text_editor_state() -> &'static mut TextEditorState {
unsafe {
debug_assert!(!TEXT_EDITOR_STATE.is_null(), "Text Editor state is null");
&mut *TEXT_EDITOR_STATE
}
}
// FIXME: These with_state* macros should be using our CriticalError instead of expect.
// But to do that, we need to not use them at domain-level (i.e. in business logic), just
// in the context of the wasm call.
#[macro_export]
macro_rules! with_state {
($state:ident, $block:block) => {{
use $crate::globals::get_design_state;
let $state = get_design_state();
$block
}};
}
#[macro_export]
macro_rules! with_current_shape_mut {
($state:ident, |$shape:ident: &mut Shape| $block:block) => {
use $crate::globals::get_design_state;
let $state = get_design_state();
$state.touch_current();
if let Some($shape) = $state.current_shape_mut() {
$block
}
};
}
#[macro_export]
macro_rules! with_current_shape {
($state:ident, |$shape:ident: &Shape| $block:block) => {
use $crate::globals::get_design_state;
let $state = get_design_state();
if let Some($shape) = $state.current_shape() {
$block
}
};
}
/// Initializes GPUState.
fn gpu_init() {
unsafe {
let gpu_state = GpuState::try_new().expect("Cannot initialize GPU State");
GPU_STATE = Box::into_raw(Box::new(gpu_state));
}
}
/// Initializes RenderState.
fn render_init(width: i32, height: i32) {
unsafe {
let render_state =
RenderState::try_new(width, height).expect("Cannot intialize RenderState");
RENDER_STATE = Box::into_raw(Box::new(render_state));
}
}
/// Initializes DesignState.
fn design_init() {
unsafe {
let design_state = State::new();
DESIGN_STATE = Box::into_raw(Box::new(design_state));
}
}
fn text_editor_init() {
unsafe {
let text_editor_state = TextEditorState::new();
TEXT_EDITOR_STATE = Box::into_raw(Box::new(text_editor_state));
}
}
#[no_mangle]
#[wasm_error]
pub extern "C" fn init(width: i32, height: i32) -> Result<()> {
gpu_init();
render_init(width, height);
text_editor_init();
design_init();
Ok(())
}
#[no_mangle]
#[wasm_error]
pub extern "C" fn clean_up() -> Result<()> {
// Cancel the current animation frame if it exists so
// it won't try to render without context
let render_state = get_render_state();
render_state.cancel_animation_frame();
render_state.prepare_context_loss_cleanup();
unsafe { DESIGN_STATE = std::ptr::null_mut() }
mem::free_bytes()?;
Ok(())
}