Skip to main content

fpsdk/
lib.rs

1//! The FL Plugin SDK helps you to make plugins for FL Studio. For more information about FL
2//! Studio, visit the [website](https://www.image-line.com/flstudio/).
3//!
4//! Note that this SDK is not meant to make hosts for FL plugins.
5//!
6//! ## How to use this library
7//!
8//! You should implement [`Plugin`](plugin/trait.Plugin.html) and export it with
9//! [`create_plugin!`](macro.create_plugin.html).
10//!
11//! To talk to the host use [`Host`](host/struct.Host.html), which is passed to the plugin's
12//! constructor.
13//!
14//! `examples/simple.rs` in the code repo provides you with more details.
15//!
16//! ## Types of plugins
17//!
18//! There are two kinds of Fruity plugins: effects and generators. Effects are plugins that receive
19//! some audio data from FL Studio and do something to it (apply an effect). Generators on the
20//! other hand create sounds that they send to FL Studio. Generators are seen as channels by the
21//! user (like the SimSynth and Sytrus). The main reason to make something a generator is that it
22//! needs input from the FL Studio pianoroll (although there are other reasons possible).
23//!
24//! ## Installation
25//!
26//! Plugins are installed in FL Studio in subfolders of the `FL Studio\Plugins\Fruity` folder on
27//! Windows and `FL\ Studio.app/Contents/Resources/FL/Plugins/Fruity` for macOS.
28//!
29//! Effects go in the **Effects** subfolder, generators are installed in the **Generators**
30//! subfolder. Each plugin has its own folder.
31//!
32//! The name of the folder has to be same as the name of the plugin. On macOS the plugin (.dylib)
33//! also has to have `_x64` suffix.
34//!
35#![deny(
36    nonstandard_style,
37    rust_2018_idioms,
38    trivial_casts,
39    trivial_numeric_casts
40)]
41#![warn(
42    deprecated_in_future,
43    missing_docs,
44    unused_import_braces,
45    unused_labels,
46    unused_lifetimes,
47    unused_qualifications,
48    unreachable_pub
49)]
50
51pub mod host;
52pub mod plugin;
53pub mod voice;
54
55use std::ffi::{CStr, CString};
56use std::mem;
57use std::os::raw::{c_char, c_int, c_void};
58
59use bitflags::bitflags;
60use log::{debug, error};
61
62/// Current FL SDK version.
63pub const CURRENT_SDK_VERSION: u32 = 1;
64
65/// Size of wavetable used by FL.
66pub const WAVETABLE_SIZE: usize = 16384;
67
68/// intptr_t alias
69#[allow(non_camel_case_types)]
70#[doc(hidden)]
71pub type intptr_t = isize;
72
73/// An identefier the host uses to identify plugin and voice instances.
74///
75/// To make it more type safe, `plugin` and `voice` modules provide their own `Tag` type.
76pub(crate) type Tag = intptr_t;
77
78/// This macro is used internally to implement `Tag` type in a type-safe manner.
79#[doc(hidden)]
80#[macro_export]
81macro_rules! implement_tag {
82    () => {
83        use std::fmt;
84
85        /// Identifier.
86        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
87        pub struct Tag(pub crate::Tag);
88
89        impl fmt::Display for Tag {
90            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91                write!(f, "{}", self.0)
92            }
93        }
94    };
95}
96
97#[derive(Debug, Clone)]
98#[repr(C)]
99struct FlMessage {
100    id: intptr_t,
101    index: intptr_t,
102    value: intptr_t,
103}
104
105/// For types, which can be represented as `intptr_t`.
106pub trait AsRawPtr {
107    /// Conversion method.
108    fn as_raw_ptr(&self) -> intptr_t;
109}
110
111macro_rules! primitive_as_raw_ptr {
112    ($type:ty) => {
113        impl AsRawPtr for $type {
114            fn as_raw_ptr(&self) -> intptr_t {
115                (*self) as intptr_t
116            }
117        }
118    };
119}
120
121primitive_as_raw_ptr!(i8);
122primitive_as_raw_ptr!(u8);
123primitive_as_raw_ptr!(i16);
124primitive_as_raw_ptr!(u16);
125primitive_as_raw_ptr!(i32);
126primitive_as_raw_ptr!(u32);
127primitive_as_raw_ptr!(i64);
128primitive_as_raw_ptr!(u64);
129primitive_as_raw_ptr!(usize);
130primitive_as_raw_ptr!(*mut c_void);
131primitive_as_raw_ptr!(*const c_void);
132
133impl AsRawPtr for bool {
134    fn as_raw_ptr(&self) -> intptr_t {
135        (self.to_owned() as u8).into()
136    }
137}
138
139impl AsRawPtr for f32 {
140    fn as_raw_ptr(&self) -> intptr_t {
141        self.to_bits() as intptr_t
142    }
143}
144
145impl AsRawPtr for f64 {
146    fn as_raw_ptr(&self) -> intptr_t {
147        self.to_bits() as intptr_t
148    }
149}
150
151impl AsRawPtr for String {
152    fn as_raw_ptr(&self) -> intptr_t {
153        let value = CString::new(self.clone()).unwrap_or_else(|e| {
154            error!("{}", e);
155            panic!();
156        });
157        // alloc_real_cstr prevents memory leak caused by CString::into_raw
158        unsafe { alloc_real_cstr(value.into_raw()) as intptr_t }
159    }
160}
161
162/// FFI to make C string (`char *`) managed by C side. Because `char *` produced by
163/// `CString::into_raw` leads to memory leak:
164///
165/// > The pointer which this function returns must be returned to Rust and reconstituted using
166/// > from_raw to be properly deallocated. Specifically, one should not use the standard C free()
167/// > function to deallocate this string.
168#[no_mangle]
169extern "C" {
170    fn alloc_real_cstr(raw_str: *mut c_char) -> *mut c_char;
171}
172
173/// For conversion from `intptr_t`.
174pub trait FromRawPtr {
175    /// Conversion method.
176    fn from_raw_ptr(value: intptr_t) -> Self
177    where
178        Self: Sized;
179}
180
181macro_rules! primitive_from_raw_ptr {
182    ($type:ty) => {
183        impl FromRawPtr for $type {
184            fn from_raw_ptr(value: intptr_t) -> Self {
185                value as Self
186            }
187        }
188    };
189}
190
191primitive_from_raw_ptr!(i8);
192primitive_from_raw_ptr!(u8);
193primitive_from_raw_ptr!(i16);
194primitive_from_raw_ptr!(u16);
195primitive_from_raw_ptr!(i32);
196primitive_from_raw_ptr!(u32);
197primitive_from_raw_ptr!(i64);
198primitive_from_raw_ptr!(u64);
199primitive_from_raw_ptr!(usize);
200primitive_from_raw_ptr!(*mut c_void);
201primitive_from_raw_ptr!(*const c_void);
202
203impl FromRawPtr for f32 {
204    fn from_raw_ptr(value: intptr_t) -> Self {
205        f32::from_bits(value as i32 as u32)
206    }
207}
208
209impl FromRawPtr for f64 {
210    fn from_raw_ptr(value: intptr_t) -> Self {
211        f64::from_bits(value as i64 as u64)
212    }
213}
214
215impl FromRawPtr for String {
216    fn from_raw_ptr(value: intptr_t) -> Self {
217        let cstr = unsafe { CStr::from_ptr(value as *const c_char) };
218        cstr.to_string_lossy().to_string()
219    }
220}
221
222impl FromRawPtr for bool {
223    fn from_raw_ptr(value: intptr_t) -> Self {
224        value != 0
225    }
226}
227
228/// Raw pointer to value.
229#[derive(Debug)]
230pub struct ValuePtr(intptr_t);
231
232impl ValuePtr {
233    /// Get value.
234    ///
235    /// See [`FromRawPtr`](trait.FromRawPtr.html) for implemented types.
236    pub fn get<T: FromRawPtr>(&self) -> T {
237        T::from_raw_ptr(self.0)
238    }
239}
240
241impl FromRawPtr for ValuePtr {
242    fn from_raw_ptr(value: intptr_t) -> Self {
243        ValuePtr(value)
244    }
245}
246
247/// Time signature.
248#[derive(Debug, Clone)]
249pub struct TimeSignature {
250    /// Steps per bar.
251    pub steps_per_bar: u32,
252    /// Steps per beat.
253    pub steps_per_beat: u32,
254    /// Pulses per quarter note.
255    pub ppq: u32,
256}
257
258impl From<TTimeSigInfo> for TimeSignature {
259    fn from(value: TTimeSigInfo) -> Self {
260        Self {
261            steps_per_bar: value.steps_per_bar as u32,
262            steps_per_beat: value.steps_per_beat as u32,
263            ppq: value.ppq as u32,
264        }
265    }
266}
267
268#[repr(C)]
269struct TTimeSigInfo {
270    steps_per_bar: c_int,
271    steps_per_beat: c_int,
272    ppq: c_int,
273}
274
275impl FromRawPtr for TTimeSigInfo {
276    fn from_raw_ptr(raw_ptr: intptr_t) -> Self {
277        let sig_ptr = raw_ptr as *mut c_void as *mut TTimeSigInfo;
278        unsafe {
279            Self {
280                steps_per_beat: (*sig_ptr).steps_per_beat,
281                steps_per_bar: (*sig_ptr).steps_per_bar,
282                ppq: (*sig_ptr).ppq,
283            }
284        }
285    }
286}
287
288/// Time format.
289#[derive(Debug)]
290pub enum TimeFormat {
291    /// Beats.
292    Beats,
293    /// Absolute ms.
294    AbsoluteMs,
295    /// Running ms.
296    RunningMs,
297    /// Time since sound card restart (in ms).
298    RestartMs,
299}
300
301impl From<TimeFormat> for u8 {
302    fn from(format: TimeFormat) -> Self {
303        match format {
304            TimeFormat::Beats => 0,
305            TimeFormat::AbsoluteMs => 1,
306            TimeFormat::RunningMs => 2,
307            TimeFormat::RestartMs => 3,
308        }
309    }
310}
311
312/// Time.
313///
314/// The first value is mixing time.
315///
316/// The second value is offset in samples.
317#[derive(Debug, Default)]
318#[repr(C)]
319pub struct Time(pub f64, pub f64);
320
321impl FromRawPtr for Time {
322    fn from_raw_ptr(value: intptr_t) -> Self {
323        unsafe { *Box::from_raw(value as *mut c_void as *mut Time) }
324    }
325}
326
327/// Song time in **bar:step:tick** format.
328#[allow(missing_docs)]
329#[derive(Clone, Debug, Default)]
330#[repr(C)]
331pub struct SongTime {
332    pub bar: i32,
333    pub step: i32,
334    pub tick: i32,
335}
336
337impl FromRawPtr for SongTime {
338    fn from_raw_ptr(value: intptr_t) -> Self {
339        unsafe { *Box::from_raw(value as *mut c_void as *mut Self) }
340    }
341}
342
343/// Name of the color (or MIDI channel) in Piano Roll.
344#[derive(Debug)]
345pub struct NameColor {
346    /// User-defined name (can be empty).
347    pub name: String,
348    /// Visible name (can be guessed).
349    pub vis_name: String,
350    /// Color/MIDI channel index.
351    pub color: u8,
352    /// Real index of the item (can be used to translate plugin's own in/out into real mixer track
353    /// number).
354    pub index: usize,
355}
356
357// Type used in FFI for [`NameColor`](struct.NameColor.html).
358#[repr(C)]
359struct TNameColor {
360    name: [u8; 256],
361    vis_name: [u8; 256],
362    color: c_int,
363    index: c_int,
364}
365
366impl From<TNameColor> for NameColor {
367    fn from(name_color: TNameColor) -> Self {
368        Self {
369            name: String::from_utf8_lossy(&name_color.name[..]).to_string(),
370            vis_name: String::from_utf8_lossy(&name_color.vis_name[..]).to_string(),
371            color: name_color.color as u8,
372            index: name_color.index as usize,
373        }
374    }
375}
376
377impl From<NameColor> for TNameColor {
378    fn from(name_color: NameColor) -> Self {
379        let mut name = [0_u8; 256];
380        name.copy_from_slice(name_color.name.as_bytes());
381        let mut vis_name = [0_u8; 256];
382        vis_name.copy_from_slice(name_color.vis_name.as_bytes());
383        Self {
384            name,
385            vis_name,
386            color: name_color.color as c_int,
387            index: name_color.index as c_int,
388        }
389    }
390}
391
392impl FromRawPtr for TNameColor {
393    fn from_raw_ptr(value: intptr_t) -> Self {
394        unsafe { *Box::from_raw(value as *mut Self) }
395    }
396}
397
398/// MIDI message.
399#[derive(Debug)]
400pub struct MidiMessage {
401    /// Status byte.
402    pub status: u8,
403    /// First data byte.
404    pub data1: u8,
405    /// Second data byte.
406    pub data2: u8,
407    /// Port number.
408    pub port: u8,
409}
410
411impl From<&mut c_int> for MidiMessage {
412    fn from(value: &mut c_int) -> Self {
413        Self {
414            status: (*value & 0xff) as u8,
415            data1: ((*value >> 8) & 0xff) as u8,
416            data2: ((*value >> 16) & 0xff) as u8,
417            port: ((*value >> 24) & 0xff) as u8,
418        }
419    }
420}
421
422impl From<c_int> for MidiMessage {
423    fn from(value: c_int) -> Self {
424        Self {
425            status: (value & 0xff) as u8,
426            data1: ((value >> 8) & 0xff) as u8,
427            data2: ((value >> 16) & 0xff) as u8,
428            port: ((value >> 24) & 0xff) as u8,
429        }
430    }
431}
432
433/// Collection of notes, which you can add to the piano roll using
434/// [`Host::on_message`](host/struct.Host.html#on_message.new) with message
435/// [`plugin::message::AddToPianoRoll`](./plugin/message/struct.AddToPianoRoll.html).
436#[derive(Debug)]
437pub struct Notes {
438    // 0=step seq (not supported yet), 1=piano roll
439    //target: i32,
440    /// Notes.
441    pub notes: Vec<Note>,
442    /// See [`NotesFlags`](struct.NotesFlags.html).
443    pub flags: NotesFlags,
444    /// Pattern number. `None` for current.
445    pub pattern: Option<u32>,
446    /// Channel number. `None` for plugin's channel, or selected channel if plugin is an effect.
447    pub channel: Option<u32>,
448}
449
450/// This type represents a note in [`Notes`](struct.Notes.html).
451#[derive(Debug)]
452#[repr(C)]
453pub struct Note {
454    /// Position in PPQ.
455    pub position: i32,
456    /// Length in PPQ.
457    pub length: i32,
458    /// Pan in range -100..100.
459    pub pan: i32,
460    /// Volume.
461    pub vol: i32,
462    /// Note number.
463    pub note: i16,
464    /// Color or MIDI channel in range of 0..15.
465    pub color: i16,
466    /// Fine pitch in range -1200..1200.
467    pub pitch: i32,
468    /// Mod X or filter cutoff frequency.
469    pub mod_x: f32,
470    /// Mod Y or filter resonance (Q).
471    pub mod_y: f32,
472}
473
474bitflags! {
475    /// Notes parameters flags.
476    pub struct NotesFlags: isize {
477        /// Delete everything currently on the piano roll before adding the notes.
478        const EMPTY_FIRST = 1;
479        /// Put the new notes in the piano roll selection, if there is one.
480        const USE_SELECTION = 2;
481    }
482}
483
484// This type in FL SDK is what we represent as Notes. Here we use it for FFI, to send it to C++.
485#[repr(C)]
486struct TNotesParams {
487    target: c_int,
488    flags: c_int,
489    pat_num: c_int,
490    chan_num: c_int,
491    count: c_int,
492    notes: *mut Note,
493}
494
495impl From<Notes> for TNotesParams {
496    fn from(mut notes: Notes) -> Self {
497        notes.notes.shrink_to_fit();
498        let notes_ptr = notes.notes.as_mut_ptr();
499        let len = notes.notes.len();
500        mem::forget(notes.notes);
501
502        Self {
503            target: 1,
504            flags: notes.flags.bits() as c_int,
505            pat_num: notes.pattern.map(|v| v as c_int).unwrap_or(-1),
506            chan_num: notes.channel.map(|v| v as c_int).unwrap_or(-1),
507            count: len as c_int,
508            notes: notes_ptr,
509        }
510    }
511}
512
513/// Describes an item that should be added to a control's right-click popup menu.
514#[derive(Debug)]
515pub struct ParamMenuEntry {
516    /// Name.
517    pub name: String,
518    /// Flags.
519    pub flags: ParamMenuItemFlags,
520}
521
522bitflags! {
523    /// Parameter popup menu item flags.
524    pub struct ParamMenuItemFlags: i32 {
525        /// The item is disabled
526        const DISABLED = 1;
527        /// The item is checked
528        const CHECKED = 2;
529    }
530}
531
532impl ParamMenuEntry {
533    fn from_ffi(ffi_t: *mut TParamMenuEntry) -> Self {
534        Self {
535            name: unsafe { CString::from_raw((*ffi_t).name) }
536                .to_string_lossy()
537                .to_string(),
538            flags: ParamMenuItemFlags::from_bits(unsafe { (*ffi_t).flags })
539                .unwrap_or_else(ParamMenuItemFlags::empty),
540        }
541    }
542}
543
544#[derive(Debug)]
545#[repr(C)]
546struct TParamMenuEntry {
547    name: *mut c_char,
548    flags: c_int,
549}
550
551bitflags! {
552    /// Parameter flags.
553    pub struct ParameterFlags: isize {
554        /// Makes no sense to interpolate parameter values (when values are not levels).
555        const CANT_INTERPOLATE = 1;
556        /// Parameter is a normalized (0..1) single float. (Integer otherwise)
557        const FLOAT = 2;
558        /// Parameter appears centered in event editors.
559        const CENTERED = 4;
560    }
561}
562
563impl AsRawPtr for ParameterFlags {
564    fn as_raw_ptr(&self) -> intptr_t {
565        self.bits()
566    }
567}
568
569bitflags! {
570    /// Processing mode flags.
571    pub struct ProcessModeFlags: isize {
572        /// Realtime rendering.
573        const NORMAL = 0;
574        /// Realtime rendering with a higher quality.
575        const HQ_REALTIME = 1;
576        /// Non realtime processing (CPU does not matter, quality does) (normally set when
577        /// rendering only).
578        const HQ_NON_REALTIME = 2;
579        /// FL is rendering to file if this flag is set.
580        const IS_RENDERING = 16;
581        /// (changed in FL 7.0) 3 bits value for interpolation quality.
582        ///
583        /// - 0=none (obsolete)
584        /// - 1=linear
585        /// - 2=6 point hermite (default)
586        /// - 3=32 points sinc
587        /// - 4=64 points sinc
588        /// - 5=128 points sinc
589        /// - 6=256 points sinc
590        const IP_MASK = 0xFFFF << 8;
591    }
592}
593
594bitflags! {
595    /// Processing parameters flags.
596    pub struct ProcessParamFlags: isize {
597        /// Update the value of the parameter.
598        const UPDATE_VALUE = 1;
599        /// Return the value of the parameter as the result of the function.
600        const GET_VALUE = 2;
601        /// Update the hint if there is one.
602        const SHOW_HINT = 4;
603        /// Update the parameter control (wheel, slider, ...).
604        const UPDATE_CONTROL = 16;
605        /// A value between 0 and 65536 has to be translated to the range of the parameter control.
606        ///
607        /// Note that you should also return the translated value, even if
608        /// [ProcessParamFlags::GET_VALUE](
609        /// struct.ProcessParamFlags.html#associatedconstant.GET_VALUE) isn't included.
610        const FROM_MIDI = 32;
611        /// (internal) Don't check if wheels are linked.
612        const NO_LINK = 1024;
613        /// Sent by an internal controller. Internal controllers should pay attention to these,
614        /// to avoid Feedback of controller changes.
615        const INTERNAL_CTRL = 2048;
616        /// This flag is free to be used by the plugin as it wishes.
617        const PLUG_RESERVED = 4096;
618    }
619}
620
621bitflags! {
622    /// Sample loading flags.
623    pub struct SampleLoadFlags: isize {
624        ///This tells the sample loader to show an open box, for the user to select a sample
625        const SHOW_DIALOG = 1;
626        /// Force the sample to be reloaded, even if the filename is the same.
627        ///
628        /// This is handy in case you modified the sample, for example
629        const FORCE_RELOAD = 2;
630        /// Don't load the sample, instead get its filename & make sure that the format is correct
631        ///
632        /// (useful after [host::HostMessage::ChanSampleChanged](
633        /// enum.HostMessage.html#variant.ChanSampleChanged))
634        const GET_NAME = 4;
635        /// Don't resample to the host sample rate
636        const NO_RESAMPLING = 5;
637    }
638}
639
640/// if `Jog`, `StripJog`, `MarkerJumpJog`, `MarkerSelJog`, `Previous` or `Next` don't answer,
641/// `PreviousNext` will be tried. So it's best to implement at least `PreviousNext`.
642///
643/// if `PunchIn` or `PunchOut` don't answer, `Punch` will be tried
644///
645/// if `UndoUp` doesn't answer, `UndoJog` will be tried
646///
647/// if `AddAltMarker` doesn't answer, `AddMarker` will be tried
648///
649/// if `Cut`, `Copy`, `Paste`, `Insert`, `Delete`, `NextWindow`, `Enter`, `Escape`, `Yes`, `No`,
650/// `Fx` don't answer, standard keystrokes will be simulated
651#[allow(missing_docs)]
652#[derive(Debug)]
653pub enum Transport {
654    /// Generic jog (can be used to select stuff).
655    Jog(Jog),
656    /// Alternate generic jog (can be used to relocate stuff).
657    Jog2(Jog),
658    /// Touch-sensitive jog strip, value will be in -65536..65536 for leftmost..rightmost.
659    Strip(Jog),
660    /// Touch-sensitive jog in jog mode.
661    StripJog(Jog),
662    /// Value will be `0` for release, 1,2 for 1,2 fingers centered mode, -1,-2 for 1,2 fingers jog
663    /// mode (will then send `StripJog`).
664    StripHold(Jog),
665    Previous(Button),
666    Next(Button),
667    /// Generic track selection.
668    PreviousNext(Jog),
669    /// Used to relocate items.
670    MoveJog(Jog),
671    /// Play/pause.
672    Play(Button),
673    Stop(Button),
674    Record(Button),
675    Rewind(Hold),
676    FastForward(Hold),
677    Loop(Button),
678    Mute(Button),
679    /// Generic or record mode.
680    Mode(Button),
681    /// Undo/redo last, or undo down in history.
682    Undo(Button),
683    /// Undo up in history (no need to implement if no undo history).
684    UndoUp(Button),
685    /// Undo in history (no need to implement if no undo history).
686    UndoJog(Jog),
687    /// Live selection.
688    Punch(Hold),
689    PunchIn(Button),
690    PunchOut(Button),
691    AddMarker(Button),
692    /// Add alternate marker.
693    AddAltMarker(Button),
694    /// Marker jump.
695    MarkerJumpJog(Jog),
696    /// Marker selection.
697    MarkerSelJog(Jog),
698    Up(Button),
699    Down(Button),
700    Left(Button),
701    Right(Button),
702    HZoomJog(Jog),
703    VZoomJog(Jog),
704    /// Snap on/off.
705    Snap(Button),
706    SnapMode(Jog),
707    Cut(Button),
708    Copy(Button),
709    Paste(Button),
710    Insert(Button),
711    Delete(Button),
712    /// TAB.
713    NextWindow(Button),
714    /// Window selection.
715    WindowJog(Jog),
716    F1(Button),
717    F2(Button),
718    F3(Button),
719    F4(Button),
720    F5(Button),
721    F6(Button),
722    F7(Button),
723    F8(Button),
724    F9(Button),
725    F10(Button),
726    /// Enter/accept.
727    Enter(Button),
728    /// Escape/cancel.
729    Escape(Button),
730    Yes(Button),
731    No(Button),
732    /// Generic menu.
733    Menu(Button),
734    /// Item edit/tool/contextual menu.
735    ItemMenu(Button),
736    Save(Button),
737    SaveNew(Button),
738    Unknown,
739}
740
741impl From<FlMessage> for Transport {
742    fn from(message: FlMessage) -> Self {
743        match message.index {
744            0 => Transport::Jog(Jog(message.value as i64)),
745            1 => Transport::Jog2(Jog(message.value as i64)),
746            2 => Transport::Strip(Jog(message.value as i64)),
747            3 => Transport::StripJog(Jog(message.value as i64)),
748            4 => Transport::StripHold(Jog(message.value as i64)),
749            5 => Transport::Previous(Button(message.value as u8)),
750            6 => Transport::Next(Button(message.value as u8)),
751            7 => Transport::PreviousNext(Jog(message.value as i64)),
752            8 => Transport::MoveJog(Jog(message.value as i64)),
753            10 => Transport::Play(Button(message.value as u8)),
754            11 => Transport::Stop(Button(message.value as u8)),
755            12 => Transport::Record(Button(message.value as u8)),
756            13 => Transport::Rewind(Hold(message.value != 0)),
757            14 => Transport::FastForward(Hold(message.value != 0)),
758            15 => Transport::Loop(Button(message.value as u8)),
759            16 => Transport::Mute(Button(message.value as u8)),
760            17 => Transport::Mode(Button(message.value as u8)),
761            20 => Transport::Undo(Button(message.value as u8)),
762            21 => Transport::UndoUp(Button(message.value as u8)),
763            22 => Transport::UndoJog(Jog(message.value as i64)),
764            30 => Transport::Punch(Hold(message.value != 0)),
765            31 => Transport::PunchIn(Button(message.value as u8)),
766            32 => Transport::PunchOut(Button(message.value as u8)),
767            33 => Transport::AddMarker(Button(message.value as u8)),
768            34 => Transport::AddAltMarker(Button(message.value as u8)),
769            35 => Transport::MarkerJumpJog(Jog(message.value as i64)),
770            36 => Transport::MarkerSelJog(Jog(message.value as i64)),
771            40 => Transport::Up(Button(message.value as u8)),
772            41 => Transport::Down(Button(message.value as u8)),
773            42 => Transport::Left(Button(message.value as u8)),
774            43 => Transport::Right(Button(message.value as u8)),
775            44 => Transport::HZoomJog(Jog(message.value as i64)),
776            45 => Transport::VZoomJog(Jog(message.value as i64)),
777            48 => Transport::Snap(Button(message.value as u8)),
778            49 => Transport::SnapMode(Jog(message.value as i64)),
779            50 => Transport::Cut(Button(message.value as u8)),
780            51 => Transport::Copy(Button(message.value as u8)),
781            52 => Transport::Paste(Button(message.value as u8)),
782            53 => Transport::Insert(Button(message.value as u8)),
783            54 => Transport::Delete(Button(message.value as u8)),
784            58 => Transport::NextWindow(Button(message.value as u8)),
785            59 => Transport::WindowJog(Jog(message.value as i64)),
786            60 => Transport::F1(Button(message.value as u8)),
787            61 => Transport::F2(Button(message.value as u8)),
788            62 => Transport::F3(Button(message.value as u8)),
789            63 => Transport::F4(Button(message.value as u8)),
790            64 => Transport::F5(Button(message.value as u8)),
791            65 => Transport::F6(Button(message.value as u8)),
792            66 => Transport::F7(Button(message.value as u8)),
793            67 => Transport::F8(Button(message.value as u8)),
794            68 => Transport::F9(Button(message.value as u8)),
795            69 => Transport::F10(Button(message.value as u8)),
796            80 => Transport::Enter(Button(message.value as u8)),
797            81 => Transport::Escape(Button(message.value as u8)),
798            82 => Transport::Yes(Button(message.value as u8)),
799            83 => Transport::No(Button(message.value as u8)),
800            90 => Transport::Menu(Button(message.value as u8)),
801            91 => Transport::ItemMenu(Button(message.value as u8)),
802            92 => Transport::Save(Button(message.value as u8)),
803            93 => Transport::SaveNew(Button(message.value as u8)),
804            _ => Transport::Unknown,
805        }
806    }
807}
808
809/// `0` for release, `1` for switch (if release is not supported), `2` for hold (if release should
810/// be expected).
811#[derive(Debug)]
812pub struct Button(pub u8);
813
814/// `false` for release, `true` for hold.
815#[derive(Debug)]
816pub struct Hold(pub bool);
817
818/// Value is an integer increment.
819#[derive(Debug)]
820pub struct Jog(pub i64);
821
822bitflags! {
823    /// Message box flags (see
824    /// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox).
825    pub struct MessageBoxFlags: isize {
826        // To indicate the buttons displayed in the message box, specify one of the following
827        // values.
828
829        /// The message box contains three push buttons: Abort, Retry, and Ignore.
830        const ABORTRETRYIGNORE = 0x0000_0002;
831        /// The message box contains three push buttons: Cancel, Try Again, Continue. Use this
832        /// message box type instead of ABORTRETRYIGNORE.
833        const CANCELTRYCONTINUE = 0x0000_0006;
834        /// Adds a Help button to the message box. When the user clicks the Help button or presses
835        /// F1, the system sends a WM_HELP message to the owner.
836        const HELP = 0x0000_4000;
837        /// The message box contains one push button: OK. This is the default.
838        const OK = 0x0000_0000;
839        /// The message box contains two push buttons: OK and Cancel.
840        const OKCANCEL = 0x0000_0001;
841        /// The message box contains two push buttons: Retry and Cancel.
842        const RETRYCANCEL = 0x0000_0005;
843        /// The message box contains two push buttons: Yes and No.
844        const YESNO = 0x0000_0004;
845        /// The message box contains three push buttons: Yes, No, and Cancel.
846        const YESNOCANCEL = 0x0000_0003;
847
848        // To display an icon in the message box, specify one of the following values.
849
850        /// An exclamation-point icon appears in the message box.
851        const ICONEXCLAMATION = 0x0000_0030;
852        /// An exclamation-point icon appears in the message box.
853        const ICONWARNING = 0x0000_0030;
854        /// An icon consisting of a lowercase letter i in a circle appears in the message box.
855        const ICONINFORMATION = 0x0000_0040;
856        /// An icon consisting of a lowercase letter i in a circle appears in the message box.
857        const ICONASTERISK = 0x0000_0040;
858        /// A question-mark icon appears in the message box. The question-mark message icon is no
859        /// longer recommended because it does not clearly represent a specific type of message and
860        /// because the phrasing of a message as a question could apply to any message type. In
861        /// addition, users can confuse the message symbol question mark with Help information.
862        /// Therefore, do not use this question mark message symbol in your message boxes. The
863        /// system continues to support its inclusion only for backward compatibility.
864        const ICONQUESTION = 0x0000_0020;
865        /// A stop-sign icon appears in the message box.
866        const ICONSTOP = 0x0000_0010;
867        /// A stop-sign icon appears in the message box.
868        const ICONERROR = 0x0000_0010;
869        /// A stop-sign icon appears in the message box.
870        const ICONHAND = 0x0000_0010;
871
872        // To indicate the default button, specify one of the following values.
873
874        /// The first button is the default button.
875        ///
876        /// DEFBUTTON1 is the default unless DEFBUTTON2, DEFBUTTON3, or DEFBUTTON4 is specified.
877        const DEFBUTTON1 = 0x0000_0000;
878
879        /// The second button is the default button.
880        const DEFBUTTON2 = 0x0000_0100;
881        /// The third button is the default button.
882        const DEFBUTTON3 = 0x0000_0200;
883        /// The fourth button is the default button.
884        const DEFBUTTON4 = 0x0000_0300;
885
886        // To indicate the modality of the dialog box, specify one of the following values.
887
888        /// The user must respond to the message box before continuing work in the window
889        /// identified by the hWnd parameter. However, the user can move to the windows of other
890        /// threads and work in those windows.
891        ///
892        /// Depending on the hierarchy of windows in the application, the user may be able to move
893        /// to other windows within the thread. All child windows of the parent of the message box
894        /// are automatically disabled, but pop-up windows are not.
895        ///
896        /// APPLMODAL is the default if neither SYSTEMMODAL nor TASKMODAL is specified.
897        const APPLMODAL = 0x0000_0000;
898
899
900        /// Same as APPLMODAL except that the message box has the WS_EX_TOPMOST style. Use
901        /// system-modal message boxes to notify the user of serious, potentially damaging errors
902        /// that require immediate attention (for example, running out of memory). This flag has no
903        /// effect on the user's ability to interact with windows other than those associated with
904        /// hWnd.
905        const SYSTEMMODAL = 0x0000_1000;
906        /// Same as APPLMODAL except that all the top-level windows belonging to the current thread
907        /// are disabled if the hWnd parameter is NULL. Use this flag when the calling application
908        /// or library does not have a window handle available but still needs to prevent input to
909        /// other windows in the calling thread without suspending other threads.
910        const TASKMODAL = 0x0000_2000;
911
912        // To specify other options, use one or more of the following values.
913
914        /// Same as desktop of the interactive window station. For more information, see Window
915        /// Stations.
916        ///
917        /// If the current input desktop is not the default desktop, MessageBox does not return
918        /// until the user switches to the default desktop.
919        const DEFAULT_DESKTOP_ONLY = 0x0002_0000;
920
921        /// The text is right-justified.
922        const RIGHT = 0x0008_0000;
923        /// Displays message and caption text using right-to-left reading order on Hebrew and
924        /// Arabic systems.
925        const RTLREADING = 0x0010_0000;
926        /// The message box becomes the foreground window. Internally, the system calls the
927        /// SetForegroundWindow function for the message box.
928        const SETFOREGROUND = 0x0001_0000;
929        /// The message box is created with the WS_EX_TOPMOST window style.
930        const TOPMOST = 0x0004_0000;
931        /// The caller is a service notifying the user of an event. The function displays a message
932        /// box on the current active desktop, even if there is no user logged on to the computer.
933        ///
934        /// Terminal Services: If the calling thread has an impersonation token, the function
935        /// directs the message box to the session specified in the impersonation token.
936        ///
937        /// If this flag is set, the hWnd parameter must be NULL. This is so that the message box
938        /// can appear on a desktop other than the desktop corresponding to the hWnd.
939        ///
940        /// For information on security considerations in regard to using this flag, see
941        /// Interactive Services. In particular, be aware that this flag can produce interactive
942        /// content on a locked desktop and should therefore be used for only a very limited set of
943        /// scenarios, such as resource exhaustion.
944        const SERVICE_NOTIFICATION = 0x0020_0000;
945    }
946}
947
948impl AsRawPtr for MessageBoxFlags {
949    fn as_raw_ptr(&self) -> intptr_t {
950        self.bits()
951    }
952}
953
954/// The result returned by a message box.
955///
956/// See
957/// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox#return-value
958/// for more info.
959#[derive(Debug)]
960pub enum MessageBoxResult {
961    /// The OK button was selected.
962    Ok,
963    /// The Cancel button was selected.
964    Cancel,
965    /// The Abort button was selected.
966    Abort,
967    /// The Retry button was selected.
968    Retry,
969    /// The Ignore button was selected.
970    Ignore,
971    /// The Yes button was selected.
972    Yes,
973    /// The No button was selected.
974    No,
975    /// The Try Again button was selected.
976    TryAgain,
977    /// The Continue button was selected.
978    Continue,
979    /// Unknown.
980    Unknown,
981}
982
983impl FromRawPtr for MessageBoxResult {
984    fn from_raw_ptr(value: intptr_t) -> Self {
985        match value {
986            1 => MessageBoxResult::Ok,
987            2 => MessageBoxResult::Cancel,
988            3 => MessageBoxResult::Abort,
989            4 => MessageBoxResult::Retry,
990            5 => MessageBoxResult::Ignore,
991            6 => MessageBoxResult::Yes,
992            7 => MessageBoxResult::No,
993            10 => MessageBoxResult::TryAgain,
994            11 => MessageBoxResult::Continue,
995            _ => MessageBoxResult::Unknown,
996        }
997    }
998}
999
1000#[no_mangle]
1001unsafe extern "C" fn fplog(message: *const c_char) {
1002    debug!("{}", CStr::from_ptr(message).to_string_lossy());
1003}
1004
1005/// FFI to free rust's Box::into_raw pointer.
1006///
1007/// It supposed to be used internally. Don't use it.
1008///
1009/// # Safety
1010///
1011/// Unsafe
1012#[no_mangle]
1013unsafe extern "C" fn free_rbox_raw(raw_ptr: *mut c_void) {
1014    let _ = Box::from_raw(raw_ptr);
1015}
1016
1017/// FFI to free rust's CString pointer.
1018///
1019/// It supposed to be used internally. Don't use it.
1020///
1021/// # Safety
1022///
1023/// Unsafe
1024#[no_mangle]
1025unsafe extern "C" fn free_rstring(raw_str: *mut c_char) {
1026    let _ = CString::from_raw(raw_str);
1027}