use crate::{
DisplayBackend, DisplayBackendError, DisplayBasicFramebufferVtable, DisplayFeatures,
DisplayVtable, Rect, ResourceFormat,
};
use log::error;
use std::ffi::c_void;
use std::marker::PhantomData;
use std::ptr;
use std::ptr::{null, null_mut};
pub trait DisplayBackendNew<T: Sync> {
fn new(userdata: Option<&T>) -> Self;
}
pub trait DisplayBackendBasicFramebuffer {
fn configure_scanout(
&mut self,
scanout_id: u32,
display_width: u32,
display_height: u32,
width: u32,
height: u32,
format: ResourceFormat,
) -> Result<(), DisplayBackendError>;
fn disable_scanout(&mut self, scanout_id: u32) -> Result<(), DisplayBackendError>;
fn alloc_frame(&mut self, scanout_id: u32) -> Result<(u32, &mut [u8]), DisplayBackendError>;
fn present_frame(
&mut self,
scanout_id: u32,
frame_id: u32,
rect: Option<&Rect>,
) -> Result<(), DisplayBackendError>;
}
pub trait IntoDisplayBackend<T: Sync> {
fn into_display_backend(userdata: Option<&T>) -> DisplayBackend<'_>;
}
impl<T: Sync, I: DisplayBackendBasicFramebuffer + DisplayBackendNew<T>> IntoDisplayBackend<T>
for I
{
fn into_display_backend(userdata: Option<&T>) -> DisplayBackend<'_> {
extern "C" fn create_fn<T: Sync, I: DisplayBackendNew<T>>(
instance: *mut *mut c_void,
userdata: *const c_void,
_reserved: *const c_void,
) -> i32 {
unsafe {
assert_ne!(
instance,
null_mut(),
"Pointer to location where to create instance cannot be null"
);
let userdata_ref = (userdata as *const T).as_ref();
*(instance as *mut *mut I) = Box::into_raw(Box::new(I::new(userdata_ref)));
}
0
}
extern "C" fn destroy_fn<I>(instance: *mut c_void) -> i32 {
drop(unsafe { Box::from_raw(instance as *mut I) });
0
}
fn cast_instance<'a, I: DisplayBackendBasicFramebuffer>(
instance: *mut c_void,
) -> &'a mut I {
assert_ne!(instance, null_mut());
unsafe { &mut *(instance as *mut I) }
}
extern "C" fn configure_scanout_fn<I: DisplayBackendBasicFramebuffer>(
instance: *mut c_void,
scanout_id: u32,
display_width: u32,
display_height: u32,
width: u32,
height: u32,
format: u32,
) -> i32 {
let Ok(format) = ResourceFormat::try_from(format) else {
error!("Unknown display format: {format}");
return DisplayBackendError::InvalidParam as i32;
};
from_rust_result(cast_instance::<I>(instance).configure_scanout(
scanout_id,
display_width,
display_height,
width,
height,
format,
))
}
extern "C" fn disable_scanout_fb<I: DisplayBackendBasicFramebuffer>(
instance: *mut c_void,
scanout_id: u32,
) -> i32 {
from_rust_result(cast_instance::<I>(instance).disable_scanout(scanout_id))
}
extern "C" fn alloc_frame<I: DisplayBackendBasicFramebuffer>(
instance: *mut c_void,
scanout_id: u32,
buffer: *mut *mut u8,
buffer_size: *mut usize,
) -> i32 {
match cast_instance::<I>(instance).alloc_frame(scanout_id) {
Ok((frame_id, allocated_buffer)) => {
unsafe {
*buffer_size = allocated_buffer.len();
*buffer = allocated_buffer.as_mut_ptr();
}
frame_id as i32
}
Err(e) => e as i32,
}
}
extern "C" fn present_frame<I: DisplayBackendBasicFramebuffer>(
instance: *mut c_void,
scanout_id: u32,
frame_id: u32,
rect: *const Rect,
) -> i32 {
let rect: Option<&Rect> = unsafe { ptr_to_option_ref(rect) };
from_rust_result(cast_instance::<I>(instance).present_frame(scanout_id, frame_id, rect))
}
DisplayBackend {
create_userdata: userdata.map_or(null(), |t| ptr::from_ref(t) as *const c_void),
create_userdata_lifetime: PhantomData,
features: DisplayFeatures::BASIC_FRAMEBUFFER.bits(),
create_fn: Some(create_fn::<T, I>),
vtable: DisplayVtable {
basic_framebuffer: DisplayBasicFramebufferVtable {
destroy: Some(destroy_fn::<I>),
configure_scanout: Some(configure_scanout_fn::<I>),
present_frame: Some(present_frame::<I>),
alloc_frame: Some(alloc_frame::<I>),
disable_scanout: Some(disable_scanout_fb::<I>),
},
},
}
}
}
unsafe fn ptr_to_option_ref<'a, T>(x: *const T) -> Option<&'a T> {
if x.is_null() {
None
} else {
unsafe { Some(&*x) }
}
}
fn from_rust_result(result: Result<(), DisplayBackendError>) -> i32 {
match result {
Ok(()) => 0,
Err(e) => e as i32,
}
}