use std::{
ffi::{CStr, c_void},
marker::PhantomData,
};
#[repr(C)]
pub struct ComputerOptions {
pub battery_enabled: bool,
pub controller_enabled: bool,
pub cpu_enabled: bool,
pub gpu_enabled: bool,
pub memory_enabled: bool,
pub motherboard_enabled: bool,
pub network_enabled: bool,
pub psu_enabled: bool,
pub storage_enabled: bool,
}
type HardwarePtr = *const c_void;
type SensorPtr = *const c_void;
type ComputerPtr = *const c_void;
type Utf8Ptr = *const c_void;
#[repr(C)]
struct SharedFfiArrayPtr {
data: *const c_void,
handle: *const c_void,
}
#[repr(C)]
struct SharedFfiArray<T> {
length: u32,
ptr: SharedFfiArrayPtr,
_phantom: PhantomData<T>,
}
#[repr(C)]
union FfiResultUnion<O: Copy, E: Copy> {
ok_value: O,
err_value: E,
}
#[repr(C)]
struct FfiResult<O: Copy, E: Copy> {
is_ok: bool,
data: FfiResultUnion<O, E>,
}
#[cfg(feature = "static")]
#[used]
static FORCE_INCLUDE: unsafe extern "C" fn() = static_initialization;
#[link(name = "lhm-bridge")]
unsafe extern "C" {
#[cfg(feature = "static")]
#[link_name = "NativeAOT_StaticInitialization"]
pub fn static_initialization();
unsafe fn free_string(ptr: Utf8Ptr);
unsafe fn free_shared_array(ptr: SharedFfiArrayPtr);
unsafe fn create_computer() -> FfiResult<ComputerPtr, Utf8Ptr>;
unsafe fn update_computer(ptr: ComputerPtr);
unsafe fn free_computer(ptr: ComputerPtr);
unsafe fn set_computer_options(ptr: ComputerPtr, options: ComputerOptions);
unsafe fn get_computer_hardware(ptr: ComputerPtr) -> SharedFfiArray<HardwarePtr>;
unsafe fn get_hardware_identifier(ptr: HardwarePtr) -> Utf8Ptr;
unsafe fn get_hardware_name(ptr: HardwarePtr) -> Utf8Ptr;
unsafe fn get_hardware_type(ptr: HardwarePtr) -> i32;
unsafe fn get_hardware_children(ptr: HardwarePtr) -> SharedFfiArray<HardwarePtr>;
unsafe fn get_hardware_sensors(ptr: HardwarePtr) -> SharedFfiArray<SensorPtr>;
unsafe fn update_hardware(ptr: HardwarePtr);
unsafe fn free_hardware(ptr: HardwarePtr);
unsafe fn get_sensor_hardware(ptr: SensorPtr) -> HardwarePtr;
unsafe fn get_sensor_identifier(ptr: SensorPtr) -> Utf8Ptr;
unsafe fn get_sensor_name(ptr: SensorPtr) -> Utf8Ptr;
unsafe fn get_sensor_type(ptr: SensorPtr) -> i32;
unsafe fn get_sensor_value(ptr: SensorPtr) -> f32;
unsafe fn get_sensor_min(ptr: SensorPtr) -> f32;
unsafe fn get_sensor_max(ptr: SensorPtr) -> f32;
unsafe fn update_sensor(ptr: SensorPtr);
unsafe fn free_sensor(ptr: SensorPtr);
}
pub struct Computer {
ptr: ComputerPtr,
}
unsafe impl Send for Computer {}
impl Computer {
pub fn create() -> std::io::Result<Self> {
let result = unsafe { create_computer() };
if !result.is_ok {
let err_ptr = unsafe { result.data.err_value };
let err = copy_string(err_ptr);
return Err(std::io::Error::other(err));
}
let ptr = unsafe { result.data.ok_value };
Ok(Self { ptr })
}
pub fn update(&mut self) {
unsafe {
update_computer(self.ptr);
}
}
pub fn set_options(&mut self, options: ComputerOptions) {
unsafe {
set_computer_options(self.ptr, options);
}
}
pub fn hardware(&self) -> Vec<Hardware> {
let array = unsafe { get_computer_hardware(self.ptr) };
let slice =
unsafe { std::slice::from_raw_parts(array.ptr.data.cast(), array.length as usize) };
let children: Vec<Hardware> = slice
.iter()
.copied()
.map(|hardware_ptr| Hardware { ptr: hardware_ptr })
.collect();
unsafe { free_shared_array(array.ptr) }
children
}
}
impl Drop for Computer {
fn drop(&mut self) {
unsafe {
free_computer(self.ptr);
}
}
}
pub struct Hardware {
ptr: HardwarePtr,
}
unsafe impl Send for Hardware {}
impl Hardware {
pub fn identifier(&self) -> String {
copy_string(unsafe { get_hardware_identifier(self.ptr) })
}
pub fn name(&self) -> String {
copy_string(unsafe { get_hardware_name(self.ptr) })
}
pub fn get_type(&self) -> i32 {
unsafe { get_hardware_type(self.ptr) }
}
pub fn get_children(&self) -> Vec<Hardware> {
let array = unsafe { get_hardware_children(self.ptr) };
let slice =
unsafe { std::slice::from_raw_parts(array.ptr.data.cast(), array.length as usize) };
let children: Vec<Hardware> = slice
.iter()
.map(|hardware_ptr| Hardware { ptr: *hardware_ptr })
.collect();
unsafe { free_shared_array(array.ptr) }
children
}
pub fn sensors(&self) -> Vec<Sensor> {
let array = unsafe { get_hardware_sensors(self.ptr) };
let slice =
unsafe { std::slice::from_raw_parts(array.ptr.data.cast(), array.length as usize) };
let children: Vec<Sensor> = slice
.iter()
.map(|sensor_ptr| Sensor { ptr: *sensor_ptr })
.collect();
unsafe { free_shared_array(array.ptr) }
children
}
pub fn update(&mut self) {
unsafe { update_hardware(self.ptr) };
}
}
impl Drop for Hardware {
fn drop(&mut self) {
unsafe { free_hardware(self.ptr) };
}
}
pub struct Sensor {
ptr: SensorPtr,
}
unsafe impl Send for Sensor {}
impl Sensor {
pub fn hardware(&self) -> Hardware {
let ptr = unsafe { get_sensor_hardware(self.ptr) };
Hardware { ptr }
}
pub fn identifier(&self) -> String {
copy_string(unsafe { get_sensor_identifier(self.ptr) })
}
pub fn name(&self) -> String {
copy_string(unsafe { get_sensor_name(self.ptr) })
}
pub fn get_type(&self) -> i32 {
unsafe { get_sensor_type(self.ptr) }
}
pub fn value(&self) -> f32 {
unsafe { get_sensor_value(self.ptr) }
}
pub fn min(&self) -> f32 {
unsafe { get_sensor_min(self.ptr) }
}
pub fn max(&self) -> f32 {
unsafe { get_sensor_max(self.ptr) }
}
pub fn update(&mut self) {
unsafe { update_sensor(self.ptr) };
}
}
impl Drop for Sensor {
fn drop(&mut self) {
unsafe { free_sensor(self.ptr) };
}
}
fn copy_string(value_ptr: Utf8Ptr) -> String {
if value_ptr.is_null() {
return String::new();
}
let value = unsafe { CStr::from_ptr(value_ptr.cast()) };
let value = value.to_string_lossy().to_string();
unsafe { free_string(value_ptr) }
value
}