use std::ffi::c_void;
use std::os::raw::c_int;
use xplm_sys::{
xplm_ControlCameraForever, xplm_ControlCameraUntilViewChanges, XPLMCameraPosition_t,
XPLMControlCamera, XPLMDontControlCamera,
};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CameraPosition {
pub x: f32,
pub y: f32,
pub z: f32,
pub pitch: f32,
pub heading: f32,
pub roll: f32,
pub zoom: f32,
}
impl From<XPLMCameraPosition_t> for CameraPosition {
fn from(raw: XPLMCameraPosition_t) -> Self {
Self {
x: raw.x,
y: raw.y,
z: raw.z,
pitch: raw.pitch,
heading: raw.heading,
roll: raw.roll,
zoom: raw.zoom,
}
}
}
impl From<CameraPosition> for XPLMCameraPosition_t {
fn from(pos: CameraPosition) -> Self {
Self {
x: pos.x,
y: pos.y,
z: pos.z,
pitch: pos.pitch,
heading: pos.heading,
roll: pos.roll,
zoom: pos.zoom,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CameraControlDuration {
UntilViewChanges,
Forever,
}
impl From<CameraControlDuration> for xplm_sys::XPLMCameraControlDuration {
fn from(d: CameraControlDuration) -> Self {
match d {
CameraControlDuration::UntilViewChanges => xplm_ControlCameraUntilViewChanges,
CameraControlDuration::Forever => xplm_ControlCameraForever,
}
}
}
type Callback = dyn FnMut(bool) -> Option<CameraPosition> + 'static;
pub struct CameraControl {
refcon: *mut Box<Callback>,
}
unsafe impl Send for CameraControl {}
impl CameraControl {
pub fn take(
duration: CameraControlDuration,
callback: impl FnMut(bool) -> Option<CameraPosition> + 'static,
) -> Self {
let boxed: Box<Callback> = Box::new(callback);
let refcon = Box::into_raw(Box::new(boxed));
unsafe {
XPLMControlCamera(duration.into(), Some(trampoline), refcon as *mut c_void);
}
Self { refcon }
}
}
impl Drop for CameraControl {
fn drop(&mut self) {
unsafe {
XPLMDontControlCamera();
drop(Box::from_raw(self.refcon));
}
}
}
unsafe extern "C" fn trampoline(
out_camera_position: *mut XPLMCameraPosition_t,
in_is_losing_control: c_int,
refcon: *mut c_void,
) -> c_int {
crate::guard(|| {
let callback: &mut Callback = unsafe { &mut *(*(refcon as *mut Box<Callback>)) };
match callback(in_is_losing_control != 0) {
Some(pos) if !out_camera_position.is_null() => {
unsafe { *out_camera_position = pos.into() };
1
}
_ => 0,
}
})
.unwrap_or(0)
}