use std::ffi::{c_void, CString};
use std::os::raw::c_char;
use xplm_sys::{
XPLMAcquirePlanes, XPLMCountAircraft, XPLMDisableAIForPlane, XPLMGetNthAircraftModel,
XPLMPlaceUserAtAirport, XPLMPluginID, XPLMReleasePlanes, XPLMSetActiveAircraftCount,
XPLMSetAircraftModel, XPLMSetUsersAircraft,
};
#[cfg(feature = "XPLM300")]
use xplm_sys::XPLMPlaceUserAtLocation;
pub const USER_AIRCRAFT: i32 = 0;
pub fn set_users_aircraft(path: &str) {
let Ok(c_path) = CString::new(path) else {
return;
};
unsafe { XPLMSetUsersAircraft(c_path.as_ptr()) }
}
pub fn place_user_at_airport(airport_code: &str) {
let Ok(c_code) = CString::new(airport_code) else {
return;
};
unsafe { XPLMPlaceUserAtAirport(c_code.as_ptr()) }
}
#[cfg(feature = "XPLM300")]
pub fn place_user_at_location(
latitude_degrees: f64,
longitude_degrees: f64,
elevation_meters_msl: f32,
heading_degrees_true: f32,
speed_meters_per_second: f32,
) {
unsafe {
XPLMPlaceUserAtLocation(
latitude_degrees,
longitude_degrees,
elevation_meters_msl,
heading_degrees_true,
speed_meters_per_second,
)
}
}
pub fn aircraft_count() -> (i32, i32, XPLMPluginID) {
let (mut total, mut active, mut controller) = (0, 0, 0);
unsafe { XPLMCountAircraft(&mut total, &mut active, &mut controller) };
(total, active, controller)
}
pub fn nth_aircraft_model(index: i32) -> (String, String) {
let mut file_name = [0u8; 256];
let mut path = [0u8; 512];
unsafe {
XPLMGetNthAircraftModel(
index,
file_name.as_mut_ptr() as *mut c_char,
path.as_mut_ptr() as *mut c_char,
)
};
(read_c_buf(&file_name), read_c_buf(&path))
}
fn read_c_buf(buf: &[u8]) -> String {
let end = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
String::from_utf8_lossy(&buf[..end]).into_owned()
}
type AvailableCallback = dyn FnOnce() + 'static;
pub struct AircraftAccess {
_private: (),
}
unsafe impl Send for AircraftAccess {}
impl AircraftAccess {
pub fn acquire(
aircraft_paths: Option<&[&str]>,
on_available: Option<impl FnOnce() + 'static>,
) -> Option<Self> {
let c_strings: Option<Vec<CString>> = aircraft_paths
.map(|paths| paths.iter().map(|p| CString::new(*p)).collect())
.transpose()
.ok()
.flatten();
let mut ptrs: Option<Vec<*mut c_char>> = c_strings.as_ref().map(|strings| {
let mut ptrs: Vec<*mut c_char> =
strings.iter().map(|s| s.as_ptr() as *mut c_char).collect();
ptrs.push(std::ptr::null_mut());
ptrs
});
let paths_ptr = ptrs
.as_mut()
.map_or(std::ptr::null_mut(), |p| p.as_mut_ptr());
let refcon: *mut c_void = match on_available {
Some(callback) => {
let boxed: Box<AvailableCallback> = Box::new(callback);
Box::into_raw(Box::new(boxed)) as *mut c_void
}
None => std::ptr::null_mut(),
};
let trampoline = if refcon.is_null() {
None
} else {
Some(planes_available_trampoline as _)
};
let granted = unsafe { XPLMAcquirePlanes(paths_ptr, trampoline, refcon) };
if granted != 0 {
if !refcon.is_null() {
unsafe { drop(Box::from_raw(refcon as *mut Box<AvailableCallback>)) };
}
Some(Self { _private: () })
} else {
None
}
}
pub fn set_active_aircraft_count(&self, count: i32) {
unsafe { XPLMSetActiveAircraftCount(count) }
}
pub fn set_aircraft_model(&self, index: i32, aircraft_path: &str) {
let Ok(c_path) = CString::new(aircraft_path) else {
return;
};
unsafe { XPLMSetAircraftModel(index, c_path.as_ptr()) }
}
pub fn disable_ai_for_plane(&self, plane_index: i32) {
unsafe { XPLMDisableAIForPlane(plane_index) }
}
}
impl Drop for AircraftAccess {
fn drop(&mut self) {
unsafe { XPLMReleasePlanes() }
}
}
unsafe extern "C" fn planes_available_trampoline(refcon: *mut c_void) {
crate::guard(|| {
let callback: Box<AvailableCallback> =
*unsafe { Box::from_raw(refcon as *mut Box<AvailableCallback>) };
callback();
});
}