Skip to main content

msb_krun_input/
c_to_rust.rs

1use crate::{
2    ConfigFeatures, EventProviderFeatures, InputAbsInfo, InputBackendError, InputDeviceIds,
3    InputEvent, InputEventsImpl, InputQueryConfig, header,
4};
5use log::{error, warn};
6use static_assertions::assert_not_impl_any;
7use std::ffi::c_void;
8use std::marker::PhantomData;
9use std::os::fd::BorrowedFd;
10use std::ptr::{null, null_mut};
11
12#[macro_export]
13macro_rules! into_rust_result {
14    ($expr:expr) => {
15        into_rust_result!($expr,
16            0 => Ok(()),
17            code @ 0.. => {
18                log::warn!("{}: Unknown OK result code: {code}", stringify!($expr));
19                Ok(())
20            }
21        )
22    };
23    ($expr:expr, $($pat:pat $(if $pat_guard:expr)? => $pat_expr:expr),+ ) => {
24        match $expr {
25            $($pat $(if $pat_guard)? => $pat_expr,)+
26            -1 => Err(InputBackendError::InternalError),
27            -2 => Err(InputBackendError::Again),
28            -3 => Err(InputBackendError::MethodNotSupported),
29            -4 => Err(InputBackendError::InvalidParam),
30            code @ i32::MIN.. => {
31                log::warn!("{}: Unknown error result code: {code}", stringify!($expr));
32                Err(InputBackendError::InternalError)
33            }
34        }
35    };
36}
37
38macro_rules! method_call {
39    ($self:ident.$method:ident($($args:expr),*) ) => {
40        unsafe {
41            $self.vtable.$method
42                .ok_or(InputBackendError::MethodNotSupported)?( $self.instance, $($args),* )
43        }
44    };
45}
46
47pub struct InputEventProviderInstance {
48    instance: *mut c_void,
49    vtable: header::krun_input_event_provider_vtable,
50}
51impl InputEventsImpl for InputEventProviderInstance {
52    /// Get the ready event file descriptor that becomes readable when input events are available
53    fn get_read_notify_fd(&self) -> Result<BorrowedFd<'_>, InputBackendError> {
54        let fd = method_call! {
55            self.get_ready_efd()
56        };
57
58        into_rust_result!(fd,
59            fd if fd >= 0 => Ok(
60                // SAFETY: We have checked the return code of the method, the so the fd should be valid
61                //         The lifetime of the fd is the existence of this event provider.
62                unsafe { BorrowedFd::borrow_raw(fd) }
63            )
64        )
65    }
66
67    /// Fetch the next available input event, returns None if no events are available
68    fn next_event(&mut self) -> Result<Option<InputEvent>, InputBackendError> {
69        let mut event = InputEvent {
70            type_: 0,
71            code: 0,
72            value: 0,
73        };
74
75        let result = method_call! {
76            self.next_event(&raw mut event)
77        };
78
79        into_rust_result!(result,
80            1 => Ok(Some(event)),
81            0 => Ok(None)
82        )
83    }
84}
85
86pub struct InputConfigInstance {
87    instance: *mut c_void,
88    vtable: header::krun_input_config_vtable,
89}
90
91unsafe impl Send for InputConfigInstance {}
92unsafe impl Sync for InputConfigInstance {}
93
94assert_not_impl_any!(InputEventProviderInstance: Sync, Send);
95
96impl Drop for InputEventProviderInstance {
97    fn drop(&mut self) {
98        let Some(destroy_fn) = self.vtable.destroy else {
99            return;
100        };
101
102        if let Err(e) = into_rust_result!(unsafe { destroy_fn(self.instance) }) {
103            error!("Failed to destroy krun input events instance: {e}");
104        }
105    }
106}
107
108impl Drop for InputConfigInstance {
109    fn drop(&mut self) {
110        let Some(destroy_fn) = self.vtable.destroy else {
111            return;
112        };
113
114        if let Err(e) = into_rust_result!(unsafe { destroy_fn(self.instance) }) {
115            error!("Failed to destroy krun input config instance: {e}");
116        }
117    }
118}
119
120// Remove the old InputConfigImpl methods as they're not needed
121
122impl InputQueryConfig for InputConfigInstance {
123    fn query_device_name(&self, name_buf: &mut [u8]) -> Result<u8, InputBackendError> {
124        let result = method_call! {
125            self.query_device_name(name_buf.as_mut_ptr(), name_buf.len())
126        };
127
128        into_rust_result!(result,
129            len if len >= 0 => Ok(len as u8)
130        )
131    }
132
133    fn query_serial_name(&self, name_buf: &mut [u8]) -> Result<u8, InputBackendError> {
134        let result = method_call! {
135            self.query_serial_name(name_buf.as_mut_ptr(), name_buf.len())
136        };
137
138        into_rust_result!(result,
139            len if len >= 0 => Ok(len as u8)
140        )
141    }
142
143    fn query_device_ids(&self, ids: &mut InputDeviceIds) -> Result<(), InputBackendError> {
144        let result = method_call! {
145            self.query_device_ids(ids as *mut InputDeviceIds)
146        };
147
148        into_rust_result!(result)
149    }
150
151    fn query_event_capabilities(
152        &self,
153        event_type: u8,
154        bitmap_buf: &mut [u8],
155    ) -> Result<u8, InputBackendError> {
156        let result = method_call! {
157            self.query_event_capabilities(event_type, bitmap_buf.as_mut_ptr(), bitmap_buf.len())
158        };
159
160        into_rust_result!(result,
161            len if len >= 0 => Ok(len as u8)
162        )
163    }
164
165    fn query_abs_info(
166        &self,
167        abs_axis: u8,
168        abs_info: &mut InputAbsInfo,
169    ) -> Result<(), InputBackendError> {
170        let result = method_call! {
171            self.query_abs_info(abs_axis, abs_info as *mut InputAbsInfo)
172        };
173
174        into_rust_result!(result)
175    }
176
177    fn query_properties(&self, properties: &mut [u8]) -> Result<u8, InputBackendError> {
178        let result = method_call! {
179            self.query_properties(properties.as_mut_ptr(), properties.len())
180        };
181
182        into_rust_result!(result,
183            len if len >= 0 => Ok(len as u8)
184        )
185    }
186}
187
188#[derive(Copy, Clone)]
189#[repr(C)]
190pub struct InputConfigBackend<'userdata> {
191    pub features: u64,
192    pub create_userdata: *const c_void,
193    pub create_userdata_lifetime: PhantomData<&'userdata c_void>,
194    pub create_fn: header::krun_input_create_fn,
195    pub vtable: header::krun_input_config_vtable,
196}
197unsafe impl Send for InputConfigBackend<'_> {}
198unsafe impl Sync for InputConfigBackend<'_> {}
199
200impl InputConfigBackend<'_> {
201    /// Create an InputConfigInstance for handling device configuration
202    pub fn create_instance(&self) -> Result<InputConfigInstance, InputBackendError> {
203        let mut instance = null_mut();
204        if let Some(create_fn) = self.create_fn {
205            into_rust_result!(unsafe {
206                create_fn(&raw mut instance, self.create_userdata, null())
207            })?;
208        }
209        assert!(self.verify());
210
211        Ok(InputConfigInstance {
212            instance,
213            vtable: self.vtable,
214        })
215    }
216
217    pub fn verify(&self) -> bool {
218        let features = ConfigFeatures::from_bits_retain(self.features);
219
220        // This requirement might change in the future when we add support for alternatives to this
221        if !features.contains(ConfigFeatures::QUERY) {
222            error!("This version of libkrun requires QUEUE feature");
223            return false;
224        }
225
226        for feature in features {
227            if feature.contains(ConfigFeatures::QUERY) {
228                if self.vtable.query_device_name.is_none()
229                    || self.vtable.query_serial_name.is_none()
230                    || self.vtable.query_device_ids.is_none()
231                    || self.vtable.query_event_capabilities.is_none()
232                    || self.vtable.query_abs_info.is_none()
233                    || self.vtable.query_properties.is_none()
234                {
235                    error!("Missing required methods for QUERY feature");
236                    return false;
237                }
238            } else {
239                warn!("Unknown features ({feature:x}) will be ignored")
240            }
241        }
242        true
243    }
244}
245
246#[derive(Copy, Clone)]
247#[repr(C)]
248pub struct InputEventProviderBackend<'userdata> {
249    pub features: u64,
250    pub create_userdata: *const c_void,
251    pub create_userdata_lifetime: PhantomData<&'userdata c_void>,
252    pub create_fn: header::krun_input_create_fn,
253    pub vtable: header::krun_input_event_provider_vtable,
254}
255
256unsafe impl Send for InputEventProviderBackend<'_> {}
257unsafe impl Sync for InputEventProviderBackend<'_> {}
258
259impl InputEventProviderBackend<'_> {
260    /// Create an InputEventsInstance for handling input events
261    pub fn create_instance(&self) -> Result<InputEventProviderInstance, InputBackendError> {
262        let mut instance = null_mut();
263        if let Some(create_fn) = self.create_fn {
264            into_rust_result!(unsafe {
265                create_fn(&raw mut instance, self.create_userdata, null())
266            })?;
267        }
268        assert!(self.verify());
269
270        Ok(InputEventProviderInstance {
271            instance,
272            vtable: self.vtable,
273        })
274    }
275
276    pub fn verify(&self) -> bool {
277        let features = EventProviderFeatures::from_bits_retain(self.features);
278
279        // This requirement might change in the future when we add support for alternatives to this
280        if !features.contains(EventProviderFeatures::QUEUE) {
281            error!("This version of libkrun requires QUEUE feature");
282            return false;
283        }
284
285        for feature in features {
286            if feature.contains(EventProviderFeatures::QUEUE) {
287                if self.vtable.get_ready_efd.is_none() || self.vtable.get_ready_efd.is_none() {
288                    error!("Missing required methods for BASIC_FRAMEBUFFER");
289                    return false;
290                }
291            } else {
292                warn!("Unknown features ({feature:x}) will be ignored")
293            }
294        }
295        true
296    }
297}