use xplm_sys;
use std::f32;
use std::fmt;
use std::mem;
use std::ops::DerefMut;
use std::os::raw::*;
use std::time::Duration;
#[derive(Debug)]
pub struct FlightLoop {
data: Box<LoopData>,
}
impl FlightLoop {
pub fn new<C: FlightLoopCallback>(callback: C) -> Self {
let mut data = Box::new(LoopData::new(callback));
let data_ptr: *mut LoopData = data.deref_mut();
let mut config = xplm_sys::XPLMCreateFlightLoop_t {
structSize: mem::size_of::<xplm_sys::XPLMCreateFlightLoop_t>() as c_int,
phase: xplm_sys::xplm_FlightLoop_Phase_AfterFlightModel as i32,
callbackFunc: Some(flight_loop_callback::<C>),
refcon: data_ptr as *mut c_void,
};
data.loop_id = unsafe { Some(xplm_sys::XPLMCreateFlightLoop(&mut config)) };
FlightLoop { data }
}
pub fn schedule_immediate(&mut self) {
self.data.set_interval(LoopResult::Loops(1))
}
pub fn schedule_after_loops(&mut self, loops: u32) {
self.data.set_interval(LoopResult::Loops(loops));
}
pub fn schedule_after(&mut self, time: Duration) {
let seconds_f = (time.as_secs() as f32) + (1e-9_f32 * time.subsec_nanos() as f32);
self.data.set_interval(LoopResult::Seconds(seconds_f));
}
pub fn deactivate(&mut self) {
self.data.set_interval(LoopResult::Deactivate);
}
}
struct LoopData {
loop_result: Option<LoopResult>,
loop_id: Option<xplm_sys::XPLMFlightLoopID>,
callback: Box<dyn FlightLoopCallback>,
}
impl fmt::Debug for LoopData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("LoopData")
.field("loop_result", &self.loop_result)
.field("loop_id", &self.loop_id)
.field("callback", &String::from("[callback]"))
.finish()
}
}
impl LoopData {
pub fn new<C: FlightLoopCallback>(callback: C) -> Self {
LoopData {
loop_result: None,
loop_id: None,
callback: Box::new(callback),
}
}
fn set_interval(&mut self, loop_result: LoopResult) {
let loop_id = self.loop_id.expect("Loop ID not set");
unsafe { xplm_sys::XPLMScheduleFlightLoop(loop_id, loop_result.clone().into(), 1) };
self.loop_result = Some(loop_result);
}
}
impl Drop for LoopData {
fn drop(&mut self) {
if let Some(loop_id) = self.loop_id {
unsafe { xplm_sys::XPLMDestroyFlightLoop(loop_id) }
}
}
}
pub trait FlightLoopCallback: 'static {
fn flight_loop(&mut self, state: &mut LoopState);
}
impl<F> FlightLoopCallback for F
where
F: 'static + FnMut(&mut LoopState),
{
fn flight_loop(&mut self, state: &mut LoopState) {
self(state)
}
}
#[derive(Debug)]
pub struct LoopState<'a> {
since_call: Duration,
since_loop: Duration,
counter: i32,
result: &'a mut LoopResult,
}
impl<'a> LoopState<'a> {
pub fn since_last_call(&self) -> Duration {
self.since_call
}
pub fn since_last_loop(&self) -> Duration {
self.since_loop
}
pub fn counter(&self) -> i32 {
self.counter
}
pub fn deactivate(&mut self) {
*self.result = LoopResult::Deactivate;
}
pub fn call_next_loop(&mut self) {
*self.result = LoopResult::Loops(1);
}
pub fn call_after_loops(&mut self, loops: u32) {
*self.result = LoopResult::Loops(loops);
}
pub fn call_after(&mut self, time: Duration) {
let seconds_f = (time.as_secs() as f32) + (1e-9_f32 * time.subsec_nanos() as f32);
*self.result = LoopResult::Seconds(seconds_f);
}
}
#[derive(Debug, Clone)]
enum LoopResult {
Seconds(f32),
Loops(u32),
Deactivate,
}
impl From<LoopResult> for f32 {
fn from(lr: LoopResult) -> Self {
match lr {
LoopResult::Deactivate => 0f32,
LoopResult::Seconds(secs) => secs,
LoopResult::Loops(loops) => -1.0f32 * (loops as f32),
}
}
}
unsafe extern "C" fn flight_loop_callback<C: FlightLoopCallback>(
since_last_call: c_float,
since_loop: c_float,
counter: c_int,
refcon: *mut c_void,
) -> c_float {
let loop_data = refcon as *mut LoopData;
let mut state = LoopState {
since_call: secs_to_duration(since_last_call),
since_loop: secs_to_duration(since_loop),
counter,
result: (*loop_data).loop_result.as_mut().unwrap(),
};
let callback_ptr: *mut dyn FlightLoopCallback = (*loop_data).callback.as_mut();
let callback = callback_ptr as *mut C;
(*callback).flight_loop(&mut state);
f32::from(state.result.clone())
}
fn secs_to_duration(time: f32) -> Duration {
let seconds = time.trunc() as u64;
let nanoseconds = (time.fract() * 1e9_f32) as u32;
Duration::new(seconds, nanoseconds)
}