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 enum ResultCode {
pub(crate) mod sys {
use std::ffi;
#[repr(u32)]
#[allow(unused)] // it IS used, just not by Rust
pub enum ResultCode {
Unchanged,
Changed,
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" {
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;
}
pub type ResultCode = sys::ResultCode;
/// 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);
impl HazelnutClient {
/// Creates a new client.
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 {
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 {
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) {
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) {
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) {
let tup = unsafe {
let width = rust_hazelnut_client_get_width(self.0);
let height = rust_hazelnut_client_get_height(self.0);
let width = sys::rust_hazelnut_client_get_width(self.0);
let height = sys::rust_hazelnut_client_get_height(self.0);
(width, height)
};
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] {
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();
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
}
@ -82,7 +124,7 @@ impl HazelnutClient {
impl Drop for HazelnutClient {
fn drop(&mut self) {
unsafe {
rust_destroy_hazelnut_client(self.0);
sys::rust_destroy_hazelnut_client(self.0);
}
self.0 = std::ptr::null_mut();
}

View file

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