rust_widgets 0.9.6

Pure Rust cross-platform native GUI library with hardware-adaptive rendering, 60+ widgets, touch/gesture support, i18n, and SVG-pipeline-accurate output
//! Embedded render engine with independent lifecycle and resource registry.

use super::embedded::embedded_engine_shared;
use super::engine_trait::RenderEngine;
use super::native::NativeRenderEngine;
use crate::core::RuntimeProfile;

/// Embedded engine with independent lifecycle and resource registry.
#[derive(Clone)]
pub struct EmbeddedRenderEngine;

impl EmbeddedRenderEngine {
    /// Create embedded engine.
    pub const fn new() -> Self {
        Self
    }
}

impl Default for EmbeddedRenderEngine {
    fn default() -> Self {
        Self::new()
    }
}

impl RenderEngine for EmbeddedRenderEngine {
    fn name(&self) -> &'static str {
        "embedded-render-engine"
    }

    fn profile(&self) -> RuntimeProfile {
        RuntimeProfile::Embedded
    }

    fn init(&self) {
        embedded_engine_shared().init();
    }

    fn run(&self) {
        embedded_engine_shared().run_loop();
    }

    fn quit(&self) {
        embedded_engine_shared().quit();
    }

    fn create_window(&self, title: &str, x: i32, y: i32, width: u32, height: u32) -> u64 {
        embedded_engine_shared().register_window(title, x, y, width, height)
    }

    fn create_button(
        &self,
        parent: u64,
        text: &str,
        x: i32,
        y: i32,
        width: u32,
        height: u32,
    ) -> u64 {
        embedded_engine_shared().register_button(parent, text, x, y, width, height)
    }
}

/// Build default engine for compile-time profile.
pub fn default_render_engine() -> Box<dyn RenderEngine> {
    if cfg!(feature = "embedded") {
        Box::new(EmbeddedRenderEngine::new())
    } else {
        Box::new(NativeRenderEngine::new())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::render_engine::embedded::{set_embedded_target_fps, submit_embedded_task};
    use core::time::Duration;
    #[cfg(not(feature = "mini"))]
    use std::sync::mpsc;
    #[cfg(not(feature = "mini"))]
    use std::thread;

    fn test_guard() -> crate::compat::MutexGuard<'static, ()> {
        static GUARD: OnceLock<Mutex<()>> = OnceLock::new();
        GUARD.get_or_init(|| Mutex::new(())).lock().unwrap_or_else(|poisoned| poisoned.into_inner())
    }

    #[test]
    fn embedded_task_executes_in_run_loop() {
        let _guard = test_guard();
        let engine = EmbeddedRenderEngine::new();
        set_embedded_target_fps(120);
        let (tx, rx) = mpsc::channel();
        submit_embedded_task("unit-test-task", move |frame| {
            let _ = tx.send(frame);
        });
        let runner = engine.clone();
        let handle = thread::spawn(move || {
            runner.run();
        });
        let frame = rx
            .recv_timeout(Duration::from_secs(1))
            .expect("embedded task should execute within timeout");
        assert!(frame >= 1);
        engine.quit();
        handle.join().expect("embedded render loop thread should join");
    }

    #[test]
    fn embedded_task_queue_order_is_deterministic() {
        let _guard = test_guard();
        let engine = EmbeddedRenderEngine::new();
        set_embedded_target_fps(120);
        let (tx, rx) = mpsc::channel();
        submit_embedded_task("task-1", {
            let tx = tx.clone();
            move |_| {
                let _ = tx.send(1u32);
            }
        });
        submit_embedded_task("task-2", {
            let tx = tx.clone();
            move |_| {
                let _ = tx.send(2u32);
            }
        });
        submit_embedded_task("task-3", move |_| {
            let _ = tx.send(3u32);
        });
        let runner = engine.clone();
        let handle = thread::spawn(move || {
            runner.run();
        });
        let first = rx
            .recv_timeout(Duration::from_secs(1))
            .expect("first embedded task should execute within timeout");
        let second = rx
            .recv_timeout(Duration::from_secs(1))
            .expect("second embedded task should execute within timeout");
        let third = rx
            .recv_timeout(Duration::from_secs(1))
            .expect("third embedded task should execute within timeout");
        assert_eq!([first, second, third], [1, 2, 3]);
        engine.quit();
        handle.join().expect("embedded render loop thread should join");
    }

    use crate::compat::Mutex;
    #[cfg(not(feature = "mini"))]
    use std::sync::OnceLock;
}