ffgl_core/
entry.rs

1//! Primary entry point of the FFGL plugin. This is the function that is called by the host.
2//! You can use [crate::plugin_main] to automate calling this entry function from the FFGL ABI
3//!
4use crate::info;
5use crate::FFGLData;
6
7use crate::ffi::ffgl2::*;
8
9use crate::handler::{FFGLHandler, FFGLInstance};
10use crate::log::try_init_default_subscriber;
11use crate::parameters::ParamInfo;
12
13use core::slice;
14
15use std::{any::Any, ffi::CString};
16
17use crate::conversions::*;
18
19use crate::handler;
20use anyhow::{Context, Error};
21
22fn param<H: FFGLHandler>(handler: &'static H, index: FFGLVal) -> &'static dyn ParamInfo {
23    handler.param_info(unsafe { index.num as usize })
24}
25
26static mut INITIALIZED: bool = false;
27static mut INFO: Option<info::PluginInfo> = None;
28static mut INFO_STRUCT: Option<PluginInfoStruct> = None;
29static mut ABOUT: Option<CString> = None;
30static mut DESCRIPTION: Option<CString> = None;
31static mut INFO_STRUCT_EXTENDED: Option<PluginExtendedInfoStruct> = None;
32static mut HANDLER: Option<Box<dyn Any>> = None;
33
34use tracing::{debug, info, trace};
35
36///backtrace didn't seem to work. Maybe a problem with FFI. This is a hacky way to get the source
37macro_rules! e {
38    ($($arg:tt)*) => {{
39        format!("{orig}\nSOURCE {file}:{line}:{column}", orig=format!($($arg)*),
40        file = file!(),
41        line = line!(),
42        column = column!(),)
43    }}
44}
45
46pub fn default_ffgl_entry<H: FFGLHandler + 'static>(
47    function: Op,
48    mut input_value: FFGLVal,
49    instance: Option<&mut handler::Instance<H::Instance>>,
50) -> Result<FFGLVal, Error> {
51    let noisy_op = match function {
52        Op::ProcessOpenGL
53        | Op::SetBeatInfo
54        | Op::SetTime
55        | Op::GetParameterEvents
56        | Op::SetParameter
57        | Op::GetParameterDisplay
58        | Op::GetParameterType => true,
59        _ => false,
60    };
61
62    if !noisy_op {
63        debug!("Op::{function:?}({})", unsafe { input_value.num });
64    } else {
65        trace!("Op::{function:?}({})", unsafe { input_value.num });
66    }
67
68    unsafe {
69        if !INITIALIZED {
70            INITIALIZED = true;
71
72            HANDLER = Some(Box::new(H::init()));
73
74            let _ = try_init_default_subscriber();
75
76            let handler = &*HANDLER
77                .as_ref()
78                .context(e!("No handler"))?
79                .downcast_ref::<H>()
80                .context(e!("Handler incorrect type"))?;
81
82            INFO = Some(handler.plugin_info());
83            let info = INFO.as_ref().context(e!("No info"))?;
84            ABOUT = Some(CString::new(info.about.clone())?);
85            DESCRIPTION = Some(CString::new(info.description.clone())?);
86
87            INFO_STRUCT = Some(info::plugin_info(
88                std::mem::transmute(&info.unique_id),
89                std::mem::transmute(&info.name),
90                info.ty,
91            ));
92
93            INFO_STRUCT_EXTENDED = Some(info::plugin_info_extended(
94                ABOUT.as_ref().context(e!("ABOUT not initialized"))?,
95                DESCRIPTION
96                    .as_ref()
97                    .context(e!("DESCRIPTION not initialized"))?,
98            ));
99
100            info!(
101                "INITIALIZED PLUGIN '{id:?}' '{name}'",
102                name = std::str::from_utf8(&info.name)?,
103                id = info.unique_id
104            );
105        }
106    }
107
108    let info = unsafe { INFO.as_ref().context(e!("No info"))? };
109
110    let handler = unsafe { &HANDLER }
111        .as_ref()
112        .context(e!("Handler not initialized"))?
113        .downcast_ref::<H>()
114        .context(e!("Handler type mismatch"))?;
115
116    let resp = match function {
117        Op::GetPluginCaps => {
118            let cap_num = unsafe { input_value.num };
119            let cap = num::FromPrimitive::from_u32(cap_num).expect("Unexpected cap n{cap_num}");
120
121            let result = match cap {
122                PluginCapacity::MinInputFrames => FFGLVal { num: 0 },
123                PluginCapacity::MaxInputFrames => FFGLVal { num: 1 },
124
125                PluginCapacity::ProcessOpenGl => SupportVal::Supported.into(),
126                PluginCapacity::SetTime => SupportVal::Supported.into(),
127
128                PluginCapacity::TopLeftTextureOrientation => SupportVal::Supported.into(),
129
130                _ => SupportVal::Unsupported.into(),
131            };
132
133            debug!("{cap:?} => {}", unsafe { result.num });
134
135            result
136        }
137
138        Op::EnablePluginCap => {
139            let cap_num = unsafe { input_value.num };
140            let cap = num::FromPrimitive::from_u32(cap_num).expect("Unexpected cap n{cap_num}");
141
142            let result: FFGLVal = match cap {
143                PluginCapacity::TopLeftTextureOrientation => SuccessVal::Success.into(),
144                _ => SuccessVal::Fail.into(),
145            };
146
147            debug!("{cap:?} => {}", unsafe { result.num });
148
149            result
150        }
151
152        Op::GetNumParameters => FFGLVal {
153            num: handler.num_params() as u32,
154        },
155
156        Op::GetParameterDefault => param(handler, input_value).default_val().into(),
157        Op::GetParameterGroup => {
158            let input: &GetStringStructTag = unsafe { input_value.as_ref() };
159            let buffer = input.stringBuffer;
160
161            let group = H::param_info(handler, input.parameterNumber as usize).group();
162
163            let string_target: &mut [char] = unsafe {
164                slice::from_raw_parts_mut(buffer.address as *mut char, buffer.maxToWrite as usize)
165            };
166
167            let copied_chars = std::cmp::min(group.len(), buffer.maxToWrite as usize);
168
169            string_target[..copied_chars]
170                .copy_from_slice(&group[..copied_chars].chars().collect::<Vec<_>>());
171
172            debug!("GET PARAM GROUP {group:?}");
173
174            SuccessVal::Success.into()
175        }
176        Op::GetParameterDisplay => param(handler, input_value).display_name().into(),
177        Op::GetParameterName => param(handler, input_value).name().into(),
178        Op::GetParameterType => param(handler, input_value).param_type().into(),
179
180        Op::GetParameter => instance
181            .context(e!("No instance"))?
182            .renderer
183            .get_param(unsafe { input_value.num } as usize)
184            .into(),
185
186        Op::SetParameter => {
187            let input: &SetParameterStruct = unsafe { input_value.as_ref() };
188            let index = input.ParameterNumber;
189
190            // let param = param_mut(instance, index as usize);
191            let index_usize = index as usize;
192
193            //dunno why they store this in a u32, whatever..
194            let new_value =
195                unsafe { std::mem::transmute::<u32, f32>(input.NewParameterValue.UIntValue) };
196
197            instance
198                .context(e!("No instance"))?
199                .renderer
200                .set_param(index_usize, new_value);
201
202            SuccessVal::Success.into()
203        }
204        Op::GetParameterRange => {
205            let input: &mut GetRangeStruct = unsafe { (input_value).as_mut() };
206
207            let index = input.parameterNumber;
208            let param = handler.param_info(index as usize);
209
210            input.range = RangeStruct {
211                min: param.min(),
212                max: param.max(),
213            };
214
215            SuccessVal::Success.into()
216        }
217        // Op::GetParameterGroup => param(instance, ffgl2::GetParameterGroupStruct).group.into(),
218        Op::GetInfo => unsafe { INFO_STRUCT.as_ref().context(e!("No info"))?.into() },
219
220        Op::GetExtendedInfo => unsafe {
221            INFO_STRUCT_EXTENDED
222                .as_ref()
223                .context(e!("No extended info"))?
224                .into()
225        },
226
227        Op::InstantiateGL => {
228            let viewport: &FFGLViewportStruct = unsafe { input_value.as_ref() };
229
230            let data = FFGLData::new(viewport);
231            let renderer = H::new_instance(handler, &data)
232                .context("Failed to instantiate renderer")
233                .context(format!("For {}", std::str::from_utf8(&info.name).unwrap()))?;
234
235            let instance = handler::Instance { data, renderer };
236
237            info!(
238                "Created INSTANCE \n{instance:#?} of ({id:?}, '{name:?}')",
239                id = info.unique_id,
240                name = String::from_utf8_lossy(&info.name),
241            );
242
243            FFGLVal::from_static(Box::leak(Box::<handler::Instance<H::Instance>>::new(
244                instance,
245            )))
246        }
247
248        Op::DeinstantiateGL => {
249            let inst = instance.context(e!("No instance"))?;
250
251            debug!("DEINSTGL\n{inst:#?}");
252            unsafe {
253                drop(Box::from_raw(inst as *mut handler::Instance<H::Instance>));
254            }
255
256            SuccessVal::Success.into()
257        }
258
259        Op::ProcessOpenGL => {
260            let gl_process_info: &ProcessOpenGLStruct = unsafe { input_value.as_ref() };
261
262            let handler::Instance { data, renderer } = instance.context(e!("No instance"))?;
263            let gl_input = gl_process_info.into();
264
265            renderer.draw(&data, gl_input);
266
267            SuccessVal::Success.into()
268        }
269
270        Op::SetTime => {
271            let seconds: f64 = *unsafe { input_value.as_ref() };
272            instance.context(e!("No instance"))?.data.set_time(seconds);
273            SuccessVal::Success.into()
274        }
275
276        //This is can be called before GLInitialize.
277        Op::SetBeatInfo => {
278            let beat_info: &SetBeatinfoStruct = unsafe { input_value.as_ref() };
279            if let Some(inst) = instance {
280                inst.data.set_beat(*beat_info);
281                SuccessVal::Success.into()
282            } else {
283                SuccessVal::Fail.into()
284            }
285        }
286
287        Op::Resize => {
288            let viewport: &FFGLViewportStruct = unsafe { input_value.as_ref() };
289            debug!("RESIZE\n{viewport:#?}");
290            SuccessVal::Success.into()
291        }
292
293        Op::Connect => SuccessVal::Success.into(),
294
295        Op::Instantiate | Op::Deinstantiate | Op::ProcessFrame | Op::ProcessFrameCopy => {
296            SuccessVal::Fail.into()
297        }
298
299        Op::InitialiseV2 => SuccessVal::Success.into(),
300        Op::Initialise => SuccessVal::Success.into(),
301        Op::Deinitialise => SuccessVal::Success.into(),
302
303        _ => SuccessVal::Fail.into(),
304    };
305
306    if !noisy_op {
307        debug!("=> {}", unsafe { resp.num });
308    } else {
309        trace!("=> {}", unsafe { resp.num });
310    }
311
312    Ok(resp)
313}