use std::ffi::c_int;
use std::{
ffi::{c_void, CStr, CString},
marker::PhantomData,
path::Path,
};
use snafu::prelude::*;
use xplane_sys::{self, XPLMGetPluginInfo};
use crate::{ffi::StringBuffer, message::MessageId, NoSendSync};
pub struct Plugins {
next: c_int,
count: c_int,
_phantom: NoSendSync,
}
#[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
impl Iterator for Plugins {
type Item = Plugin;
fn next(&mut self) -> Option<Self::Item> {
if self.next < self.count {
let id = unsafe { xplane_sys::XPLMGetNthPlugin(self.next) };
self.next += 1;
if id == xplane_sys::XPLM_PLUGIN_XPLANE as xplane_sys::XPLMPluginID {
self.next()
} else {
Some(unsafe { get_plugin(id) })
}
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = (self.count - self.next) as usize;
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for Plugins {}
pub struct Plugin {
id: xplane_sys::XPLMPluginID,
name: CString,
file_path: CString,
signature: CString,
description: CString,
_phantom: NoSendSync,
}
#[derive(Debug, Snafu)]
#[snafu(display("The requested plugin could not be enabled."))]
pub struct PluginEnableError;
impl Plugin {
#[must_use]
pub fn name_c(&self) -> &CStr {
&self.name
}
#[must_use]
pub fn signature_c(&self) -> &CStr {
&self.signature
}
#[must_use]
pub fn description_c(&self) -> &CStr {
&self.description
}
#[must_use]
pub fn path_c(&self) -> &CStr {
&self.file_path
}
#[must_use]
pub fn is_enabled(&mut self) -> bool {
unsafe { xplane_sys::XPLMIsPluginEnabled(self.id) == 1 }
}
pub fn enable(&mut self) -> Result<(), PluginEnableError> {
unsafe {
if xplane_sys::XPLMEnablePlugin(self.id) == 1 {
Ok(())
} else {
Err(PluginEnableError)
}
}
}
pub fn disable(&mut self) {
unsafe {
xplane_sys::XPLMDisablePlugin(self.id);
}
}
pub unsafe fn send_message(&mut self, message_id: MessageId, param: *mut c_void) {
unsafe {
xplane_sys::XPLMSendMessageToPlugin(self.id, message_id.into(), param);
}
}
}
unsafe fn get_plugin(id: xplane_sys::XPLMPluginID) -> Plugin {
let mut name = StringBuffer::new(257);
let mut fp = StringBuffer::new(257);
let mut sig = StringBuffer::new(257);
let mut desc = StringBuffer::new(257);
unsafe {
XPLMGetPluginInfo(
id,
name.as_mut_ptr(),
fp.as_mut_ptr(),
sig.as_mut_ptr(),
desc.as_mut_ptr(),
);
}
Plugin {
id,
name: name.into(),
file_path: fp.into(),
signature: sig.into(),
description: desc.into(),
_phantom: PhantomData,
}
}
pub struct PluginApi {
pub(crate) _phantom: NoSendSync,
}
impl PluginApi {
#[must_use]
pub fn from_signature(&mut self, signature: &str) -> Option<Plugin> {
let signature = CString::new(signature).ok()?;
let plugin_id = unsafe { xplane_sys::XPLMFindPluginBySignature(signature.as_ptr()) };
if plugin_id == xplane_sys::XPLM_NO_PLUGIN_ID {
None
} else {
Some(unsafe { get_plugin(plugin_id) })
}
}
#[must_use]
pub fn from_path(&mut self, path: &Path) -> Option<Plugin> {
let path_c = CString::new(path.as_os_str().as_encoded_bytes()).ok()?;
let plugin_id = unsafe { xplane_sys::XPLMFindPluginByPath(path_c.as_ptr()) };
if plugin_id == xplane_sys::XPLM_NO_PLUGIN_ID {
None
} else {
Some(unsafe { get_plugin(plugin_id) })
}
}
#[must_use]
pub fn this_plugin(&mut self) -> Plugin {
let plugin_id = unsafe { xplane_sys::XPLMGetMyID() };
assert_ne!(
plugin_id,
xplane_sys::XPLM_NO_PLUGIN_ID,
"XPLMGetMyId() returned no plugin ID. Please get in touch -- this should be impossible."
);
unsafe { get_plugin(plugin_id) }
}
#[must_use]
pub fn all_plugins(&mut self) -> Plugins {
Plugins {
next: 0,
count: unsafe { xplane_sys::XPLMCountPlugins() - 1 },
_phantom: PhantomData,
}
}
pub fn reload_all(&mut self) {
unsafe {
xplane_sys::XPLMReloadPlugins();
}
}
pub unsafe fn broadcast_plugin_message(&mut self, message_id: MessageId, param: *mut c_void) {
unsafe {
xplane_sys::XPLMSendMessageToPlugin(
xplane_sys::XPLM_NO_PLUGIN_ID,
message_id.into(),
param,
);
}
}
}