use std::ffi::{c_void, CString};
use std::os::raw::c_int;
use xplm_sys::{
xplm_CommandBegin, xplm_CommandEnd, XPLMCommandBegin, XPLMCommandEnd, XPLMCommandOnce,
XPLMCommandPhase, XPLMCommandRef, XPLMCreateCommand, XPLMFindCommand,
XPLMRegisterCommandHandler, XPLMUnregisterCommandHandler,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CommandPhase {
Begin,
Continue,
End,
}
impl From<XPLMCommandPhase> for CommandPhase {
fn from(raw: XPLMCommandPhase) -> Self {
if raw == xplm_CommandBegin {
CommandPhase::Begin
} else if raw == xplm_CommandEnd {
CommandPhase::End
} else {
CommandPhase::Continue
}
}
}
#[derive(Clone, Copy)]
pub struct Command(XPLMCommandRef);
impl Command {
pub fn find(name: &str) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let raw = unsafe { XPLMFindCommand(c_name.as_ptr()) };
(!raw.is_null()).then_some(Self(raw))
}
pub fn create(name: &str, description: &str) -> Option<Self> {
let c_name = CString::new(name).ok()?;
let c_description = CString::new(description).ok()?;
let raw = unsafe { XPLMCreateCommand(c_name.as_ptr(), c_description.as_ptr()) };
(!raw.is_null()).then_some(Self(raw))
}
pub fn begin(&self) {
unsafe { XPLMCommandBegin(self.0) }
}
pub fn end(&self) {
unsafe { XPLMCommandEnd(self.0) }
}
pub fn once(&self) {
unsafe { XPLMCommandOnce(self.0) }
}
pub fn register_handler(
&self,
before: bool,
callback: impl FnMut(CommandPhase) -> bool + 'static,
) -> CommandHandler {
CommandHandler::register(*self, before, callback)
}
}
type Callback = dyn FnMut(CommandPhase) -> bool + 'static;
pub struct CommandHandler {
command: XPLMCommandRef,
before: c_int,
refcon: *mut Box<Callback>,
}
unsafe impl Send for CommandHandler {}
impl CommandHandler {
fn register(
command: Command,
before: bool,
callback: impl FnMut(CommandPhase) -> bool + 'static,
) -> Self {
let boxed: Box<Callback> = Box::new(callback);
let refcon = Box::into_raw(Box::new(boxed));
let before = before as c_int;
unsafe {
XPLMRegisterCommandHandler(command.0, Some(trampoline), before, refcon as *mut c_void);
}
Self {
command: command.0,
before,
refcon,
}
}
}
impl Drop for CommandHandler {
fn drop(&mut self) {
unsafe {
XPLMUnregisterCommandHandler(
self.command,
Some(trampoline),
self.before,
self.refcon as *mut c_void,
);
drop(Box::from_raw(self.refcon));
}
}
}
unsafe extern "C" fn trampoline(
_command: XPLMCommandRef,
phase: XPLMCommandPhase,
refcon: *mut c_void,
) -> c_int {
crate::guard(|| {
let callback: &mut Callback = unsafe { &mut *(*(refcon as *mut Box<Callback>)) };
callback(phase.into())
})
.unwrap_or(true) as c_int
}