Skip to main content

fpsdk/plugin/
message.rs

1//! Plugin messages.
2use std::mem;
3use std::os::raw::{c_int, c_void};
4
5use crate::host::{GetName, Host};
6use crate::plugin;
7use crate::{
8    intptr_t, AsRawPtr, FlMessage, MessageBoxFlags, MessageBoxResult, NameColor, Note, Notes,
9    ParamMenuEntry, SongTime, TNameColor, TParamMenuEntry, Tag, Time, TimeFormat, ValuePtr,
10};
11
12/// Messsage which you can send to the host using
13/// [`Host::on_message`](../../host/struct.Host.html#method.on_message).
14pub trait Message {
15    /// The result returned after sending the message.
16    type Return;
17
18    /// Send the message.
19    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return;
20}
21
22macro_rules! impl_message {
23    ($message: ident) => {
24        impl Message for $message {
25            type Return = ();
26
27            fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
28                unsafe {
29                    host_on_message(*host.host_ptr.get_mut(), tag.0, self.into());
30                }
31            }
32        }
33    };
34}
35
36macro_rules! impl_message_ty {
37    ($message: ident, $res: ty) => {
38        impl Message for $message {
39            type Return = $res;
40
41            fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
42                ValuePtr(unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, self.into()) })
43                    .get::<$res>()
44            }
45        }
46    };
47}
48
49extern "C" {
50    fn host_on_message(host: *mut c_void, plugin_tag: Tag, message: FlMessage) -> intptr_t;
51}
52
53/// Tells the host that the user has clicked an item of the control popup menu.
54///
55/// The first value holds the parameter index.
56///
57/// The second value holds the popup item index.
58#[derive(Debug)]
59pub struct ParamMenu(pub usize, pub usize);
60
61impl_message!(ParamMenu);
62
63impl From<ParamMenu> for FlMessage {
64    fn from(message: ParamMenu) -> Self {
65        FlMessage {
66            id: 0,
67            index: message.0.as_raw_ptr(),
68            value: message.1.as_raw_ptr(),
69        }
70    }
71}
72
73/// Notify the host that the editor has been resized.
74#[derive(Debug)]
75pub struct EditorResized;
76
77impl_message!(EditorResized);
78
79impl From<EditorResized> for FlMessage {
80    fn from(_message: EditorResized) -> Self {
81        FlMessage {
82            id: 2,
83            index: 0,
84            value: 0,
85        }
86    }
87}
88
89/// Notify the host that names ([`Plugin::name_of`](../trait.Plugin.html#tymethod.name_of)) have
90/// changed, with the type of names in value (see [`GetName`](../../host/enum.GetName.html)).
91#[derive(Debug)]
92pub struct NamesChanged(pub GetName);
93
94impl_message!(NamesChanged);
95
96impl From<NamesChanged> for FlMessage {
97    fn from(message: NamesChanged) -> Self {
98        FlMessage {
99            id: 3,
100            index: 0,
101            value: Option::<FlMessage>::from(message.0)
102                .map(|msg| msg.id)
103                .unwrap_or_default(),
104        }
105    }
106}
107
108/// This makes the host enable its MIDI output. This is useful when a MIDI out plugin is
109/// created (a plugin which will send midi messages to external midi hardware, most likely).
110#[derive(Debug)]
111pub struct ActivateMidi;
112
113impl_message!(ActivateMidi);
114
115impl From<ActivateMidi> for FlMessage {
116    fn from(_: ActivateMidi) -> Self {
117        FlMessage {
118            id: 4,
119            index: 0,
120            value: 0,
121        }
122    }
123}
124
125/// The plugin either wants to be notified about MIDI messages (for processing or filtering), or
126/// wants to stop being notified about them.
127///
128/// Value tells the host whether the plugin want to be notified or not (`true` to be added to the
129/// list of plugins that are notified, `false` to be removed from the list).
130#[derive(Debug)]
131pub struct WantMidiInput(pub bool);
132
133impl_message!(WantMidiInput);
134
135impl From<WantMidiInput> for FlMessage {
136    fn from(message: WantMidiInput) -> Self {
137        FlMessage {
138            id: 5,
139            index: 0,
140            value: message.0.as_raw_ptr(),
141        }
142    }
143}
144
145/// Ask the host to kill the automation linked to the plugin. This can for example be used for a
146/// demo version of the plugin. The host will kill all automation information in the range <first
147/// value>..<second value>. So to kill the automation for all parameters, you'd call the method
148/// with <first value> = 0 and <second value> = <num of params> - 1.
149///
150/// The first value is the first parameter index for which to kill the automation.
151///
152/// The second value is the last parameter index for which to kill the automation (inclusive).
153#[derive(Debug)]
154pub struct KillAutomation(pub usize, pub usize);
155
156impl_message!(KillAutomation);
157
158impl From<KillAutomation> for FlMessage {
159    fn from(message: KillAutomation) -> Self {
160        FlMessage {
161            id: 8,
162            index: message.0.as_raw_ptr(),
163            value: message.1.as_raw_ptr(),
164        }
165    }
166}
167
168/// This tells the host how many presets are supported by the plugin (this is mainly used by the
169/// wrapper plugin).
170///
171/// The value holds the number of presets.
172#[derive(Debug)]
173pub struct SetNumPresets(pub usize);
174
175impl_message!(SetNumPresets);
176
177impl From<SetNumPresets> for FlMessage {
178    fn from(message: SetNumPresets) -> Self {
179        FlMessage {
180            id: 9,
181            index: 0,
182            value: message.0.as_raw_ptr(),
183        }
184    }
185}
186
187/// Sets a new short name for the parent.
188///
189/// The value is the new name.
190#[derive(Debug)]
191pub struct SetNewName(pub String);
192
193impl_message!(SetNewName);
194
195impl From<SetNewName> for FlMessage {
196    fn from(message: SetNewName) -> Self {
197        FlMessage {
198            id: 10,
199            index: 0,
200            value: message.0.as_raw_ptr(),
201        }
202    }
203}
204
205/// Used by the VSTi wrapper, because the dumb VSTGUI needs idling for his knobs.
206#[derive(Debug)]
207pub struct VstiIdle;
208
209impl_message!(VstiIdle);
210
211impl From<VstiIdle> for FlMessage {
212    fn from(_message: VstiIdle) -> Self {
213        FlMessage {
214            id: 11,
215            index: 0,
216            value: 0,
217        }
218    }
219}
220
221// Ask the host to open a selector for its channel sample (Also see FPF_UseChanSample).
222//
223// HYBRID GENERATORS ARE DEPRECATED.
224// pub struct SelectChanSample;
225
226/// Tell the host that the plugin wants to receive the idle message (or not). Idle messages are
227/// received by default.
228#[derive(Debug)]
229pub enum WantIdle {
230    /// Disabled.
231    Disabled,
232    /// Enabled when UI is visible (default).
233    EnabledVisible,
234    /// Always enabled.
235    EnabledAlways,
236}
237
238impl_message!(WantIdle);
239
240impl From<WantIdle> for FlMessage {
241    fn from(message: WantIdle) -> Self {
242        FlMessage {
243            id: 13,
244            index: 0,
245            value: message.into(),
246        }
247    }
248}
249
250impl From<WantIdle> for intptr_t {
251    fn from(message: WantIdle) -> Self {
252        match message {
253            WantIdle::Disabled => 0,
254            WantIdle::EnabledVisible => 1,
255            WantIdle::EnabledAlways => 2,
256        }
257    }
258}
259
260/// Ask the host to search for a file in its search paths.
261///
262/// Value should hold the simple filename.
263///
264/// The full path is returned as result of the function (`String`).
265#[derive(Debug)]
266pub struct LocateDataFile(pub String);
267
268impl_message_ty!(LocateDataFile, String);
269
270impl From<LocateDataFile> for FlMessage {
271    fn from(message: LocateDataFile) -> Self {
272        FlMessage {
273            id: 14,
274            index: 0,
275            value: message.0.as_raw_ptr(),
276        }
277    }
278}
279
280/// Translate tick time into Bar:Step:Tick (warning: it's *not* Bar:Beat:Tick).
281///
282/// The value should hold the tick time to translate.
283///
284/// The result is [`SongTime`](../struct.SongTime.html).
285#[derive(Debug)]
286pub struct TicksToTime(pub u32);
287
288impl Message for TicksToTime {
289    type Return = SongTime;
290
291    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
292        let message = FlMessage::from(self);
293        let time_ptr = message.index;
294        unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
295        ValuePtr(time_ptr).get::<Self::Return>()
296    }
297}
298
299impl From<TicksToTime> for FlMessage {
300    fn from(message: TicksToTime) -> Self {
301        let time = SongTime::default();
302        FlMessage {
303            id: 16,
304            index: (Box::into_raw(Box::new(time)) as *mut c_void).as_raw_ptr(),
305            value: message.0.as_raw_ptr(),
306        }
307    }
308}
309
310/// Ask the host to add one or more notes to the piano roll.
311#[derive(Debug)]
312pub struct AddToPianoRoll(pub Notes);
313
314impl_message!(AddToPianoRoll);
315
316impl From<AddToPianoRoll> for FlMessage {
317    fn from(mut message: AddToPianoRoll) -> Self {
318        message.0.notes.shrink_to_fit();
319        let notes_ptr = message.0.notes.as_mut_ptr();
320        let len = message.0.notes.len();
321
322        let p_notes_params = unsafe {
323            init_p_notes_params(
324                1,
325                message.0.flags.bits() as c_int,
326                message.0.channel.map(|v| v as c_int).unwrap_or(-1),
327                message.0.pattern.map(|v| v as c_int).unwrap_or(-1),
328                notes_ptr,
329                len as c_int,
330            )
331        };
332
333        FlMessage {
334            id: 17,
335            index: 0,
336            value: p_notes_params,
337        }
338    }
339}
340
341extern "C" {
342    // target:
343    // 0=step seq (not supported yet), 1=piano roll
344    //
345    // so we always use 1 for this
346    fn init_p_notes_params(
347        target: c_int,
348        flags: c_int,
349        ch_num: c_int,
350        pat_num: c_int,
351        notes: *mut Note,
352        count: c_int,
353    ) -> intptr_t;
354}
355
356/// Before the popup menu is shown, you must fill it with the entries set by the host. You use this
357/// message to find out which those are.
358///
359/// First value is the parameter index.
360///
361/// Second value holds the popup item index.
362///
363/// The result is [`Option<ParamMenuEntry>`](../struct.ParamMenuEntry.html).
364#[derive(Debug)]
365pub struct GetParamMenuEntry(pub usize, pub usize);
366
367impl Message for GetParamMenuEntry {
368    type Return = Option<ParamMenuEntry>;
369
370    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
371        let message = FlMessage::from(self);
372        let result = unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
373
374        if (result as *mut c_void).is_null() {
375            return None;
376        }
377
378        Some(ParamMenuEntry::from_ffi(
379            result as *mut c_void as *mut TParamMenuEntry,
380        ))
381    }
382}
383
384impl From<GetParamMenuEntry> for FlMessage {
385    fn from(message: GetParamMenuEntry) -> Self {
386        FlMessage {
387            id: 18,
388            index: message.0.as_raw_ptr(),
389            value: message.1.as_raw_ptr(),
390        }
391    }
392}
393
394/// This will make FL to show a message box.
395///
396/// The first value is the message box title.
397///
398/// The second value is the message.
399///
400/// The third value is flags (see [`MessageBoxFlags`](../../struct.MessageBoxFlags.html)).
401///
402/// The result is [`MessageBoxResult`](../../enum.MessageBoxResult.html).
403#[derive(Debug)]
404pub struct MessageBox(pub String, pub String, pub MessageBoxFlags);
405
406impl_message_ty!(MessageBox, MessageBoxResult);
407
408impl From<MessageBox> for FlMessage {
409    fn from(message: MessageBox) -> Self {
410        FlMessage {
411            id: 19,
412            index: format!("{}|{}", message.0, message.1).as_raw_ptr(),
413            value: message.2.as_raw_ptr(),
414        }
415    }
416}
417
418/// Turn on a preview note.
419///
420/// The first value is the note number.
421///
422/// The second value is the color (or MIDI channel).
423///
424/// The third value is the velocity.
425#[derive(Debug)]
426pub struct NoteOn(pub u8, pub u8, pub u8);
427
428impl_message!(NoteOn);
429
430impl From<NoteOn> for FlMessage {
431    fn from(message: NoteOn) -> Self {
432        FlMessage {
433            id: 20,
434            index: dword_from_note_and_ch(message.0, message.1).as_raw_ptr(),
435            value: message.2.as_raw_ptr(),
436        }
437    }
438}
439
440/// Turn a preview note off.
441///
442/// The value is note number.
443#[derive(Debug)]
444pub struct NoteOff(pub u8);
445
446impl_message!(NoteOff);
447
448impl From<NoteOff> for FlMessage {
449    fn from(message: NoteOff) -> Self {
450        FlMessage {
451            id: 21,
452            index: message.0.as_raw_ptr(),
453            value: 0,
454        }
455    }
456}
457
458/// This shows a hint message in the FL hint area. It's the same as OnHint, but shows it
459/// immediately (to show a progress while you're doing something).
460///
461/// The value is the message.
462#[derive(Debug)]
463pub struct OnHintDirect(pub String);
464
465impl_message!(OnHintDirect);
466
467impl From<OnHintDirect> for FlMessage {
468    fn from(message: OnHintDirect) -> Self {
469        FlMessage {
470            id: 22,
471            index: 0,
472            value: message.0.as_raw_ptr(),
473        }
474    }
475}
476
477/// Use this code to set a new color for the parent.
478///
479/// The value is the color.
480///
481/// (also see [`SetNewName`](../struct.SetNewName.html)).
482#[derive(Debug)]
483pub struct SetNewColor(pub u8);
484
485impl_message!(SetNewColor);
486
487impl From<SetNewColor> for FlMessage {
488    fn from(message: SetNewColor) -> Self {
489        FlMessage {
490            id: 23,
491            index: 0,
492            value: message.0.as_raw_ptr(),
493        }
494    }
495}
496
497// This returns the module instance of the host in Windows. This could be an exe or a DLL, so it
498// won't be the process itself.
499// #[derive(Debug)]
500// pub struct GetInstance;
501
502// impl From<GetInstance> for FlMessage {
503// fn from(_: GetInstance) -> Self {
504// FlMessage {
505// id: 24,
506// index: 0,
507// value: 0,
508// }
509// }
510// }
511
512/// Ask the host to kill anything linked to an internal controller. This is used when undeclaring
513/// internal controllers.
514///
515/// The first value is the index of the first internal controller to kill.
516///
517/// The second value is the index of the last internal controller to kill.
518#[derive(Debug)]
519pub struct KillIntCtrl(pub usize, pub usize);
520
521impl_message!(KillIntCtrl);
522
523impl From<KillIntCtrl> for FlMessage {
524    fn from(message: KillIntCtrl) -> Self {
525        FlMessage {
526            id: 25,
527            index: message.0.as_raw_ptr(),
528            value: message.1.as_raw_ptr(),
529        }
530    }
531}
532
533/// Call this to override the number of parameters that this plugin instance has. This is meant for
534/// plugins that have a different set of parameters per instance.
535///
536/// The value holds the new number of parameters.
537#[derive(Debug)]
538pub struct SetNumParams(pub usize);
539
540impl_message!(SetNumParams);
541
542impl From<SetNumParams> for FlMessage {
543    fn from(message: SetNumParams) -> Self {
544        FlMessage {
545            id: 27,
546            index: 0,
547            value: message.0.as_raw_ptr(),
548        }
549    }
550}
551
552/// Ask the host to create a filename relative to the FL Studio data folder. This makes it much
553/// faster to look for this file (samples, for example) when the song is loaded again.
554///
555/// The value is the full name.
556///
557/// The result is the packed filename `String`.
558#[derive(Debug)]
559pub struct PackDataFile(pub String);
560
561impl_message_ty!(PackDataFile, String);
562
563impl From<PackDataFile> for FlMessage {
564    fn from(message: PackDataFile) -> Self {
565        FlMessage {
566            id: 28,
567            index: 0,
568            value: message.0.as_raw_ptr(),
569        }
570    }
571}
572
573/// Ask the host where the FL Studio engine DLL is. This may be different from the location of the
574/// executable. It can be used to discover the location of the FL Studio data path.
575///
576/// The result is the path `String`.
577#[derive(Debug)]
578pub struct GetProgPath;
579
580impl_message_ty!(GetProgPath, String);
581
582impl From<GetProgPath> for FlMessage {
583    fn from(_: GetProgPath) -> Self {
584        FlMessage {
585            id: 29,
586            index: 0,
587            value: 0,
588        }
589    }
590}
591
592/// Set the plugin latency, if any.
593///
594/// The value is the latency in samples.
595#[derive(Debug)]
596pub struct SetLatency(pub u32);
597
598impl_message!(SetLatency);
599
600impl From<SetLatency> for FlMessage {
601    fn from(message: SetLatency) -> Self {
602        FlMessage {
603            id: 30,
604            index: 0,
605            value: message.0.as_raw_ptr(),
606        }
607    }
608}
609
610/// (FL 6.0) Ask the host to show the preset downloader/selector for this plugin.
611#[derive(Debug)]
612pub struct CallDownloader;
613
614impl_message!(CallDownloader);
615
616impl From<CallDownloader> for FlMessage {
617    fn from(_: CallDownloader) -> Self {
618        FlMessage {
619            id: 31,
620            index: 0,
621            value: 0,
622        }
623    }
624}
625
626/// (FL 7.0) Edits sample in Edison.
627///
628/// The first value holds the sample filename.
629///
630/// The second value is `true` if an existing instance of Edison can be re-used or `false`
631/// otherwise.
632#[derive(Debug)]
633pub struct EditSample(pub String, pub bool);
634
635impl_message!(EditSample);
636
637impl From<EditSample> for FlMessage {
638    fn from(message: EditSample) -> Self {
639        FlMessage {
640            id: 32,
641            index: message.1.as_raw_ptr(),
642            value: message.0.as_raw_ptr(),
643        }
644    }
645}
646
647/// (FL 7.0) Call this to let FL know that this plugin is thread-safe (or not). The default is not.
648/// You should do your own thread-sync using
649/// [`Host::lock_mix`](../../host/struct.Host.html#method.lock_mix).
650///
651/// The value is `false` for `not safe` and `true` for `safe`.
652///
653/// **Important: this should only be used from a generator plugin!**
654#[derive(Debug)]
655pub struct SetThreadSafe(pub bool);
656
657impl_message!(SetThreadSafe);
658
659impl From<SetThreadSafe> for FlMessage {
660    fn from(message: SetThreadSafe) -> Self {
661        FlMessage {
662            id: 33,
663            index: 0,
664            value: message.0.as_raw_ptr(),
665        }
666    }
667}
668
669/// (FL 7.0) The plugin asks FL to enable or disable smart disabling. This is mainly for
670/// generators, so they can get MIDI input (if applicable).
671///
672/// The value holds the switch.
673#[derive(Debug)]
674pub struct SmartDisable(pub bool);
675
676impl_message!(SmartDisable);
677
678impl From<SmartDisable> for FlMessage {
679    fn from(message: SmartDisable) -> Self {
680        FlMessage {
681            id: 34,
682            index: 0,
683            value: message.0.as_raw_ptr(),
684        }
685    }
686}
687
688/// (FL 8.0) Sets the unique identifying string for this plugin. This will be used to save/restore
689/// custom data related to this plugin. Handy for wrapper plugins.
690///
691/// The value is the identifying string.
692#[derive(Debug)]
693pub struct SetUid(pub String);
694
695impl_message!(SetUid);
696
697impl From<SetUid> for FlMessage {
698    fn from(message: SetUid) -> Self {
699        FlMessage {
700            id: 35,
701            index: 0,
702            value: message.0.as_raw_ptr(),
703        }
704    }
705}
706
707/// (FL 8.0) Get the mixer time, relative to the current time.
708///
709/// The first value is the time format required.
710///
711/// The second value is offset in samples.
712///
713/// The result is [`Time`](../struct.Time.html).
714#[derive(Debug)]
715pub struct GetMixingTime(pub TimeFormat, pub u64);
716
717impl Message for GetMixingTime {
718    type Return = Time;
719
720    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
721        get_time_send(self, tag, host)
722    }
723}
724
725fn get_time_send<T: Into<FlMessage>>(msg: T, tag: plugin::Tag, host: &mut Host) -> Time {
726    let message: FlMessage = msg.into();
727    let time_ptr = message.value;
728    unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
729    ValuePtr(time_ptr).get::<Time>()
730}
731
732impl From<GetMixingTime> for FlMessage {
733    fn from(message: GetMixingTime) -> Self {
734        get_time_ffi(36, message.0, message.1)
735    }
736}
737
738fn get_time_ffi(id: intptr_t, format: TimeFormat, offset: u64) -> FlMessage {
739    let time = Time(offset as f64, offset as f64);
740    FlMessage {
741        id,
742        index: u8::from(format).as_raw_ptr(),
743        value: (Box::into_raw(Box::new(time)) as *mut c_void).as_raw_ptr(),
744    }
745}
746
747/// (FL 8.0) Get playback time. See `GetMixingTime` for details.
748#[derive(Debug)]
749pub struct GetPlaybackTime(pub TimeFormat, pub u64);
750
751impl Message for GetPlaybackTime {
752    type Return = Time;
753
754    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
755        get_time_send(self, tag, host)
756    }
757}
758
759impl From<GetPlaybackTime> for FlMessage {
760    fn from(message: GetPlaybackTime) -> Self {
761        get_time_ffi(37, message.0, message.1)
762    }
763}
764
765/// (FL 8.0) Get selection time.
766///
767/// The value is the time formad required.
768///
769/// The result is [`Time`](../struct.Time.html). If there's no selection, the `Time` will content
770/// the full song range.
771#[derive(Debug)]
772pub struct GetSelTime(pub TimeFormat);
773
774impl Message for GetSelTime {
775    type Return = Time;
776
777    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
778        get_time_send(self, tag, host)
779    }
780}
781
782impl From<GetSelTime> for FlMessage {
783    fn from(message: GetSelTime) -> Self {
784        get_time_ffi(38, message.0, 0)
785    }
786}
787
788/// (FL 8.0) Get the current tempo multiplicator. This is not part of the song but used for
789/// fast-forward.
790///
791/// The result is `f32`.
792#[derive(Debug)]
793pub struct GetTimeMul;
794
795impl_message_ty!(GetTimeMul, f32);
796
797impl From<GetTimeMul> for FlMessage {
798    fn from(_: GetTimeMul) -> Self {
799        FlMessage {
800            id: 39,
801            index: 0,
802            value: 0,
803        }
804    }
805}
806
807/// (FL 8.0) Captionize the plugin. This can be useful when dragging.
808///
809/// The value is `true` for captionized or `false` otherwise.
810#[derive(Debug)]
811pub struct Captionize(pub bool);
812
813impl_message!(Captionize);
814
815impl From<Captionize> for FlMessage {
816    fn from(message: Captionize) -> Self {
817        FlMessage {
818            id: 40,
819            index: 0,
820            value: message.0.as_raw_ptr(),
821        }
822    }
823}
824
825/// (FL 8.0) Send a SysEx bytes, without delay. Do not abuse this!
826///
827/// The first value is the port to send to.
828///
829/// The second value is the data to send.
830#[derive(Debug)]
831pub struct SendSysEx<'a>(pub usize, pub &'a [u8]);
832
833impl Message for SendSysEx<'_> {
834    type Return = ();
835
836    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
837        unsafe {
838            host_on_message(*host.host_ptr.get_mut(), tag.0, self.into());
839        }
840    }
841}
842
843impl From<SendSysEx<'_>> for FlMessage {
844    fn from(message: SendSysEx<'_>) -> Self {
845        let len = message.1.len() as i32;
846        let len_bytes: [u8; mem::size_of::<i32>()] = unsafe { mem::transmute(len) };
847        let mut final_data = [&len_bytes, message.1].concat();
848        let data_ptr = final_data.as_mut_ptr();
849        mem::forget(final_data);
850
851        FlMessage {
852            id: 41,
853            index: message.0.as_raw_ptr(),
854            value: (data_ptr as *mut c_void).as_raw_ptr(),
855        }
856    }
857}
858
859/// (FL 8.0) Send an audio file to the playlist as an audio clip, starting at the playlist
860/// selection (mainly for Edison).
861///
862/// The value is the file name.
863#[derive(Debug)]
864pub struct LoadAudioClip(pub String);
865
866impl_message!(LoadAudioClip);
867
868impl From<LoadAudioClip> for FlMessage {
869    fn from(message: LoadAudioClip) -> Self {
870        FlMessage {
871            id: 42,
872            index: 0,
873            value: message.0.as_raw_ptr(),
874        }
875    }
876}
877
878/// (FL 8.0) Send a file to the selected channel(s) (mainly for Edison).
879///
880/// The value is the file name.
881#[derive(Debug)]
882pub struct LoadInChannel(pub String);
883
884impl_message!(LoadInChannel);
885
886impl From<LoadInChannel> for FlMessage {
887    fn from(message: LoadInChannel) -> Self {
888        FlMessage {
889            id: 43,
890            index: 0,
891            value: message.0.as_raw_ptr(),
892        }
893    }
894}
895
896/// (FL 8.0) Locates the specified file in the browser and jumps to it. This also adds the file's
897/// folder to the browser search paths if necessary.
898///
899/// The value is the file name.
900#[derive(Debug)]
901pub struct ShowInBrowser(pub String);
902
903impl_message!(ShowInBrowser);
904
905impl From<ShowInBrowser> for FlMessage {
906    fn from(message: ShowInBrowser) -> Self {
907        FlMessage {
908            id: 44,
909            index: 0,
910            value: message.0.as_raw_ptr(),
911        }
912    }
913}
914
915/// Adds message to the debug log.
916///
917/// The value is the message.
918#[derive(Debug)]
919pub struct DebugLogMsg(pub String);
920
921impl_message!(DebugLogMsg);
922
923impl From<DebugLogMsg> for FlMessage {
924    fn from(message: DebugLogMsg) -> Self {
925        FlMessage {
926            id: 45,
927            index: 0,
928            value: message.0.as_raw_ptr(),
929        }
930    }
931}
932
933/// Gets the handle of the main form.
934///
935/// The result is `Option<*mut c_void>` (`Option<HWND>`).
936#[derive(Debug)]
937pub struct GetMainFormHandle;
938
939impl Message for GetMainFormHandle {
940    type Return = Option<*mut c_void>;
941
942    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
943        let message = FlMessage::from(self);
944        let result_ptr = message.value;
945        unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
946
947        if result_ptr == 0 {
948            None
949        } else {
950            Some(result_ptr as *mut c_void)
951        }
952    }
953}
954
955impl From<GetMainFormHandle> for FlMessage {
956    fn from(_: GetMainFormHandle) -> Self {
957        FlMessage {
958            id: 46,
959            index: 0,
960            value: 0,
961        }
962    }
963}
964
965/// Ask the host where the project data is, to store project data.
966///
967/// The result is `String`.
968#[derive(Debug)]
969pub struct GetProjDataPath;
970
971impl_message_ty!(GetProjDataPath, String);
972
973impl From<GetProjDataPath> for FlMessage {
974    fn from(_message: GetProjDataPath) -> Self {
975        FlMessage {
976            id: 47,
977            index: 0,
978            value: 0,
979        }
980    }
981}
982
983/// Mark project as dirty (not required for automatable parameters, only for tweaks the host can't
984/// be aware of).
985#[derive(Debug)]
986pub struct SetDirty;
987
988impl_message!(SetDirty);
989
990impl From<SetDirty> for FlMessage {
991    fn from(_message: SetDirty) -> Self {
992        FlMessage {
993            id: 48,
994            index: 0,
995            value: 0,
996        }
997    }
998}
999
1000/// Add file to recent files.
1001///
1002/// The value is file name.
1003#[derive(Debug)]
1004pub struct AddToRecent(pub String);
1005
1006impl_message!(AddToRecent);
1007
1008impl From<AddToRecent> for FlMessage {
1009    fn from(message: AddToRecent) -> Self {
1010        FlMessage {
1011            id: 49,
1012            index: 0,
1013            value: message.0.as_raw_ptr(),
1014        }
1015    }
1016}
1017
1018/// Ask the host how many inputs are routed to this effect, or how many outputs this effect is
1019/// routed to.
1020///
1021/// The result is `usize`.
1022#[derive(Debug)]
1023pub enum GetNumInOut {
1024    /// To get inputs number.
1025    Inputs,
1026    /// To get outputs number.
1027    Outputs,
1028}
1029
1030impl_message_ty!(GetNumInOut, usize);
1031
1032impl From<GetNumInOut> for FlMessage {
1033    fn from(message: GetNumInOut) -> Self {
1034        FlMessage {
1035            id: 50,
1036            index: message.into(),
1037            value: 0,
1038        }
1039    }
1040}
1041
1042impl From<GetNumInOut> for intptr_t {
1043    fn from(message: GetNumInOut) -> Self {
1044        match message {
1045            GetNumInOut::Inputs => 0,
1046            GetNumInOut::Outputs => 1,
1047        }
1048    }
1049}
1050
1051/// Ask the host the name of the input.
1052///
1053/// The value is the input index starting from 1.
1054///
1055/// The result is [`Option<NameColor>`](../struct.NameColor.html).
1056///
1057/// **Important: the first input index is `1`.**
1058#[derive(Debug)]
1059pub struct GetInName(pub usize);
1060
1061impl Message for GetInName {
1062    type Return = Option<NameColor>;
1063
1064    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
1065        get_name_dispatcher(self, tag, host)
1066    }
1067}
1068
1069fn get_name_dispatcher<T: Into<FlMessage>>(
1070    msg: T,
1071    tag: plugin::Tag,
1072    host: &mut Host,
1073) -> Option<NameColor> {
1074    let message: FlMessage = msg.into();
1075    let result_ptr = message.value;
1076    let result = unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
1077
1078    if result == 0 || (result_ptr as *mut c_void).is_null() {
1079        return None;
1080    }
1081
1082    Some(ValuePtr(result_ptr).get::<TNameColor>().into())
1083}
1084
1085impl From<GetInName> for FlMessage {
1086    fn from(message: GetInName) -> Self {
1087        get_name_ffi(51, message.0)
1088    }
1089}
1090
1091fn get_name_ffi(id: intptr_t, index: usize) -> FlMessage {
1092    let name_color = TNameColor {
1093        name: [0; 256],
1094        vis_name: [0; 256],
1095        color: 0,
1096        index: index as c_int,
1097    };
1098    FlMessage {
1099        id,
1100        index: index.as_raw_ptr(),
1101        value: (Box::into_raw(Box::new(name_color)) as *mut c_void).as_raw_ptr(),
1102    }
1103}
1104
1105/// Ask the host the name of the output.
1106///
1107/// The value is the output index starting from 1.
1108///
1109/// The result is [`Option<NameColor>`](../struct.NameColor.html).
1110///
1111/// **Important: the first output index is `1`.**
1112#[derive(Debug)]
1113pub struct GetOutName(pub usize);
1114
1115impl Message for GetOutName {
1116    type Return = Option<NameColor>;
1117
1118    fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
1119        get_name_dispatcher(self, tag, host)
1120    }
1121}
1122
1123impl From<GetOutName> for FlMessage {
1124    fn from(message: GetOutName) -> Self {
1125        get_name_ffi(52, message.0)
1126    }
1127}
1128
1129/// Make the host bring plugin's editor.
1130#[derive(Debug)]
1131pub enum ShowEditor {
1132    /// Show.
1133    Show,
1134    /// Hide.
1135    Hide,
1136    /// Toggle.
1137    Toggle,
1138}
1139
1140impl_message!(ShowEditor);
1141
1142impl From<ShowEditor> for FlMessage {
1143    fn from(message: ShowEditor) -> Self {
1144        FlMessage {
1145            id: 53,
1146            index: 0,
1147            value: message.into(),
1148        }
1149    }
1150}
1151
1152impl From<ShowEditor> for intptr_t {
1153    fn from(message: ShowEditor) -> Self {
1154        match message {
1155            ShowEditor::Show => 1,
1156            ShowEditor::Hide => 0,
1157            ShowEditor::Toggle => -1,
1158        }
1159    }
1160}
1161
1162/// (for the plugin wrapper only) Ask the host to turn 0..65536 automation into 0..1 float, for
1163/// params number between the first and last value (included).
1164#[derive(Debug)]
1165pub struct FloatAutomation(pub usize, pub usize);
1166
1167impl_message!(FloatAutomation);
1168
1169impl From<FloatAutomation> for FlMessage {
1170    fn from(message: FloatAutomation) -> Self {
1171        FlMessage {
1172            id: 54,
1173            index: message.0.as_raw_ptr(),
1174            value: message.1.as_raw_ptr(),
1175        }
1176    }
1177}
1178
1179/// Called when the settings button on the titlebar should be switched.
1180///
1181/// The value is `true` to show and `false` to hide.
1182///
1183/// See
1184/// [`InfoBuilder::want_settings_button`](
1185/// ../plugin/struct.InfoBuilder.html#method.want_settings_button).
1186#[derive(Debug)]
1187pub struct ShowSettings(pub bool);
1188
1189impl_message!(ShowSettings);
1190
1191impl From<ShowSettings> for FlMessage {
1192    fn from(message: ShowSettings) -> Self {
1193        FlMessage {
1194            id: 55,
1195            index: 0,
1196            value: message.0.as_raw_ptr(),
1197        }
1198    }
1199}
1200
1201/// Note on/off.
1202///
1203/// The first value is note nummber.
1204///
1205/// The second value is the color/MIDI channel.
1206///
1207/// The third value is velocity. Note off send for velocity `0`, note on otherwise.
1208#[derive(Debug)]
1209pub struct NoteOnOff(pub u8, pub u8, pub u8);
1210
1211impl_message!(NoteOnOff);
1212
1213impl From<NoteOnOff> for FlMessage {
1214    fn from(message: NoteOnOff) -> Self {
1215        FlMessage {
1216            id: 56,
1217            index: dword_from_note_and_ch(message.0, message.1).as_raw_ptr(),
1218            value: message.2.as_raw_ptr(),
1219        }
1220    }
1221}
1222
1223/// Show picker.
1224#[derive(Debug)]
1225pub enum ShowPicker {
1226    /// Plugins.
1227    Plugins(PickerFilter),
1228    /// Project.
1229    Project(PickerFilter),
1230}
1231
1232impl_message!(ShowPicker);
1233
1234/// What kind of items the picker should show.
1235#[derive(Debug)]
1236pub enum PickerFilter {
1237    /// Generators.
1238    Generators,
1239    /// Effects.
1240    Effects,
1241    /// Generators and effects.
1242    GeneratorsEffects,
1243    /// Patcher (includes VFX).
1244    Patcher,
1245}
1246
1247impl From<ShowPicker> for FlMessage {
1248    fn from(message: ShowPicker) -> Self {
1249        let (index, value): (intptr_t, intptr_t) = message.into();
1250        FlMessage {
1251            id: 57,
1252            index,
1253            value,
1254        }
1255    }
1256}
1257
1258impl From<ShowPicker> for (intptr_t, intptr_t) {
1259    fn from(message: ShowPicker) -> Self {
1260        match message {
1261            ShowPicker::Plugins(filter) => (0, filter.into()),
1262            ShowPicker::Project(filter) => (1, filter.into()),
1263        }
1264    }
1265}
1266
1267impl From<PickerFilter> for intptr_t {
1268    fn from(filter: PickerFilter) -> Self {
1269        match filter {
1270            PickerFilter::Generators => 0,
1271            PickerFilter::Effects => 1,
1272            PickerFilter::GeneratorsEffects => -1,
1273            PickerFilter::Patcher => -2,
1274        }
1275    }
1276}
1277
1278/// Ask the host for the number of extra frames `Plugin::idle` should process, generally 0 if no
1279/// overflow/frameskip occured.
1280#[derive(Debug)]
1281pub struct GetIdleOverflow;
1282
1283impl_message!(GetIdleOverflow);
1284
1285impl From<GetIdleOverflow> for FlMessage {
1286    fn from(_: GetIdleOverflow) -> Self {
1287        FlMessage {
1288            id: 58,
1289            index: 0,
1290            value: 0,
1291        }
1292    }
1293}
1294
1295/// Used by FL plugins, when idling from a modal window, mainly for the smoothness hack.
1296#[derive(Debug)]
1297pub struct ModalIdle;
1298
1299impl_message!(ModalIdle);
1300
1301impl From<ModalIdle> for FlMessage {
1302    fn from(_: ModalIdle) -> Self {
1303        FlMessage {
1304            id: 59,
1305            index: 0,
1306            value: 0,
1307        }
1308    }
1309}
1310
1311/// Prompt the rendering dialog in song mode.
1312#[derive(Debug)]
1313pub struct RenderProject;
1314
1315impl_message!(RenderProject);
1316
1317impl From<RenderProject> for FlMessage {
1318    fn from(_: RenderProject) -> Self {
1319        FlMessage {
1320            id: 60,
1321            index: 0,
1322            value: 0,
1323        }
1324    }
1325}
1326
1327/// Get project title, author, comments or URL.
1328///
1329/// The result is `String`.
1330#[derive(Debug)]
1331pub enum GetProjectInfo {
1332    /// Title.
1333    Title,
1334    /// Author.
1335    Author,
1336    /// Comments.
1337    Comments,
1338    /// URL.
1339    Url,
1340}
1341
1342impl_message_ty!(GetProjectInfo, String);
1343
1344impl From<GetProjectInfo> for FlMessage {
1345    fn from(message: GetProjectInfo) -> Self {
1346        FlMessage {
1347            id: 61,
1348            index: message.into(),
1349            value: 0,
1350        }
1351    }
1352}
1353
1354impl From<GetProjectInfo> for intptr_t {
1355    fn from(value: GetProjectInfo) -> Self {
1356        match value {
1357            GetProjectInfo::Title => 0,
1358            GetProjectInfo::Author => 1,
1359            GetProjectInfo::Comments => 2,
1360            GetProjectInfo::Url => 3,
1361        }
1362    }
1363}
1364
1365fn dword_from_note_and_ch(note: u8, channel: u8) -> u32 {
1366    (note as u32) | ((channel as u32) << 16)
1367}
1368
1369#[cfg(test)]
1370mod tests {
1371    use super::*;
1372
1373    #[test]
1374    fn test_dword() {
1375        let value = dword_from_note_and_ch(60, 15);
1376        assert_eq!(60, value & 0xff);
1377        assert_eq!(15, (value >> 16) & 0xff);
1378    }
1379}