Skip to main content

lv2_state/
interface.rs

1use crate::raw::*;
2use crate::StateErr;
3use core::extension::ExtensionDescriptor;
4use core::prelude::*;
5use std::marker::PhantomData;
6use urid::*;
7
8/// A plugin extension that lets a plugins save and restore it's state.
9///
10/// This extension contains two new methods: [`save`](#tymethod.save) and [`restore`](#tymethod.restore). These are called by the host to save and restore the state of the plugin, which is done with a handle.
11///
12/// You can also add a feature collection to retrieve host features; It works just like the plugin's feature collection: You create a struct with multiple `Feature`s, derive `FeatureCollection` for it, and set the [`StateFeatures`](#associatedtype.StateFeatures) type to it. Then, the framework will try to populate it with the features supplied by the host and pass it to the method.
13pub trait State: Plugin {
14    /// The feature collection to populate for the [`save`](#tymethod.save) and [`restore`](#tymethod.restore) methods.
15    type StateFeatures: FeatureCollection<'static>;
16
17    /// Save the state of the plugin.
18    ///
19    /// The storage is done with the store handle. You draft a property, write it using the property handle, and then commit it to the store.
20    fn save(&self, store: StoreHandle, features: Self::StateFeatures) -> Result<(), StateErr>;
21
22    /// Restore the state of the plugin.
23    ///
24    /// The properties you have previously written can be retrieved with the store handle.
25    fn restore(
26        &mut self,
27        store: RetrieveHandle,
28        features: Self::StateFeatures,
29    ) -> Result<(), StateErr>;
30}
31
32/// Raw wrapper of the [`State`](trait.State.html) extension.
33///
34/// This is a marker type that has the required external methods for the extension.
35pub struct StateDescriptor<P: State> {
36    plugin: PhantomData<P>,
37}
38
39unsafe impl<P: State> UriBound for StateDescriptor<P> {
40    const URI: &'static [u8] = sys::LV2_STATE__interface;
41}
42
43impl<P: State> StateDescriptor<P> {
44    /// Handle a save request by the host.
45    ///
46    /// This involves creating the plugin reference, constructing the store handle and discovering the required host features.
47    ///
48    /// # Safety
49    ///
50    /// This method is unsafe since it is an interface for hosts written in C and since it dereferences raw pointers.
51    pub unsafe extern "C" fn extern_save(
52        instance: sys::LV2_Handle,
53        store: sys::LV2_State_Store_Function,
54        handle: sys::LV2_State_Handle,
55        flags: u32,
56        features: *const *const sys::LV2_Feature,
57    ) -> sys::LV2_State_Status {
58        let flags: u32 =
59            (sys::LV2_State_Flags::from(flags) & sys::LV2_State_Flags::LV2_STATE_IS_POD).into();
60        if flags == 0 {
61            return sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS;
62        }
63
64        let plugin: &P = if let Some(plugin) = (instance as *const P).as_ref() {
65            plugin
66        } else {
67            return sys::LV2_State_Status_LV2_STATE_ERR_UNKNOWN;
68        };
69
70        let store = StoreHandle::new(store, handle);
71
72        let mut feature_container = core::feature::FeatureCache::from_raw(features);
73        let features = if let Ok(features) =
74            P::StateFeatures::from_cache(&mut feature_container, ThreadingClass::Other)
75        {
76            features
77        } else {
78            return sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE;
79        };
80
81        StateErr::into(plugin.save(store, features))
82    }
83
84    /// Handle a restore request by the host.
85    ///
86    /// This involves creating the plugin reference, constructing the retrieve handle and discovering the required host features.
87    ///
88    /// # Safety
89    ///
90    /// This method is unsafe since it is an interface for hosts written in C and since it dereferences raw pointers.
91    pub unsafe extern "C" fn extern_restore(
92        instance: sys::LV2_Handle,
93        retrieve: sys::LV2_State_Retrieve_Function,
94        handle: sys::LV2_State_Handle,
95        flags: u32,
96        features: *const *const sys::LV2_Feature,
97    ) -> sys::LV2_State_Status {
98        let flags: u32 =
99            (sys::LV2_State_Flags::from(flags) & sys::LV2_State_Flags::LV2_STATE_IS_POD).into();
100        if flags == 0 {
101            return sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS;
102        }
103
104        let plugin: &mut P = if let Some(plugin) = (instance as *mut P).as_mut() {
105            plugin
106        } else {
107            return sys::LV2_State_Status_LV2_STATE_ERR_UNKNOWN;
108        };
109
110        let store = RetrieveHandle::new(retrieve, handle);
111
112        let mut feature_container = core::feature::FeatureCache::from_raw(features);
113        let features = if let Ok(features) =
114            P::StateFeatures::from_cache(&mut feature_container, ThreadingClass::Other)
115        {
116            features
117        } else {
118            return sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE;
119        };
120
121        StateErr::into(plugin.restore(store, features))
122    }
123}
124
125impl<P: State> ExtensionDescriptor for StateDescriptor<P> {
126    type ExtensionInterface = sys::LV2_State_Interface;
127
128    const INTERFACE: &'static sys::LV2_State_Interface = &sys::LV2_State_Interface {
129        save: Some(Self::extern_save),
130        restore: Some(Self::extern_restore),
131    };
132}
133
134#[cfg(test)]
135mod tests {
136    use crate::*;
137    use lv2_core::prelude::*;
138    use lv2_urid::*;
139    use urid::*;
140
141    #[uri("urn:stateful")]
142    struct Stateful;
143
144    impl Plugin for Stateful {
145        type InitFeatures = ();
146        type AudioFeatures = ();
147        type Ports = ();
148
149        #[cfg_attr(tarpaulin, skip)]
150        fn new(_: &PluginInfo, _: &mut ()) -> Option<Self> {
151            Some(Self)
152        }
153
154        #[cfg_attr(tarpaulin, skip)]
155        fn run(&mut self, _: &mut (), _: &mut (), _: u32) {}
156    }
157
158    #[derive(FeatureCollection)]
159    struct Features<'a> {
160        _map: LV2Map<'a>,
161    }
162
163    impl State for Stateful {
164        type StateFeatures = Features<'static>;
165
166        #[cfg_attr(tarpaulin, skip)]
167        fn save(&self, _: StoreHandle, _: Features<'static>) -> Result<(), StateErr> {
168            Ok(())
169        }
170
171        #[cfg_attr(tarpaulin, skip)]
172        fn restore(&mut self, _: RetrieveHandle, _: Features<'static>) -> Result<(), StateErr> {
173            Ok(())
174        }
175    }
176
177    #[test]
178    fn test_illegal_paths() {
179        type Descriptor = StateDescriptor<Stateful>;
180        let mut plugin = Stateful;
181
182        assert_eq!(sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS, unsafe {
183            Descriptor::extern_save(
184                std::ptr::null_mut(),
185                None,
186                std::ptr::null_mut(),
187                0,
188                std::ptr::null_mut(),
189            )
190        });
191
192        assert_eq!(sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS, unsafe {
193            Descriptor::extern_restore(
194                std::ptr::null_mut(),
195                None,
196                std::ptr::null_mut(),
197                0,
198                std::ptr::null_mut(),
199            )
200        });
201
202        assert_eq!(sys::LV2_State_Status_LV2_STATE_ERR_UNKNOWN, unsafe {
203            Descriptor::extern_save(
204                std::ptr::null_mut(),
205                None,
206                std::ptr::null_mut(),
207                sys::LV2_State_Flags::LV2_STATE_IS_POD.into(),
208                std::ptr::null_mut(),
209            )
210        });
211
212        assert_eq!(sys::LV2_State_Status_LV2_STATE_ERR_UNKNOWN, unsafe {
213            Descriptor::extern_restore(
214                std::ptr::null_mut(),
215                None,
216                std::ptr::null_mut(),
217                sys::LV2_State_Flags::LV2_STATE_IS_POD.into(),
218                std::ptr::null_mut(),
219            )
220        });
221
222        assert_eq!(sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE, unsafe {
223            Descriptor::extern_save(
224                &mut plugin as *mut Stateful as sys::LV2_Handle,
225                None,
226                std::ptr::null_mut(),
227                sys::LV2_State_Flags::LV2_STATE_IS_POD.into(),
228                std::ptr::null_mut(),
229            )
230        });
231
232        assert_eq!(sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE, unsafe {
233            Descriptor::extern_restore(
234                &mut plugin as *mut Stateful as sys::LV2_Handle,
235                None,
236                std::ptr::null_mut(),
237                sys::LV2_State_Flags::LV2_STATE_IS_POD.into(),
238                std::ptr::null_mut(),
239            )
240        });
241    }
242}