krun_display/
c_to_rust.rs1use 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
50assert_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 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 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 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 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 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 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<'_> {}