extern crate self as nsi;
use crate::{argument::*, *};
use null_terminated_str::NullTerminatedStr;
use rclite::Arc;
#[allow(unused_imports)]
use std::{
ffi::{c_char, CStr, CString},
marker::PhantomData,
ops::Drop,
os::raw::{c_int, c_void},
};
use ustr::Ustr;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct InnerContext<'a> {
context: NSIContext,
_marker: PhantomData<*mut &'a ()>,
}
unsafe impl<'a> Send for InnerContext<'a> {}
unsafe impl<'a> Sync for InnerContext<'a> {}
impl<'a> Drop for InnerContext<'a> {
#[inline]
fn drop(&mut self) {
NSI_API.NSIEnd(self.context);
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Context<'a>(Arc<InnerContext<'a>>);
unsafe impl<'a> Send for Context<'a> {}
unsafe impl<'a> Sync for Context<'a> {}
impl<'a> From<Context<'a>> for NSIContext {
#[inline]
fn from(context: Context<'a>) -> Self {
context.0.context
}
}
impl<'a> From<NSIContext> for Context<'a> {
#[inline]
fn from(context: NSIContext) -> Self {
Self(Arc::new(InnerContext {
context,
_marker: PhantomData,
}))
}
}
impl<'a> Context<'a> {
#[inline]
pub fn new(args: Option<&ArgSlice<'_, 'a>>) -> Option<Self> {
let (_, _, mut args_out) = get_c_param_vec(args);
let fn_pointer: nsi_sys::NSIErrorHandler = Some(
error_handler
as extern "C" fn(*mut c_void, c_int, c_int, *const c_char),
);
if let Some(args) = args {
if let Some(arg) = args
.iter()
.find(|arg| Ustr::from("errorhandler") == arg.name)
{
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("errorhandler").as_char_ptr(),
data: &fn_pointer as *const _ as _,
type_: NSIType::Pointer as _,
arraylength: 0,
count: 1,
flags: 0,
});
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("errorhandlerdata").as_char_ptr(),
data: &arg.data.as_c_ptr() as *const _ as _,
type_: NSIType::Pointer as _,
arraylength: 1,
count: 1,
flags: 0,
});
}
}
let context = NSI_API.NSIBegin(args_out.len() as _, args_out.as_ptr());
if 0 == context {
None
} else {
Some(Self(Arc::new(InnerContext {
context,
_marker: PhantomData,
})))
}
}
#[inline]
pub fn create(
&self,
handle: &str,
node_type: &str,
args: Option<&ArgSlice<'_, 'a>>,
) {
let handle = HandleString::from(handle);
let node_type = Ustr::from(node_type);
let (args_len, args_ptr, _args_out) = get_c_param_vec(args);
NSI_API.NSICreate(
self.0.context,
handle.as_char_ptr(),
node_type.as_char_ptr(),
args_len,
args_ptr,
);
}
#[inline]
pub fn delete(&self, handle: &str, args: Option<&ArgSlice<'_, 'a>>) {
let handle = HandleString::from(handle);
let (args_len, args_ptr, _args_out) = get_c_param_vec(args);
NSI_API.NSIDelete(
self.0.context,
handle.as_char_ptr(),
args_len,
args_ptr,
);
}
#[inline]
pub fn set_attribute(&self, handle: &str, args: &ArgSlice<'_, 'a>) {
let handle = HandleString::from(handle);
let (args_len, args_ptr, _args_out) = get_c_param_vec(Some(args));
NSI_API.NSISetAttribute(
self.0.context,
handle.as_char_ptr(),
args_len,
args_ptr,
);
}
#[inline]
pub fn set_attribute_at_time(
&self,
handle: &str,
time: f64,
args: &ArgSlice<'_, 'a>,
) {
let handle = HandleString::from(handle);
let (args_len, args_ptr, _args_out) = get_c_param_vec(Some(args));
NSI_API.NSISetAttributeAtTime(
self.0.context,
handle.as_char_ptr(),
time,
args_len,
args_ptr,
);
}
#[inline]
pub fn delete_attribute(&self, handle: &str, name: &str) {
let handle = HandleString::from(handle);
let name = Ustr::from(name);
NSI_API.NSIDeleteAttribute(
self.0.context,
handle.as_char_ptr(),
name.as_char_ptr(),
);
}
#[inline]
pub fn connect(
&self,
from: &str,
from_attr: Option<&str>,
to: &str,
to_attr: &str,
args: Option<&ArgSlice<'_, 'a>>,
) {
let from = HandleString::from(from);
let from_attr = Ustr::from(from_attr.unwrap_or(""));
let to = HandleString::from(to);
let to_attr = Ustr::from(to_attr);
let (args_len, args_ptr, _args_out) = get_c_param_vec(args);
NSI_API.NSIConnect(
self.0.context,
from.as_char_ptr(),
from_attr.as_char_ptr(),
to.as_char_ptr(),
to_attr.as_char_ptr(),
args_len,
args_ptr,
);
}
#[inline]
pub fn disconnect(
&self,
from: &str,
from_attr: Option<&str>,
to: &str,
to_attr: &str,
) {
let from = HandleString::from(from);
let from_attr = Ustr::from(from_attr.unwrap_or(""));
let to = HandleString::from(to);
let to_attr = Ustr::from(to_attr);
NSI_API.NSIDisconnect(
self.0.context,
from.as_char_ptr(),
from_attr.as_char_ptr(),
to.as_char_ptr(),
to_attr.as_char_ptr(),
);
}
#[inline]
pub fn evaluate(&self, args: &ArgSlice<'_, 'a>) {
let (args_len, args_ptr, _args_out) = get_c_param_vec(Some(args));
NSI_API.NSIEvaluate(self.0.context, args_len, args_ptr);
}
#[inline]
pub fn render_control(
&self,
action: Action,
args: Option<&ArgSlice<'_, 'a>>,
) {
let (_, _, mut args_out) = get_c_param_vec(args);
let fn_pointer: nsi_sys::NSIRenderStopped =
Some(render_status as extern "C" fn(*mut c_void, c_int, c_int));
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("action").as_char_ptr(),
data: &Ustr::from(match action {
Action::Start => "start",
Action::Wait => "wait",
Action::Synchronize => "synchronize",
Action::Suspend => "suspend",
Action::Resume => "resume",
Action::Stop => "stop",
})
.as_char_ptr() as *const _ as _,
type_: NSIType::String as _,
arraylength: 0,
count: 1,
flags: 0,
});
if let Some(args) = args {
if let Some(arg) =
args.iter().find(|arg| Ustr::from("callback") == arg.name)
{
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("stoppedcallback").as_char_ptr(),
data: &fn_pointer as *const _ as _,
type_: NSIType::Pointer as _,
arraylength: 0,
count: 1,
flags: 0,
});
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("stoppedcallbackdata").as_char_ptr(),
data: &arg.data.as_c_ptr() as *const _ as _,
type_: NSIType::Pointer as _,
arraylength: 1,
count: 1,
flags: 0,
});
}
}
NSI_API.NSIRenderControl(
self.0.context,
args_out.len() as _,
args_out.as_ptr(),
);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Action {
Start,
Wait,
Synchronize,
Suspend,
Resume,
Stop,
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, num_enum::FromPrimitive)]
pub enum RenderStatus {
#[num_enum(default)]
Completed = nsi_sys::NSIStoppingStatus::RenderCompleted as _,
Aborted = nsi_sys::NSIStoppingStatus::RenderAborted as _,
Synchronized = nsi_sys::NSIStoppingStatus::RenderSynchronized as _,
Restarted = nsi_sys::NSIStoppingStatus::RenderRestarted as _,
}
pub trait FnStatus<'a>: Fn(
&Context,
RenderStatus,
)
+ 'a {}
#[doc(hidden)]
impl<
'a,
T: Fn(&Context, RenderStatus)
+ 'a
+ for<'r, 's> Fn(&'r context::Context<'s>, RenderStatus),
> FnStatus<'a> for T
{
}
pub struct StatusCallback<'a>(Box<Box<dyn FnStatus<'a>>>);
unsafe impl Send for StatusCallback<'static> {}
unsafe impl Sync for StatusCallback<'static> {}
impl<'a> StatusCallback<'a> {
pub fn new<F>(fn_status: F) -> Self
where
F: FnStatus<'a>,
{
StatusCallback(Box::new(Box::new(fn_status)))
}
}
impl CallbackPtr for StatusCallback<'_> {
#[doc(hidden)]
fn to_ptr(self) -> *const core::ffi::c_void {
Box::into_raw(self.0) as *const _ as _
}
}
#[no_mangle]
pub(crate) extern "C" fn render_status(
payload: *mut c_void,
context: nsi_sys::NSIContext,
status: c_int,
) {
if !payload.is_null() {
let fn_status =
unsafe { Box::from_raw(payload as *mut Box<dyn FnStatus>) };
let ctx = Context(Arc::new(InnerContext {
context,
_marker: PhantomData,
}));
fn_status(&ctx, status.into());
std::mem::forget(ctx);
}
}
pub trait FnError<'a>: Fn(
log::Level,
i32,
&str,
)
+ 'a {}
#[doc(hidden)]
impl<
'a,
T: Fn(log::Level, i32, &str) + 'a + for<'r> Fn(log::Level, i32, &'r str),
> FnError<'a> for T
{
}
pub struct ErrorCallback<'a>(Box<Box<dyn FnError<'a>>>);
unsafe impl Send for ErrorCallback<'static> {}
unsafe impl Sync for ErrorCallback<'static> {}
impl<'a> ErrorCallback<'a> {
pub fn new<F>(fn_error: F) -> Self
where
F: FnError<'a>,
{
ErrorCallback(Box::new(Box::new(fn_error)))
}
}
impl CallbackPtr for ErrorCallback<'_> {
#[doc(hidden)]
fn to_ptr(self) -> *const core::ffi::c_void {
Box::into_raw(self.0) as *const _ as _
}
}
#[no_mangle]
pub(crate) extern "C" fn error_handler(
payload: *mut c_void,
level: c_int,
code: c_int,
message: *const c_char,
) {
if !payload.is_null() {
let fn_error =
unsafe { Box::from_raw(payload as *mut Box<dyn FnError>) };
let message = unsafe {
NullTerminatedStr::from_cstr_unchecked(CStr::from_ptr(message as _))
};
let level = match NSIErrorLevel::from(level) {
NSIErrorLevel::Message => log::Level::Trace,
NSIErrorLevel::Info => log::Level::Info,
NSIErrorLevel::Warning => log::Level::Warn,
NSIErrorLevel::Error => log::Level::Error,
};
fn_error(level, code as _, message.as_ref());
}
}