use crate::{
DisplayBackendBasicFramebuffer, DisplayBackendError, DisplayBasicFramebufferVtable,
DisplayFeatures, DisplayVtable, Rect, ResourceFormat, header,
};
use log::{error, warn};
use static_assertions::assert_not_impl_any;
use std::ffi::c_void;
use std::marker::PhantomData;
use std::ptr::{null, null_mut, slice_from_raw_parts_mut};
#[macro_export]
macro_rules! into_rust_result {
($expr:expr) => {
into_rust_result!($expr,
0 => Ok(()),
code @ 0.. => {
log::warn!("{}: Unknown OK result code: {code}", stringify!($expr));
Ok(())
}
)
};
($expr:expr, $($pat:pat $(if $pat_guard:expr)? => $pat_expr:expr),+ ) => {
match $expr {
$($pat $(if $pat_guard)? => $pat_expr,)+
-1 => Err(DisplayBackendError::InternalError),
-3 => Err(DisplayBackendError::InvalidScanoutId),
-4 => Err(DisplayBackendError::InvalidParam),
code @ i32::MIN.. => {
log::warn!("{}: Unknown error result code: {code}", stringify!($expr));
Err(DisplayBackendError::InternalError)
}
}
};
}
macro_rules! method_call {
($self:ident.$method:ident($($args:expr),*) ) => {
unsafe {
$self.vtable.$method
.ok_or(DisplayBackendError::MethodNotSupported)?( $self.instance, $($args),* )
}
};
}
pub struct DisplayBackendInstance {
instance: *mut c_void,
vtable: DisplayBasicFramebufferVtable,
}
assert_not_impl_any!(DisplayBackendInstance: Sync, Send);
impl Drop for DisplayBackendInstance {
fn drop(&mut self) {
let Some(destroy_fn) = self.vtable.destroy else {
return;
};
if let Err(e) = into_rust_result!(unsafe { destroy_fn(self.instance) }) {
error!("Failed to destroy krun_gtk_display instance: {e}");
}
}
}
impl DisplayBackendBasicFramebuffer for DisplayBackendInstance {
fn configure_scanout(
&mut self,
scanout_id: u32,
display_width: u32,
display_height: u32,
width: u32,
height: u32,
format: ResourceFormat,
) -> Result<(), DisplayBackendError> {
into_rust_result!(method_call! {
self.configure_scanout(
scanout_id,
display_width,
display_height,
width,
height,
format as u32
)
})
}
fn disable_scanout(&mut self, scanout_id: u32) -> Result<(), DisplayBackendError> {
into_rust_result! {
method_call! {
self.disable_scanout(scanout_id)
}
}
}
fn alloc_frame(&mut self, scanout_id: u32) -> Result<(u32, &mut [u8]), DisplayBackendError> {
let mut buffer: *mut u8 = null_mut();
let mut buffer_len: usize = 0;
let frame_id = into_rust_result! {
method_call! {
self.alloc_frame(scanout_id, &raw mut buffer, &raw mut buffer_len)
},
result @ 0.. => Ok(result as u32)
}?;
assert_ne!(buffer, null_mut());
assert_ne!(buffer_len, 0);
let buffer = unsafe {
slice_from_raw_parts_mut(buffer, buffer_len)
.as_mut()
.unwrap()
};
Ok((frame_id, buffer))
}
fn present_frame(
&mut self,
scanout_id: u32,
frame_id: u32,
rect: Option<&Rect>,
) -> Result<(), DisplayBackendError> {
into_rust_result! {
method_call!{
self.present_frame(scanout_id, frame_id, rect.map(|r| r as *const _).unwrap_or(null()))
}
}
}
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct DisplayBackend<'userdata> {
pub features: u64,
pub create_userdata: *const c_void,
pub create_userdata_lifetime: PhantomData<&'userdata c_void>,
pub create_fn: header::krun_display_create_fn,
pub vtable: DisplayVtable,
}
impl DisplayBackend<'_> {
pub fn create_instance(&self) -> Result<DisplayBackendInstance, DisplayBackendError> {
let mut instance = null_mut();
if let Some(create_fn) = self.create_fn {
into_rust_result!(unsafe {
create_fn(&raw mut instance, self.create_userdata, null())
})?;
}
assert!(self.verify());
Ok(DisplayBackendInstance {
instance,
vtable: unsafe { self.vtable.basic_framebuffer },
})
}
pub fn verify(&self) -> bool {
let features = DisplayFeatures::from_bits_retain(self.features);
if !features.contains(DisplayFeatures::BASIC_FRAMEBUFFER) {
error!("This version of libkrun requires BASIC_FRAMEBUFFER feature");
return false;
}
for feature in features {
if feature.contains(DisplayFeatures::BASIC_FRAMEBUFFER) {
if unsafe {
self.vtable.basic_framebuffer.disable_scanout.is_none()
|| self.vtable.basic_framebuffer.configure_scanout.is_none()
|| self.vtable.basic_framebuffer.alloc_frame.is_none()
|| self.vtable.basic_framebuffer.present_frame.is_none()
} {
error!("Missing required methods for BASIC_FRAMEBUFFER");
return false;
}
} else {
warn!("Unknown display features ({feature:x}) will be ignored")
}
}
true
}
}
unsafe impl Send for DisplayBackend<'_> {}