use std::cell::RefCell;
use std::ffi::CString;
use std::marker::PhantomData;
use std::os::raw::{c_char, c_void};
use xplm_sys::{
xplmType_Data, xplmType_Double, xplmType_Float, xplmType_FloatArray, xplmType_Int,
xplmType_IntArray, XPLMDataRef, XPLMGetDatab, XPLMRegisterDataAccessor, XPLMSetDatab,
XPLMUnregisterDataAccessor,
};
mod sealed {
pub trait Sealed {}
}
pub struct ReadOnlyMarker;
pub struct ReadWriteMarker;
impl sealed::Sealed for ReadOnlyMarker {}
impl sealed::Sealed for ReadWriteMarker {}
pub trait Access: sealed::Sealed {}
impl Access for ReadOnlyMarker {}
impl Access for ReadWriteMarker {}
pub trait Writable: Access {}
impl Writable for ReadWriteMarker {}
fn find_raw(name: &str) -> Option<XPLMDataRef> {
let c_name = CString::new(name).ok()?;
let raw = unsafe { xplm_sys::XPLMFindDataRef(c_name.as_ptr()) };
(!raw.is_null()).then_some(raw)
}
pub trait Scalar: sealed_scalar::Sealed + Copy {
#[doc(hidden)]
unsafe fn get_raw(raw: XPLMDataRef) -> Self;
#[doc(hidden)]
unsafe fn set_raw(raw: XPLMDataRef, value: Self);
}
mod sealed_scalar {
pub trait Sealed {}
impl Sealed for i32 {}
impl Sealed for f32 {}
impl Sealed for f64 {}
}
impl Scalar for i32 {
unsafe fn get_raw(raw: XPLMDataRef) -> Self {
unsafe { xplm_sys::XPLMGetDatai(raw) }
}
unsafe fn set_raw(raw: XPLMDataRef, value: Self) {
unsafe { xplm_sys::XPLMSetDatai(raw, value) }
}
}
impl Scalar for f32 {
unsafe fn get_raw(raw: XPLMDataRef) -> Self {
unsafe { xplm_sys::XPLMGetDataf(raw) }
}
unsafe fn set_raw(raw: XPLMDataRef, value: Self) {
unsafe { xplm_sys::XPLMSetDataf(raw, value) }
}
}
impl Scalar for f64 {
unsafe fn get_raw(raw: XPLMDataRef) -> Self {
unsafe { xplm_sys::XPLMGetDatad(raw) }
}
unsafe fn set_raw(raw: XPLMDataRef, value: Self) {
unsafe { xplm_sys::XPLMSetDatad(raw, value) }
}
}
pub struct DataRef<T: Scalar, A: Access> {
raw: XPLMDataRef,
_type: PhantomData<T>,
_access: PhantomData<A>,
}
unsafe impl<T: Scalar, A: Access> Send for DataRef<T, A> {}
impl<T: Scalar, A: Access> DataRef<T, A> {
pub fn find(name: &str) -> Option<Self> {
find_raw(name).map(|raw| Self {
raw,
_type: PhantomData,
_access: PhantomData,
})
}
pub fn get(&self) -> T {
unsafe { T::get_raw(self.raw) }
}
}
impl<T: Scalar, A: Writable> DataRef<T, A> {
pub fn set(&self, value: T) {
unsafe { T::set_raw(self.raw, value) }
}
}
pub type ReadOnly<T> = DataRef<T, ReadOnlyMarker>;
pub type ReadWrite<T> = DataRef<T, ReadWriteMarker>;
pub trait ArrayElement: sealed_array::Sealed + Copy + Default {
#[doc(hidden)]
unsafe fn get_raw(raw: XPLMDataRef, out: Option<&mut [Self]>, offset: i32) -> i32;
#[doc(hidden)]
unsafe fn set_raw(raw: XPLMDataRef, values: &[Self], offset: i32);
}
mod sealed_array {
pub trait Sealed {}
impl Sealed for i32 {}
impl Sealed for f32 {}
}
impl ArrayElement for i32 {
unsafe fn get_raw(raw: XPLMDataRef, out: Option<&mut [Self]>, offset: i32) -> i32 {
match out {
Some(buf) => unsafe {
xplm_sys::XPLMGetDatavi(raw, buf.as_mut_ptr(), offset, buf.len() as i32)
},
None => unsafe { xplm_sys::XPLMGetDatavi(raw, std::ptr::null_mut(), 0, 0) },
}
}
unsafe fn set_raw(raw: XPLMDataRef, values: &[Self], offset: i32) {
unsafe {
xplm_sys::XPLMSetDatavi(
raw,
values.as_ptr() as *mut i32,
offset,
values.len() as i32,
)
}
}
}
impl ArrayElement for f32 {
unsafe fn get_raw(raw: XPLMDataRef, out: Option<&mut [Self]>, offset: i32) -> i32 {
match out {
Some(buf) => unsafe {
xplm_sys::XPLMGetDatavf(raw, buf.as_mut_ptr(), offset, buf.len() as i32)
},
None => unsafe { xplm_sys::XPLMGetDatavf(raw, std::ptr::null_mut(), 0, 0) },
}
}
unsafe fn set_raw(raw: XPLMDataRef, values: &[Self], offset: i32) {
unsafe {
xplm_sys::XPLMSetDatavf(
raw,
values.as_ptr() as *mut f32,
offset,
values.len() as i32,
)
}
}
}
pub struct ArrayDataRef<T: ArrayElement, A: Access> {
raw: XPLMDataRef,
_type: PhantomData<T>,
_access: PhantomData<A>,
}
unsafe impl<T: ArrayElement, A: Access> Send for ArrayDataRef<T, A> {}
impl<T: ArrayElement, A: Access> ArrayDataRef<T, A> {
pub fn find(name: &str) -> Option<Self> {
find_raw(name).map(|raw| Self {
raw,
_type: PhantomData,
_access: PhantomData,
})
}
pub fn len(&self) -> usize {
unsafe { T::get_raw(self.raw, None, 0) as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, offset: usize, out: &mut [T]) -> usize {
unsafe { T::get_raw(self.raw, Some(out), offset as i32) as usize }
}
pub fn get_all(&self) -> Vec<T> {
let mut buf = vec![T::default(); self.len()];
let n = self.get(0, &mut buf);
buf.truncate(n);
buf
}
}
impl<T: ArrayElement, A: Writable> ArrayDataRef<T, A> {
pub fn set(&self, offset: usize, values: &[T]) {
unsafe { T::set_raw(self.raw, values, offset as i32) }
}
}
pub type ReadOnlyArray<T> = ArrayDataRef<T, ReadOnlyMarker>;
pub type ReadWriteArray<T> = ArrayDataRef<T, ReadWriteMarker>;
pub struct DataBytes<A: Access> {
raw: XPLMDataRef,
_access: PhantomData<A>,
}
unsafe impl<A: Access> Send for DataBytes<A> {}
impl<A: Access> DataBytes<A> {
pub fn find(name: &str) -> Option<Self> {
find_raw(name).map(|raw| Self {
raw,
_access: PhantomData,
})
}
pub fn len(&self) -> usize {
unsafe { XPLMGetDatab(self.raw, std::ptr::null_mut(), 0, 0) as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, offset: usize, out: &mut [u8]) -> usize {
unsafe {
XPLMGetDatab(
self.raw,
out.as_mut_ptr() as *mut c_void,
offset as i32,
out.len() as i32,
) as usize
}
}
pub fn get_all(&self) -> Vec<u8> {
let mut buf = vec![0u8; self.len()];
let n = self.get(0, &mut buf);
buf.truncate(n);
buf
}
}
impl<A: Writable> DataBytes<A> {
pub fn set(&self, offset: usize, values: &[u8]) {
unsafe {
XPLMSetDatab(
self.raw,
values.as_ptr() as *mut c_void,
offset as i32,
values.len() as i32,
)
}
}
}
pub type ReadOnlyBytes = DataBytes<ReadOnlyMarker>;
pub type ReadWriteBytes = DataBytes<ReadWriteMarker>;
struct Accessor<T> {
read: Box<dyn Fn() -> T>,
write: RefCell<Option<Box<dyn FnMut(T)>>>,
}
pub trait PublishScalar: Scalar + Default + sealed_publish::Sealed {
#[doc(hidden)]
unsafe fn register(name: *const c_char, writable: bool, accessor: *mut c_void) -> XPLMDataRef;
}
mod sealed_publish {
pub trait Sealed {}
impl Sealed for i32 {}
impl Sealed for f32 {}
impl Sealed for f64 {}
}
unsafe extern "C" fn read_i32(refcon: *mut c_void) -> i32 {
crate::guard(|| unsafe { ((&*(refcon as *const Accessor<i32>)).read)() }).unwrap_or_default()
}
unsafe extern "C" fn write_i32(refcon: *mut c_void, value: i32) {
crate::guard(|| unsafe {
if let Some(write) = (&*(refcon as *const Accessor<i32>))
.write
.borrow_mut()
.as_mut()
{
write(value);
}
});
}
impl PublishScalar for i32 {
unsafe fn register(name: *const c_char, writable: bool, accessor: *mut c_void) -> XPLMDataRef {
unsafe {
XPLMRegisterDataAccessor(
name,
xplmType_Int,
writable as i32,
Some(read_i32),
writable.then_some(write_i32 as _),
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
accessor,
if writable {
accessor
} else {
std::ptr::null_mut()
},
)
}
}
}
unsafe extern "C" fn read_f32(refcon: *mut c_void) -> f32 {
crate::guard(|| unsafe { ((&*(refcon as *const Accessor<f32>)).read)() }).unwrap_or_default()
}
unsafe extern "C" fn write_f32(refcon: *mut c_void, value: f32) {
crate::guard(|| unsafe {
if let Some(write) = (&*(refcon as *const Accessor<f32>))
.write
.borrow_mut()
.as_mut()
{
write(value);
}
});
}
impl PublishScalar for f32 {
unsafe fn register(name: *const c_char, writable: bool, accessor: *mut c_void) -> XPLMDataRef {
unsafe {
XPLMRegisterDataAccessor(
name,
xplmType_Float,
writable as i32,
None,
None,
Some(read_f32),
writable.then_some(write_f32 as _),
None,
None,
None,
None,
None,
None,
None,
None,
accessor,
if writable {
accessor
} else {
std::ptr::null_mut()
},
)
}
}
}
unsafe extern "C" fn read_f64(refcon: *mut c_void) -> f64 {
crate::guard(|| unsafe { ((&*(refcon as *const Accessor<f64>)).read)() }).unwrap_or_default()
}
unsafe extern "C" fn write_f64(refcon: *mut c_void, value: f64) {
crate::guard(|| unsafe {
if let Some(write) = (&*(refcon as *const Accessor<f64>))
.write
.borrow_mut()
.as_mut()
{
write(value);
}
});
}
impl PublishScalar for f64 {
unsafe fn register(name: *const c_char, writable: bool, accessor: *mut c_void) -> XPLMDataRef {
unsafe {
XPLMRegisterDataAccessor(
name,
xplmType_Double,
writable as i32,
None,
None,
None,
None,
Some(read_f64),
writable.then_some(write_f64 as _),
None,
None,
None,
None,
None,
None,
accessor,
if writable {
accessor
} else {
std::ptr::null_mut()
},
)
}
}
}
pub struct PublishedDataRef<T: PublishScalar, A: Access> {
raw: XPLMDataRef,
_accessor: Box<Accessor<T>>,
_type: PhantomData<T>,
_access: PhantomData<A>,
}
unsafe impl<T: PublishScalar, A: Access> Send for PublishedDataRef<T, A> {}
impl<T: PublishScalar> PublishedDataRef<T, ReadOnlyMarker> {
pub fn publish(name: &str, read: impl Fn() -> T + 'static) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let mut accessor = Box::new(Accessor {
read: Box::new(read),
write: RefCell::new(None),
});
let accessor_ptr: *mut c_void = &mut *accessor as *mut Accessor<T> as *mut c_void;
let raw = unsafe { T::register(c_name.as_ptr(), false, accessor_ptr) };
(!raw.is_null()).then_some(Self {
raw,
_accessor: accessor,
_type: PhantomData,
_access: PhantomData,
})
}
}
impl<T: PublishScalar> PublishedDataRef<T, ReadWriteMarker> {
pub fn publish_writable(
name: &str,
read: impl Fn() -> T + 'static,
write: impl FnMut(T) + 'static,
) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let mut accessor = Box::new(Accessor {
read: Box::new(read),
write: RefCell::new(Some(Box::new(write) as Box<dyn FnMut(T)>)),
});
let accessor_ptr: *mut c_void = &mut *accessor as *mut Accessor<T> as *mut c_void;
let raw = unsafe { T::register(c_name.as_ptr(), true, accessor_ptr) };
(!raw.is_null()).then_some(Self {
raw,
_accessor: accessor,
_type: PhantomData,
_access: PhantomData,
})
}
}
impl<T: PublishScalar, A: Access> Drop for PublishedDataRef<T, A> {
fn drop(&mut self) {
unsafe { XPLMUnregisterDataAccessor(self.raw) }
}
}
pub type PublishedReadOnly<T> = PublishedDataRef<T, ReadOnlyMarker>;
pub type PublishedReadWrite<T> = PublishedDataRef<T, ReadWriteMarker>;
struct DataAccessor {
read: Box<dyn Fn() -> Vec<u8>>,
write: RefCell<Option<Box<dyn FnMut(&[u8])>>>,
}
unsafe extern "C" fn read_data(
refcon: *mut c_void,
out: *mut c_void,
offset: i32,
max_length: i32,
) -> i32 {
crate::guard(|| unsafe {
let bytes = ((&*(refcon as *const DataAccessor)).read)();
if out.is_null() {
return bytes.len() as i32;
}
let offset = offset.max(0) as usize;
let Some(available) = bytes.get(offset..) else {
return 0;
};
let n = available.len().min(max_length.max(0) as usize);
std::ptr::copy_nonoverlapping(available.as_ptr(), out as *mut u8, n);
n as i32
})
.unwrap_or(0)
}
unsafe extern "C" fn write_data(
refcon: *mut c_void,
value: *mut c_void,
_offset: i32,
length: i32,
) {
crate::guard(|| unsafe {
if let Some(write) = (&*(refcon as *const DataAccessor))
.write
.borrow_mut()
.as_mut()
{
let length = length.max(0) as usize;
let slice = std::slice::from_raw_parts(value as *const u8, length);
write(slice);
}
});
}
fn register_data(name: *const c_char, writable: bool, accessor: *mut c_void) -> XPLMDataRef {
unsafe {
XPLMRegisterDataAccessor(
name,
xplmType_Data,
writable as i32,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
Some(read_data),
writable.then_some(write_data as _),
accessor,
if writable {
accessor
} else {
std::ptr::null_mut()
},
)
}
}
pub struct PublishedData<A: Access> {
raw: XPLMDataRef,
_accessor: Box<DataAccessor>,
_access: PhantomData<A>,
}
unsafe impl<A: Access> Send for PublishedData<A> {}
impl PublishedData<ReadOnlyMarker> {
pub fn publish(name: &str, read: impl Fn() -> Vec<u8> + 'static) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let mut accessor = Box::new(DataAccessor {
read: Box::new(read),
write: RefCell::new(None),
});
let accessor_ptr: *mut c_void = &mut *accessor as *mut DataAccessor as *mut c_void;
let raw = register_data(c_name.as_ptr(), false, accessor_ptr);
(!raw.is_null()).then_some(Self {
raw,
_accessor: accessor,
_access: PhantomData,
})
}
}
impl PublishedData<ReadWriteMarker> {
pub fn publish_writable(
name: &str,
read: impl Fn() -> Vec<u8> + 'static,
write: impl FnMut(&[u8]) + 'static,
) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let mut accessor = Box::new(DataAccessor {
read: Box::new(read),
write: RefCell::new(Some(Box::new(write) as Box<dyn FnMut(&[u8])>)),
});
let accessor_ptr: *mut c_void = &mut *accessor as *mut DataAccessor as *mut c_void;
let raw = register_data(c_name.as_ptr(), true, accessor_ptr);
(!raw.is_null()).then_some(Self {
raw,
_accessor: accessor,
_access: PhantomData,
})
}
}
impl<A: Access> Drop for PublishedData<A> {
fn drop(&mut self) {
unsafe { XPLMUnregisterDataAccessor(self.raw) }
}
}
pub type PublishedDataReadOnly = PublishedData<ReadOnlyMarker>;
pub type PublishedDataReadWrite = PublishedData<ReadWriteMarker>;
pub unsafe trait PackedDataRef: Copy {}
pub struct DataStruct<T: PackedDataRef, A: Access> {
raw: XPLMDataRef,
_type: PhantomData<T>,
_access: PhantomData<A>,
}
unsafe impl<T: PackedDataRef, A: Access> Send for DataStruct<T, A> {}
impl<T: PackedDataRef, A: Access> DataStruct<T, A> {
pub fn find(name: &str) -> Option<Self> {
find_raw(name).map(|raw| Self {
raw,
_type: PhantomData,
_access: PhantomData,
})
}
pub fn get(&self) -> Option<T> {
let size = std::mem::size_of::<T>();
let len = unsafe { XPLMGetDatab(self.raw, std::ptr::null_mut(), 0, 0) as usize };
if len != size {
return None;
}
let mut buf = vec![0u8; size];
let n = unsafe {
XPLMGetDatab(self.raw, buf.as_mut_ptr() as *mut c_void, 0, size as i32) as usize
};
if n != size {
return None;
}
Some(unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) })
}
pub fn get_bytes(&self, byte_offset: usize, out: &mut [u8]) -> usize {
unsafe {
XPLMGetDatab(
self.raw,
out.as_mut_ptr() as *mut c_void,
byte_offset as i32,
out.len() as i32,
) as usize
}
}
pub fn get_field<F: Copy>(&self, byte_offset: usize) -> Option<F> {
let size = std::mem::size_of::<F>();
let mut buf = vec![0u8; size];
if self.get_bytes(byte_offset, &mut buf) != size {
return None;
}
Some(unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const F) })
}
}
impl<T: PackedDataRef, A: Writable> DataStruct<T, A> {
pub fn set(&self, value: T) {
self.set_bytes(0, unsafe {
std::slice::from_raw_parts(&value as *const T as *const u8, std::mem::size_of::<T>())
});
}
pub fn set_bytes(&self, byte_offset: usize, bytes: &[u8]) {
unsafe {
XPLMSetDatab(
self.raw,
bytes.as_ptr() as *mut c_void,
byte_offset as i32,
bytes.len() as i32,
)
}
}
pub fn set_field<F: Copy>(&self, byte_offset: usize, value: F) {
self.set_bytes(byte_offset, unsafe {
std::slice::from_raw_parts(&value as *const F as *const u8, std::mem::size_of::<F>())
});
}
}
pub type ReadOnlyStruct<T> = DataStruct<T, ReadOnlyMarker>;
pub type ReadWriteStruct<T> = DataStruct<T, ReadWriteMarker>;
struct StructAccessor<T> {
read: Box<dyn Fn() -> T>,
write: RefCell<Option<Box<dyn FnMut(T)>>>,
}
unsafe extern "C" fn read_struct<T: PackedDataRef>(
refcon: *mut c_void,
out: *mut c_void,
offset: i32,
max_length: i32,
) -> i32 {
crate::guard(|| unsafe {
let value = ((&*(refcon as *const StructAccessor<T>)).read)();
let size = std::mem::size_of::<T>() as i32;
if out.is_null() {
return size;
}
let offset = offset.max(0);
if offset >= size {
return 0;
}
let bytes = std::slice::from_raw_parts(&value as *const T as *const u8, size as usize);
let avail = &bytes[offset as usize..];
let n = avail.len().min(max_length.max(0) as usize);
std::ptr::copy_nonoverlapping(avail.as_ptr(), out as *mut u8, n);
n as i32
})
.unwrap_or(0)
}
unsafe extern "C" fn write_struct<T: PackedDataRef>(
refcon: *mut c_void,
value: *mut c_void,
offset: i32,
length: i32,
) {
crate::guard(|| unsafe {
let size = std::mem::size_of::<T>();
let offset = offset.max(0) as usize;
let length = length.max(0) as usize;
if offset > size || length > size - offset {
return;
}
let accessor = &*(refcon as *const StructAccessor<T>);
if accessor.write.borrow().is_none() {
return; }
let current = (accessor.read)();
let mut bytes =
std::slice::from_raw_parts(¤t as *const T as *const u8, size).to_vec();
let incoming = std::slice::from_raw_parts(value as *const u8, length);
bytes[offset..offset + length].copy_from_slice(incoming);
let decoded = std::ptr::read_unaligned(bytes.as_ptr() as *const T);
if let Some(write) = accessor.write.borrow_mut().as_mut() {
write(decoded);
}
});
}
fn register_struct<T: PackedDataRef>(
name: *const c_char,
writable: bool,
accessor: *mut c_void,
) -> XPLMDataRef {
unsafe {
XPLMRegisterDataAccessor(
name,
xplmType_Data,
writable as i32,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
Some(read_struct::<T>),
if writable {
Some(write_struct::<T>)
} else {
None
},
accessor,
if writable {
accessor
} else {
std::ptr::null_mut()
},
)
}
}
pub struct PublishedStruct<T: PackedDataRef, A: Access> {
raw: XPLMDataRef,
_accessor: Box<StructAccessor<T>>,
_access: PhantomData<A>,
}
unsafe impl<T: PackedDataRef, A: Access> Send for PublishedStruct<T, A> {}
impl<T: PackedDataRef> PublishedStruct<T, ReadOnlyMarker> {
pub fn publish(name: &str, read: impl Fn() -> T + 'static) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let mut accessor = Box::new(StructAccessor {
read: Box::new(read),
write: RefCell::new(None),
});
let accessor_ptr: *mut c_void = &mut *accessor as *mut StructAccessor<T> as *mut c_void;
let raw = register_struct::<T>(c_name.as_ptr(), false, accessor_ptr);
(!raw.is_null()).then_some(Self {
raw,
_accessor: accessor,
_access: PhantomData,
})
}
}
impl<T: PackedDataRef> PublishedStruct<T, ReadWriteMarker> {
pub fn publish_writable(
name: &str,
read: impl Fn() -> T + 'static,
write: impl FnMut(T) + 'static,
) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let mut accessor = Box::new(StructAccessor {
read: Box::new(read),
write: RefCell::new(Some(Box::new(write) as Box<dyn FnMut(T)>)),
});
let accessor_ptr: *mut c_void = &mut *accessor as *mut StructAccessor<T> as *mut c_void;
let raw = register_struct::<T>(c_name.as_ptr(), true, accessor_ptr);
(!raw.is_null()).then_some(Self {
raw,
_accessor: accessor,
_access: PhantomData,
})
}
}
impl<T: PackedDataRef, A: Access> Drop for PublishedStruct<T, A> {
fn drop(&mut self) {
unsafe { XPLMUnregisterDataAccessor(self.raw) }
}
}
pub type PublishedStructReadOnly<T> = PublishedStruct<T, ReadOnlyMarker>;
pub type PublishedStructReadWrite<T> = PublishedStruct<T, ReadWriteMarker>;
pub trait PublishArrayElement: ArrayElement + sealed_publish_array::Sealed {
#[doc(hidden)]
unsafe fn register(name: *const c_char, writable: bool, accessor: *mut c_void) -> XPLMDataRef;
}
mod sealed_publish_array {
pub trait Sealed {}
impl Sealed for i32 {}
impl Sealed for f32 {}
}
struct ArrayAccessor<T> {
read: Box<dyn Fn() -> Vec<T>>,
write: RefCell<Option<Box<dyn FnMut(usize, &[T])>>>,
}
unsafe extern "C" fn read_array_i32(
refcon: *mut c_void,
out_values: *mut i32,
offset: i32,
max: i32,
) -> i32 {
crate::guard(|| unsafe {
let values = ((&*(refcon as *const ArrayAccessor<i32>)).read)();
if out_values.is_null() {
return values.len() as i32;
}
let offset = offset.max(0) as usize;
let Some(avail) = values.get(offset..) else {
return 0;
};
let n = avail.len().min(max.max(0) as usize);
std::ptr::copy_nonoverlapping(avail.as_ptr(), out_values, n);
n as i32
})
.unwrap_or(0)
}
unsafe extern "C" fn write_array_i32(
refcon: *mut c_void,
values: *mut i32,
offset: i32,
count: i32,
) {
crate::guard(|| unsafe {
if let Some(write) = (&*(refcon as *const ArrayAccessor<i32>))
.write
.borrow_mut()
.as_mut()
{
let offset = offset.max(0) as usize;
let count = count.max(0) as usize;
let slice = std::slice::from_raw_parts(values, count);
write(offset, slice);
}
});
}
impl PublishArrayElement for i32 {
unsafe fn register(name: *const c_char, writable: bool, accessor: *mut c_void) -> XPLMDataRef {
unsafe {
XPLMRegisterDataAccessor(
name,
xplmType_IntArray,
writable as i32,
None,
None,
None,
None,
None,
None,
Some(read_array_i32),
writable.then_some(write_array_i32 as _),
None,
None,
None,
None,
accessor,
if writable {
accessor
} else {
std::ptr::null_mut()
},
)
}
}
}
unsafe extern "C" fn read_array_f32(
refcon: *mut c_void,
out_values: *mut f32,
offset: i32,
max: i32,
) -> i32 {
crate::guard(|| unsafe {
let values = ((&*(refcon as *const ArrayAccessor<f32>)).read)();
if out_values.is_null() {
return values.len() as i32;
}
let offset = offset.max(0) as usize;
let Some(avail) = values.get(offset..) else {
return 0;
};
let n = avail.len().min(max.max(0) as usize);
std::ptr::copy_nonoverlapping(avail.as_ptr(), out_values, n);
n as i32
})
.unwrap_or(0)
}
unsafe extern "C" fn write_array_f32(
refcon: *mut c_void,
values: *mut f32,
offset: i32,
count: i32,
) {
crate::guard(|| unsafe {
if let Some(write) = (&*(refcon as *const ArrayAccessor<f32>))
.write
.borrow_mut()
.as_mut()
{
let offset = offset.max(0) as usize;
let count = count.max(0) as usize;
let slice = std::slice::from_raw_parts(values, count);
write(offset, slice);
}
});
}
impl PublishArrayElement for f32 {
unsafe fn register(name: *const c_char, writable: bool, accessor: *mut c_void) -> XPLMDataRef {
unsafe {
XPLMRegisterDataAccessor(
name,
xplmType_FloatArray,
writable as i32,
None,
None,
None,
None,
None,
None,
None,
None,
Some(read_array_f32),
writable.then_some(write_array_f32 as _),
None,
None,
accessor,
if writable {
accessor
} else {
std::ptr::null_mut()
},
)
}
}
}
pub struct PublishedArray<T: PublishArrayElement, A: Access> {
raw: XPLMDataRef,
_accessor: Box<ArrayAccessor<T>>,
_access: PhantomData<A>,
}
unsafe impl<T: PublishArrayElement, A: Access> Send for PublishedArray<T, A> {}
impl<T: PublishArrayElement> PublishedArray<T, ReadOnlyMarker> {
pub fn publish(name: &str, read: impl Fn() -> Vec<T> + 'static) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let mut accessor = Box::new(ArrayAccessor {
read: Box::new(read),
write: RefCell::new(None),
});
let accessor_ptr: *mut c_void = &mut *accessor as *mut ArrayAccessor<T> as *mut c_void;
let raw = unsafe { T::register(c_name.as_ptr(), false, accessor_ptr) };
(!raw.is_null()).then_some(Self {
raw,
_accessor: accessor,
_access: PhantomData,
})
}
}
impl<T: PublishArrayElement> PublishedArray<T, ReadWriteMarker> {
pub fn publish_writable(
name: &str,
read: impl Fn() -> Vec<T> + 'static,
write: impl FnMut(usize, &[T]) + 'static,
) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let mut accessor = Box::new(ArrayAccessor {
read: Box::new(read),
write: RefCell::new(Some(Box::new(write) as Box<dyn FnMut(usize, &[T])>)),
});
let accessor_ptr: *mut c_void = &mut *accessor as *mut ArrayAccessor<T> as *mut c_void;
let raw = unsafe { T::register(c_name.as_ptr(), true, accessor_ptr) };
(!raw.is_null()).then_some(Self {
raw,
_accessor: accessor,
_access: PhantomData,
})
}
}
impl<T: PublishArrayElement, A: Access> Drop for PublishedArray<T, A> {
fn drop(&mut self) {
unsafe { XPLMUnregisterDataAccessor(self.raw) }
}
}
pub type PublishedArrayReadOnly<T> = PublishedArray<T, ReadOnlyMarker>;
pub type PublishedArrayReadWrite<T> = PublishedArray<T, ReadWriteMarker>;