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}