jazz up Rust bindings, document some

This commit is contained in:
Lily Tsuru 2024-12-02 03:26:01 -05:00
parent ea76b9f9d8
commit ec1649197c
2 changed files with 121 additions and 66 deletions

View file

@ -1,79 +1,121 @@
use std::{ffi, path::Path}; use std::ffi;
#[repr(u32)] pub(crate) mod sys {
pub enum ResultCode { use std::ffi;
#[repr(u32)]
#[allow(unused)] // it IS used, just not by Rust
pub enum ResultCode {
Unchanged, Unchanged,
Changed, Changed,
Fail, Fail,
}
extern "C" {
pub(crate) fn rust_new_hazelnut_client() -> *mut ffi::c_void;
pub(crate) fn rust_destroy_hazelnut_client(client: *mut ffi::c_void);
pub(crate) fn rust_hazelnut_client_open(
client: *mut ffi::c_void,
url: *const ffi::c_char,
) -> bool;
pub(crate) fn rust_hazelnut_client_tick(client: *mut ffi::c_void) -> ResultCode;
// Explicitly lock/unlock.
pub(crate) fn rust_hazelnut_client_lock(client: *mut ffi::c_void);
pub(crate) fn rust_hazelnut_client_unlock(client: *mut ffi::c_void);
// Use only when locked
pub(crate) fn rust_hazelnut_client_get_framebuffer(client: *mut ffi::c_void) -> *mut u32;
pub(crate) fn rust_hazelnut_client_get_width(client: *mut ffi::c_void) -> u32;
pub(crate) fn rust_hazelnut_client_get_height(client: *mut ffi::c_void) -> u32;
}
} }
extern "C" { pub type ResultCode = sys::ResultCode;
fn rust_new_hazelnut_client() -> *mut ffi::c_void;
fn rust_destroy_hazelnut_client(client: *mut ffi::c_void);
fn rust_hazelnut_client_open(client: *mut ffi::c_void, url: *const ffi::c_char) -> bool;
fn rust_hazelnut_client_tick(client: *mut ffi::c_void) -> ResultCode;
fn rust_hazelnut_client_lock(client: *mut ffi::c_void);
fn rust_hazelnut_client_unlock(client: *mut ffi::c_void);
fn rust_hazelnut_client_get_framebuffer(client: *mut ffi::c_void) -> *mut u32;
fn rust_hazelnut_client_get_width(client: *mut ffi::c_void) -> u32;
fn rust_hazelnut_client_get_height(client: *mut ffi::c_void) -> u32;
}
/// A Hazelnut IVSHMEM client. Wraps the C++ code (see ./rust_wrapper.cpp and the shared sources)
/// so that we can use it in Rust, semi-safely. (Note that I really do not like the current
/// state of these bindings, and they were written JUST to get working.
/// I am aware they are not very idiomatic and of other problems with them.)
pub struct HazelnutClient(*mut std::ffi::c_void); pub struct HazelnutClient(*mut std::ffi::c_void);
impl HazelnutClient { impl HazelnutClient {
/// Creates a new client.
pub fn new() -> HazelnutClient { pub fn new() -> HazelnutClient {
unsafe { Self(rust_new_hazelnut_client()) }
}
pub fn open(&mut self, path: String) -> bool {
let cstr = ffi::CString::new(path).expect("dumbass");
unsafe { unsafe {
return rust_hazelnut_client_open(self.0, cstr.as_ptr()); let client = sys::rust_new_hazelnut_client();
assert!(
!client.is_null(),
"C++ code failed to allocate a client object"
);
Self(client)
}
} }
/// Opens the given IVSHMEM shmem file.
pub fn open(&mut self, path: &String) -> bool {
let cstr = ffi::CString::new(path.clone()).expect("dumbass");
// FIXME: this really should work by FD so we can just return io::Result<> or something, but bleh
// for now it's "fine", also it's a path in the shared sources currently.
unsafe {
return sys::rust_hazelnut_client_open(self.0, cstr.as_ptr());
}
} }
/// Ticks this client.
///
/// # Notes
/// If this returns [ResultCode::Changed], then the Hazelnut lock (see [HazelnutClient::lock] and [HazelnutClient::unlock]) is still held,
/// so you will need to unlock after your processing and let the guest VM continue processing.
pub fn tick_one(&mut self) -> ResultCode { pub fn tick_one(&mut self) -> ResultCode {
unsafe { unsafe {
return rust_hazelnut_client_tick(self.0); return sys::rust_hazelnut_client_tick(self.0);
} }
} }
/// Explicitly lock the Hazelnut IVSHMEM memory.
/// During this, the guest VM will wait for us to release the lock, and we can read from the memory without
/// risk of it being changed.
pub fn lock(&mut self) { pub fn lock(&mut self) {
unsafe { unsafe {
return rust_hazelnut_client_lock(self.0); return sys::rust_hazelnut_client_lock(self.0);
} }
} }
/// Unlock the Hazelnut IVSHMEM memory.
pub fn unlock(&mut self) { pub fn unlock(&mut self) {
unsafe { unsafe {
return rust_hazelnut_client_unlock(self.0); return sys::rust_hazelnut_client_unlock(self.0);
} }
} }
/// only use while lock is held! /// Obtains the framebuffer dimensions.
///
/// # Safety
/// Only use while the Hazelnut atomic lock is held!
pub fn dimensions(&mut self) -> (u32, u32) { pub fn dimensions(&mut self) -> (u32, u32) {
let tup = unsafe { let tup = unsafe {
let width = rust_hazelnut_client_get_width(self.0); let width = sys::rust_hazelnut_client_get_width(self.0);
let height = rust_hazelnut_client_get_height(self.0); let height = sys::rust_hazelnut_client_get_height(self.0);
(width, height) (width, height)
}; };
tup tup
} }
/// only use while lock is held! /// Obtains a mutable slice containing the framebuffer.
///
/// # Safety
/// Only use while the Hazelnut atomic lock is held!
pub fn framebuffer(&mut self) -> &mut [u32] { pub fn framebuffer(&mut self) -> &mut [u32] {
let sl = unsafe { let sl = unsafe {
let fb_ptr = rust_hazelnut_client_get_framebuffer(self.0); let fb_ptr = sys::rust_hazelnut_client_get_framebuffer(self.0);
let dim = self.dimensions(); let dim = self.dimensions();
std::slice::from_raw_parts_mut(fb_ptr, ((dim.0 * dim.1) as usize * 4)) std::slice::from_raw_parts_mut(fb_ptr, (dim.0 * dim.1) as usize * 4)
}; };
sl sl
} }
@ -82,7 +124,7 @@ impl HazelnutClient {
impl Drop for HazelnutClient { impl Drop for HazelnutClient {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
rust_destroy_hazelnut_client(self.0); sys::rust_destroy_hazelnut_client(self.0);
} }
self.0 = std::ptr::null_mut(); self.0 = std::ptr::null_mut();
} }

View file

@ -5,21 +5,29 @@ mod hzclient;
fn main() { fn main() {
let mut screen_width: u32 = 320; let mut screen_width: u32 = 320;
let mut screen_height: u32 = 200; let mut screen_height: u32 = 200;
let mut window: Option<Window> = None;
let mut window = Window::new("FbcServer", 320, 200, WindowOptions::default())
.expect("you banned forever: rules do not");
let mut client = hzclient::HazelnutClient::new(); let mut client = hzclient::HazelnutClient::new();
if !client.open("/dev/shm/lg-win7".into()) { let socket_path: String = "/dev/shm/lg-win7".into();
println!("FUCK");
if !client.open(&socket_path) {
println!("Could not open IVSHMEM.");
return ();
} else { } else {
println!("Opened ivshmem!!!"); println!("Opened IVSHMEM device.");
} }
//let mut argb_buffer: Vec<u32> = Vec::new();
loop { loop {
match window.as_mut() {
Some(window) => {
if !window.is_open() {
break;
}
}
None => {}
}
match client.tick_one() { match client.tick_one() {
hzclient::ResultCode::Fail => break, hzclient::ResultCode::Fail => break,
hzclient::ResultCode::Changed => { hzclient::ResultCode::Changed => {
@ -29,30 +37,35 @@ fn main() {
screen_width = dims.0; screen_width = dims.0;
screen_height = dims.1; screen_height = dims.1;
window = Window::new( window = Some(
"FbcServer", Window::new(
&format!("Hazelnut client \"{socket_path}\" ({screen_width}x{screen_height})"),
screen_width as usize, screen_width as usize,
screen_height as usize, screen_height as usize,
WindowOptions::default(), WindowOptions::default(),
) )
.expect("you banned forever: rules do not"); .expect("Could not create window"),
);
} }
window.update_with_buffer( window
.as_mut()
.unwrap()
.update_with_buffer(
&client.framebuffer(), &client.framebuffer(),
screen_width as usize, screen_width as usize,
screen_height as usize, screen_height as usize,
).expect("well its done anyways"); )
.expect("Failed to update");
client.unlock(); client.unlock();
} }
hzclient::ResultCode::Unchanged => { hzclient::ResultCode::Unchanged => match window.as_mut() {
Some(window) => {
window.update(); window.update();
// Not needed, C++ unlocks us }
//client.unlock(); _ => {}
},
} }
} }
}
println!("Hello, world!");
} }