#![forbid(unsafe_code)] //! SDL platform adapter proof behind safe `FParkan` ports. use fparkan_platform::{ EventSource, GraphicsContextRequest, GraphicsProfile, PhysicalSize, PlatformError, PlatformEvent, Version, WindowPort, }; /// Adapter capabilities compiled into this package. #[derive(Clone, Debug, Eq, PartialEq)] pub struct SdlAdapterCapabilities { /// Supported graphics context requests in preference order. pub graphics: Vec, /// Whether adapter-owned code is free of `unsafe`. pub project_owned_unsafe_free: bool, } impl Default for SdlAdapterCapabilities { fn default() -> Self { Self { graphics: vec![ GraphicsContextRequest { profile: GraphicsProfile::DesktopCore, version: Version { major: 3, minor: 3 }, }, GraphicsContextRequest { profile: GraphicsProfile::Embedded, version: Version { major: 2, minor: 0 }, }, ], project_owned_unsafe_free: true, } } } /// Returns adapter readiness status for the safe project-owned layer. #[must_use] pub fn safe_adapter_ready() -> bool { SdlAdapterCapabilities::default().project_owned_unsafe_free } /// In-memory event source used by adapter smoke tests and composition roots /// before a concrete SDL runtime is injected. #[derive(Clone, Debug, Default)] pub struct SdlEventSourceProof { pending: Vec, } impl SdlEventSourceProof { /// Creates an event source with deterministic pending events. #[must_use] pub fn new(pending: Vec) -> Self { Self { pending } } } impl EventSource for SdlEventSourceProof { fn poll(&mut self, out: &mut Vec) -> Result<(), PlatformError> { out.append(&mut self.pending); Ok(()) } } /// Safe window-port proof with SDL-compatible drawable-size semantics. #[derive(Clone, Debug, Eq, PartialEq)] pub struct SdlWindowProof { size: PhysicalSize, presents: u64, } impl SdlWindowProof { /// Creates a proof window with a fixed drawable size. #[must_use] pub fn new(size: PhysicalSize) -> Self { Self { size, presents: 0 } } /// Number of successful present calls. #[must_use] pub fn presents(&self) -> u64 { self.presents } } impl WindowPort for SdlWindowProof { fn drawable_size(&self) -> PhysicalSize { self.size } fn present(&mut self) -> Result<(), PlatformError> { self.presents = self.presents.saturating_add(1); Ok(()) } } #[cfg(test)] mod tests { use super::*; #[test] fn adapter_reports_safe_project_layer_ready() { assert!(safe_adapter_ready()); assert_eq!(SdlAdapterCapabilities::default().graphics.len(), 2); } #[test] fn event_source_and_window_ports_are_deterministic() -> Result<(), PlatformError> { let mut source = SdlEventSourceProof::new(vec![PlatformEvent::Quit]); let mut events = Vec::new(); source.poll(&mut events)?; source.poll(&mut events)?; assert_eq!(events, vec![PlatformEvent::Quit]); let mut window = SdlWindowProof::new(PhysicalSize { width: 320, height: 240, }); assert_eq!(window.drawable_size().width, 320); window.present()?; assert_eq!(window.presents(), 1); Ok(()) } }