Skip to main content

krun_display/
c_to_rust.rs

1use crate::{
2    DisplayBackendBasicFramebuffer, DisplayBackendError, DisplayBasicFramebufferVtable,
3    DisplayFeatures, DisplayVtable, Rect, ResourceFormat, header,
4};
5use log::{error, warn};
6use static_assertions::assert_not_impl_any;
7use std::ffi::c_void;
8use std::marker::PhantomData;
9use std::ptr::{null, null_mut, slice_from_raw_parts_mut};
10
11#[macro_export]
12macro_rules! into_rust_result {
13    ($expr:expr) => {
14        into_rust_result!($expr,
15            0 => Ok(()),
16            code @ 0.. => {
17                log::warn!("{}: Unknown OK result code: {code}", stringify!($expr));
18                Ok(())
19            }
20        )
21    };
22    ($expr:expr, $($pat:pat $(if $pat_guard:expr)? => $pat_expr:expr),+ ) => {
23        match $expr {
24            $($pat $(if $pat_guard)? => $pat_expr,)+
25            -1 => Err(DisplayBackendError::InternalError),
26            -3 => Err(DisplayBackendError::InvalidScanoutId),
27            -4 => Err(DisplayBackendError::InvalidParam),
28            code @ i32::MIN.. => {
29                log::warn!("{}: Unknown error result code: {code}", stringify!($expr));
30                Err(DisplayBackendError::InternalError)
31            }
32        }
33    };
34}
35
36macro_rules! method_call {
37    ($self:ident.$method:ident($($args:expr),*) ) => {
38        unsafe {
39            $self.vtable.$method
40                .ok_or(DisplayBackendError::MethodNotSupported)?( $self.instance, $($args),* )
41        }
42    };
43}
44
45pub struct DisplayBackendInstance {
46    instance: *mut c_void,
47    vtable: DisplayBasicFramebufferVtable,
48}
49
50// By design the struct is !Send and !Sync to allow for the implementation to safely assume that
51// the methods are always called on the GPU worker thread
52assert_not_impl_any!(DisplayBackendInstance: Sync, Send);
53
54impl Drop for DisplayBackendInstance {
55    fn drop(&mut self) {
56        let Some(destroy_fn) = self.vtable.destroy else {
57            return;
58        };
59
60        if let Err(e) = into_rust_result!(unsafe { destroy_fn(self.instance) }) {
61            error!("Failed to destroy krun_gtk_display instance: {e}");
62        }
63    }
64}
65
66impl DisplayBackendBasicFramebuffer for DisplayBackendInstance {
67    fn configure_scanout(
68        &mut self,
69        scanout_id: u32,
70        display_width: u32,
71        display_height: u32,
72        width: u32,
73        height: u32,
74        format: ResourceFormat,
75    ) -> Result<(), DisplayBackendError> {
76        into_rust_result!(method_call! {
77        self.configure_scanout(
78            scanout_id,
79            display_width,
80            display_height,
81            width,
82            height,
83            format as u32
84        )
85        })
86    }
87
88    fn disable_scanout(&mut self, scanout_id: u32) -> Result<(), DisplayBackendError> {
89        into_rust_result! {
90            method_call! {
91                self.disable_scanout(scanout_id)
92            }
93        }
94    }
95
96    // Soundness note: this method has to take &mut self in order for the lifetime of the returned
97    // slice to be tied to self. This way the returned slice cannot stay borrowed once another method
98    // on self is called.
99    fn alloc_frame(&mut self, scanout_id: u32) -> Result<(u32, &mut [u8]), DisplayBackendError> {
100        let mut buffer: *mut u8 = null_mut();
101        let mut buffer_len: usize = 0;
102        let frame_id = into_rust_result! {
103            method_call! {
104                self.alloc_frame(scanout_id, &raw mut buffer, &raw mut buffer_len)
105            },
106            result @ 0.. => Ok(result as u32)
107        }?;
108
109        assert_ne!(buffer, null_mut());
110        assert_ne!(buffer_len, 0);
111        // SAFETY: We have obtained the buffer and buffer_len from the krun_gtk_display impl. Because
112        //         the alloc_frame_fn return an error we assume they should be valid.
113        let buffer = unsafe {
114            slice_from_raw_parts_mut(buffer, buffer_len)
115                .as_mut()
116                .unwrap()
117        };
118        Ok((frame_id, buffer))
119    }
120
121    fn present_frame(
122        &mut self,
123        scanout_id: u32,
124        frame_id: u32,
125        rect: Option<&Rect>,
126    ) -> Result<(), DisplayBackendError> {
127        into_rust_result! {
128            method_call!{
129                self.present_frame(scanout_id, frame_id, rect.map(|r| r as *const _).unwrap_or(null()))
130            }
131        }
132    }
133}
134
135#[derive(Copy, Clone)]
136#[repr(C)]
137pub struct DisplayBackend<'userdata> {
138    pub features: u64,
139    pub create_userdata: *const c_void,
140    pub create_userdata_lifetime: PhantomData<&'userdata c_void>,
141    pub create_fn: header::krun_display_create_fn,
142    pub vtable: DisplayVtable,
143}
144
145impl DisplayBackend<'_> {
146    /// Create a DisplayBackendInstance, the caller is responsible for only calling this on a
147    /// properly constructed DisplayBackend struct.
148    pub fn create_instance(&self) -> Result<DisplayBackendInstance, DisplayBackendError> {
149        let mut instance = null_mut();
150        if let Some(create_fn) = self.create_fn {
151            into_rust_result!(unsafe {
152                create_fn(&raw mut instance, self.create_userdata, null())
153            })?;
154        }
155        assert!(self.verify());
156
157        Ok(DisplayBackendInstance {
158            instance,
159            // SAFETY: we have checked the feature flags, so basic_framebuffer should be populated
160            vtable: unsafe { self.vtable.basic_framebuffer },
161        })
162    }
163
164    pub fn verify(&self) -> bool {
165        let features = DisplayFeatures::from_bits_retain(self.features);
166
167        // This requirement might change in the future when we add support for alternatives to this
168        if !features.contains(DisplayFeatures::BASIC_FRAMEBUFFER) {
169            error!("This version of libkrun requires BASIC_FRAMEBUFFER feature");
170            return false;
171        }
172
173        for feature in features {
174            if feature.contains(DisplayFeatures::BASIC_FRAMEBUFFER) {
175                // SAFETY: We have checked the feature flag is enabled, so we should be able to
176                // access the union field.
177                if unsafe {
178                    self.vtable.basic_framebuffer.disable_scanout.is_none()
179                        || self.vtable.basic_framebuffer.configure_scanout.is_none()
180                        || self.vtable.basic_framebuffer.alloc_frame.is_none()
181                        || self.vtable.basic_framebuffer.present_frame.is_none()
182                } {
183                    error!("Missing required methods for BASIC_FRAMEBUFFER");
184                    return false;
185                }
186            } else {
187                warn!("Unknown display features ({feature:x}) will be ignored")
188            }
189        }
190        true
191    }
192}
193
194unsafe impl Send for DisplayBackend<'_> {}