Skip to main content

krun_display/
rust_to_c.rs

1use crate::{
2    DisplayBackend, DisplayBackendError, DisplayBasicFramebufferVtable, DisplayFeatures,
3    DisplayVtable, Rect, ResourceFormat,
4};
5use log::error;
6use std::ffi::c_void;
7use std::marker::PhantomData;
8use std::ptr;
9use std::ptr::{null, null_mut};
10
11pub trait DisplayBackendNew<T: Sync> {
12    fn new(userdata: Option<&T>) -> Self;
13}
14
15pub trait DisplayBackendBasicFramebuffer {
16    fn configure_scanout(
17        &mut self,
18        scanout_id: u32,
19        display_width: u32,
20        display_height: u32,
21        width: u32,
22        height: u32,
23        format: ResourceFormat,
24    ) -> Result<(), DisplayBackendError>;
25
26    fn disable_scanout(&mut self, scanout_id: u32) -> Result<(), DisplayBackendError>;
27
28    fn alloc_frame(&mut self, scanout_id: u32) -> Result<(u32, &mut [u8]), DisplayBackendError>;
29
30    fn present_frame(
31        &mut self,
32        scanout_id: u32,
33        frame_id: u32,
34        rect: Option<&Rect>,
35    ) -> Result<(), DisplayBackendError>;
36}
37
38pub trait IntoDisplayBackend<T: Sync> {
39    fn into_display_backend(userdata: Option<&T>) -> DisplayBackend<'_>;
40}
41
42impl<T: Sync, I: DisplayBackendBasicFramebuffer + DisplayBackendNew<T>> IntoDisplayBackend<T>
43    for I
44{
45    fn into_display_backend(userdata: Option<&T>) -> DisplayBackend<'_> {
46        extern "C" fn create_fn<T: Sync, I: DisplayBackendNew<T>>(
47            instance: *mut *mut c_void,
48            userdata: *const c_void,
49            _reserved: *const c_void,
50        ) -> i32 {
51            unsafe {
52                assert_ne!(
53                    instance,
54                    null_mut(),
55                    "Pointer to location where to create instance cannot be null"
56                );
57                let userdata_ref = (userdata as *const T).as_ref();
58                *(instance as *mut *mut I) = Box::into_raw(Box::new(I::new(userdata_ref)));
59            }
60            0
61        }
62
63        extern "C" fn destroy_fn<I>(instance: *mut c_void) -> i32 {
64            drop(unsafe { Box::from_raw(instance as *mut I) });
65            0
66        }
67
68        fn cast_instance<'a, I: DisplayBackendBasicFramebuffer>(
69            instance: *mut c_void,
70        ) -> &'a mut I {
71            assert_ne!(instance, null_mut());
72            unsafe { &mut *(instance as *mut I) }
73        }
74
75        extern "C" fn configure_scanout_fn<I: DisplayBackendBasicFramebuffer>(
76            instance: *mut c_void,
77            scanout_id: u32,
78            display_width: u32,
79            display_height: u32,
80            width: u32,
81            height: u32,
82            format: u32,
83        ) -> i32 {
84            let Ok(format) = ResourceFormat::try_from(format) else {
85                error!("Unknown display format: {format}");
86                return DisplayBackendError::InvalidParam as i32;
87            };
88
89            from_rust_result(cast_instance::<I>(instance).configure_scanout(
90                scanout_id,
91                display_width,
92                display_height,
93                width,
94                height,
95                format,
96            ))
97        }
98
99        extern "C" fn disable_scanout_fb<I: DisplayBackendBasicFramebuffer>(
100            instance: *mut c_void,
101            scanout_id: u32,
102        ) -> i32 {
103            from_rust_result(cast_instance::<I>(instance).disable_scanout(scanout_id))
104        }
105
106        extern "C" fn alloc_frame<I: DisplayBackendBasicFramebuffer>(
107            instance: *mut c_void,
108            scanout_id: u32,
109            buffer: *mut *mut u8,
110            buffer_size: *mut usize,
111        ) -> i32 {
112            match cast_instance::<I>(instance).alloc_frame(scanout_id) {
113                Ok((frame_id, allocated_buffer)) => {
114                    unsafe {
115                        *buffer_size = allocated_buffer.len();
116                        *buffer = allocated_buffer.as_mut_ptr();
117                    }
118                    frame_id as i32
119                }
120                Err(e) => e as i32,
121            }
122        }
123
124        extern "C" fn present_frame<I: DisplayBackendBasicFramebuffer>(
125            instance: *mut c_void,
126            scanout_id: u32,
127            frame_id: u32,
128            rect: *const Rect,
129        ) -> i32 {
130            // SAFETY: The pointer obtained from the bindings should be safe
131            let rect: Option<&Rect> = unsafe { ptr_to_option_ref(rect) };
132            from_rust_result(cast_instance::<I>(instance).present_frame(scanout_id, frame_id, rect))
133        }
134
135        DisplayBackend {
136            create_userdata: userdata.map_or(null(), |t| ptr::from_ref(t) as *const c_void),
137            create_userdata_lifetime: PhantomData,
138            features: DisplayFeatures::BASIC_FRAMEBUFFER.bits(),
139            create_fn: Some(create_fn::<T, I>),
140            vtable: DisplayVtable {
141                basic_framebuffer: DisplayBasicFramebufferVtable {
142                    destroy: Some(destroy_fn::<I>),
143                    configure_scanout: Some(configure_scanout_fn::<I>),
144                    present_frame: Some(present_frame::<I>),
145                    alloc_frame: Some(alloc_frame::<I>),
146                    disable_scanout: Some(disable_scanout_fb::<I>),
147                },
148            },
149        }
150    }
151}
152
153unsafe fn ptr_to_option_ref<'a, T>(x: *const T) -> Option<&'a T> {
154    if x.is_null() {
155        None
156    } else {
157        // SAFETY: this method is unsafe, up to the caller to be sure
158        unsafe { Some(&*x) }
159    }
160}
161
162fn from_rust_result(result: Result<(), DisplayBackendError>) -> i32 {
163    match result {
164        Ok(()) => 0,
165        Err(e) => e as i32,
166    }
167}