1use crate::common::LeakManager;
2use aviutl2_sys::plugin2::EDIT_SECTION;
3use std::mem::MaybeUninit;
4use std::{ffi::c_void, ptr::NonNull};
5
6pub trait FilterConfigItems: Sized {
21 fn to_config_items() -> Vec<crate::filter::FilterConfigItem>;
23
24 fn from_config_items(items: &[crate::filter::FilterConfigItem]) -> Self;
30}
31#[doc(inline)]
32pub use aviutl2_macros::filter_config_items;
33
34pub trait FilterConfigItemSliceExt {
36 fn to_struct<T: crate::filter::FilterConfigItems>(&self) -> T;
38}
39
40impl FilterConfigItemSliceExt for &[FilterConfigItem] {
41 fn to_struct<T: FilterConfigItems>(&self) -> T {
42 T::from_config_items(self)
43 }
44}
45
46#[allow(clippy::large_enum_variant)]
48#[derive(Debug, Clone)]
49pub enum FilterConfigItem {
50 Track(FilterConfigTrack),
52 Checkbox(FilterConfigCheckbox),
54 Color(FilterConfigColor),
56 Select(FilterConfigSelect),
58 File(FilterConfigFile),
60 String(FilterConfigString),
62 Text(FilterConfigText),
64 Folder(FilterConfigFolder),
66 Data(ErasedFilterConfigData),
68 Group(FilterConfigGroup),
70 Separator(FilterConfigSeparator),
72 Button(FilterConfigButton),
74}
75
76#[derive(Debug, Clone, PartialEq)]
77pub(crate) enum FilterConfigItemValue {
78 Track(f64),
79 Checkbox(bool),
80 Color(FilterConfigColorValue),
81 Select(i32),
82 File(String),
83 String(String),
84 Text(String),
85 Folder(String),
86 Data {
87 value: *mut std::ffi::c_void,
88 size: usize,
89 },
90 Group,
91 Button,
92}
93
94impl FilterConfigItem {
95 pub fn name(&self) -> &str {
101 match self {
102 FilterConfigItem::Track(item) => &item.name,
103 FilterConfigItem::Checkbox(item) => &item.name,
104 FilterConfigItem::Color(item) => &item.name,
105 FilterConfigItem::Select(item) => &item.name,
106 FilterConfigItem::File(item) => &item.name,
107 FilterConfigItem::String(item) => &item.name,
108 FilterConfigItem::Text(item) => &item.name,
109 FilterConfigItem::Folder(item) => &item.name,
110 FilterConfigItem::Data(item) => &item.name,
111 FilterConfigItem::Group(item) => item.name.as_deref().unwrap_or(""),
112 FilterConfigItem::Separator(item) => &item.name,
113 FilterConfigItem::Button(item) => &item.name,
114 }
115 }
116
117 pub(crate) fn to_raw(&self, leak_manager: &LeakManager) -> aviutl2_sys::filter2::FILTER_ITEM {
118 match self {
119 FilterConfigItem::Track(item) => aviutl2_sys::filter2::FILTER_ITEM {
120 track: aviutl2_sys::filter2::FILTER_ITEM_TRACK {
121 r#type: leak_manager.leak_as_wide_string("track"),
122 name: leak_manager.leak_as_wide_string(&item.name),
123 value: item.value,
124 s: *item.range.start(),
125 e: *item.range.end(),
126 step: item.step,
127 },
128 },
129 FilterConfigItem::Checkbox(item) => aviutl2_sys::filter2::FILTER_ITEM {
130 checkbox: aviutl2_sys::filter2::FILTER_ITEM_CHECKBOX {
131 r#type: leak_manager.leak_as_wide_string("check"),
132 name: leak_manager.leak_as_wide_string(&item.name),
133 value: item.value,
134 },
135 },
136 FilterConfigItem::Color(item) => aviutl2_sys::filter2::FILTER_ITEM {
137 color: aviutl2_sys::filter2::FILTER_ITEM_COLOR {
138 r#type: leak_manager.leak_as_wide_string("color"),
139 name: leak_manager.leak_as_wide_string(&item.name),
140 value: item.value.into(),
141 },
142 },
143 FilterConfigItem::Select(item) => {
144 let mut raw_items: Vec<aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM> = item
145 .items
146 .iter()
147 .map(|i| aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM {
148 name: leak_manager.leak_as_wide_string(&i.name),
149 value: i.value,
150 })
151 .collect();
152 raw_items.push(aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM {
153 name: std::ptr::null(),
154 value: 0,
155 }); let raw_items_ptrs = leak_manager.leak_value_vec(raw_items);
157 aviutl2_sys::filter2::FILTER_ITEM {
158 select: aviutl2_sys::filter2::FILTER_ITEM_SELECT {
159 r#type: leak_manager.leak_as_wide_string("select"),
160 name: leak_manager.leak_as_wide_string(&item.name),
161 value: item.value,
162 items: raw_items_ptrs,
163 },
164 }
165 }
166 FilterConfigItem::File(item) => {
167 let raw_filters = crate::common::format_file_filters(&item.filters);
168 aviutl2_sys::filter2::FILTER_ITEM {
169 file: aviutl2_sys::filter2::FILTER_ITEM_FILE {
170 r#type: leak_manager.leak_as_wide_string("file"),
171 name: leak_manager.leak_as_wide_string(&item.name),
172 value: leak_manager.leak_as_wide_string(&item.value),
173 filefilter: leak_manager.leak_as_wide_string(&raw_filters),
174 },
175 }
176 }
177 FilterConfigItem::String(item) => aviutl2_sys::filter2::FILTER_ITEM {
178 string: aviutl2_sys::filter2::FILTER_ITEM_STRING {
179 r#type: leak_manager.leak_as_wide_string("string"),
180 name: leak_manager.leak_as_wide_string(&item.name),
181 value: leak_manager.leak_as_wide_string(&item.value),
182 },
183 },
184 FilterConfigItem::Text(item) => aviutl2_sys::filter2::FILTER_ITEM {
185 text: aviutl2_sys::filter2::FILTER_ITEM_TEXT {
186 r#type: leak_manager.leak_as_wide_string("text"),
187 name: leak_manager.leak_as_wide_string(&item.name),
188 value: leak_manager.leak_as_wide_string(&item.value),
189 },
190 },
191 FilterConfigItem::Folder(item) => aviutl2_sys::filter2::FILTER_ITEM {
192 folder: aviutl2_sys::filter2::FILTER_ITEM_FOLDER {
193 r#type: leak_manager.leak_as_wide_string("folder"),
194 name: leak_manager.leak_as_wide_string(&item.name),
195 value: leak_manager.leak_as_wide_string(&item.value),
196 },
197 },
198 FilterConfigItem::Data(item) => {
199 let mut data = aviutl2_sys::filter2::FILTER_ITEM_DATA {
200 r#type: leak_manager.leak_as_wide_string("data"),
201 name: leak_manager.leak_as_wide_string(&item.name),
202 value: std::ptr::null_mut(),
203 size: item.size as i32,
204 default_value: [MaybeUninit::new(0); 1024],
205 };
206 assert!(item.size <= 1024, "FilterConfigData size must be <= 1024");
207 unsafe {
208 std::ptr::copy_nonoverlapping(
212 item.default_value().as_ptr(),
213 data.default_value.as_mut_ptr() as *mut u8,
214 item.size,
215 );
216 }
217
218 aviutl2_sys::filter2::FILTER_ITEM { data }
219 }
220 FilterConfigItem::Group(item) => aviutl2_sys::filter2::FILTER_ITEM {
221 group: aviutl2_sys::filter2::FILTER_ITEM_GROUP {
222 r#type: leak_manager.leak_as_wide_string("group"),
223 name: leak_manager.leak_as_wide_string(item.name.as_deref().unwrap_or("")),
224 default_visible: item.opened,
225 },
226 },
227 FilterConfigItem::Separator(item) => aviutl2_sys::filter2::FILTER_ITEM {
228 separator: aviutl2_sys::filter2::FILTER_ITEM_SEPARATOR {
229 r#type: leak_manager.leak_as_wide_string("separator"),
230 name: leak_manager.leak_as_wide_string(&item.name),
231 },
232 },
233 FilterConfigItem::Button(item) => aviutl2_sys::filter2::FILTER_ITEM {
234 button: aviutl2_sys::filter2::FILTER_ITEM_BUTTON {
235 r#type: leak_manager.leak_as_wide_string("button"),
236 name: leak_manager.leak_as_wide_string(&item.name),
237 callback: item.callback,
238 },
239 },
240 }
241 }
242
243 pub(crate) unsafe fn get_value(
247 raw: *const aviutl2_sys::filter2::FILTER_ITEM,
248 ) -> FilterConfigItemValue {
249 let item_type = unsafe {
250 crate::common::load_wide_string(
251 *(raw.cast::<aviutl2_sys::common::LPCWSTR>()),
253 )
254 };
255 match item_type.as_str() {
256 "track" => {
257 let raw_track = unsafe { &(*raw).track };
258 FilterConfigItemValue::Track(raw_track.value)
259 }
260 "check" => {
261 let raw_checkbox = unsafe { &(*raw).checkbox };
262 FilterConfigItemValue::Checkbox(raw_checkbox.value)
263 }
264 "color" => {
265 let raw_color = unsafe { &(*raw).color };
266 FilterConfigItemValue::Color(raw_color.value.into())
267 }
268 "select" => {
269 let raw_select = unsafe { &(*raw).select };
270 FilterConfigItemValue::Select(raw_select.value)
271 }
272 "file" => {
273 let raw_file = unsafe { &(*raw).file };
274 let value = unsafe { crate::common::load_wide_string(raw_file.value) };
275 FilterConfigItemValue::File(value)
276 }
277 "string" => {
278 let raw_string = unsafe { &(*raw).string };
279 let value = unsafe { crate::common::load_wide_string(raw_string.value) };
280 FilterConfigItemValue::String(value)
281 }
282 "text" => {
283 let raw_text = unsafe { &(*raw).text };
284 let value = unsafe { crate::common::load_wide_string(raw_text.value) };
285 FilterConfigItemValue::Text(value)
286 }
287 "folder" => {
288 let raw_folder = unsafe { &(*raw).folder };
289 let value = unsafe { crate::common::load_wide_string(raw_folder.value) };
290 FilterConfigItemValue::Folder(value)
291 }
292 "data" => {
293 let raw_size = unsafe { (*raw).data.size };
296 let raw_data = unsafe { (*raw).data.value };
297 let size =
298 usize::try_from(raw_size).expect("FILTER_ITEM_DATA size must not be negative");
299 assert!(
300 size <= 1024,
301 "FILTER_ITEM_DATA size must be 1024 bytes or less"
302 );
303 FilterConfigItemValue::Data {
304 value: raw_data,
305 size,
306 }
307 }
308 "group" => FilterConfigItemValue::Group,
309 "button" => FilterConfigItemValue::Button,
310 _ => panic!("Unknown filter config item type: {}", item_type),
311 }
312 }
313
314 pub(crate) unsafe fn should_apply_from_raw(
318 &self,
319 raw: *const aviutl2_sys::filter2::FILTER_ITEM,
320 ) -> bool {
321 let value = unsafe { Self::get_value(raw) };
322 match (self, value) {
323 (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => item.value != v,
324 (FilterConfigItem::Checkbox(item), FilterConfigItemValue::Checkbox(v)) => {
325 item.value != v
326 }
327 (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => item.value != v,
328 (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => item.value != v,
329 (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => item.value != v,
330 (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => item.value != v,
331 (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => item.value != v,
332 (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => item.value != v,
333 (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
334 let size_changed = item.size != size;
335 let ptr_changed = match (item.value, NonNull::new(value)) {
336 (Some(old), Some(new)) => old != new,
337 (None, None) => false,
338 _ => true,
339 };
340
341 size_changed || ptr_changed
342 }
343 (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => false,
344 (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => false,
345 _ => {
346 panic!("Mismatched filter config item type");
347 }
348 }
349 }
350
351 pub(crate) unsafe fn apply_from_raw(&mut self, raw: *const aviutl2_sys::filter2::FILTER_ITEM) {
355 let value = unsafe { Self::get_value(raw) };
356 match (self, value) {
357 (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => {
358 item.value = v;
359 }
360 (FilterConfigItem::Checkbox(item), FilterConfigItemValue::Checkbox(v)) => {
361 item.value = v;
362 }
363 (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => {
364 item.value = v;
365 }
366 (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => {
367 item.value = v;
368 }
369 (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => {
370 item.value = v;
371 }
372 (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => {
373 item.value = v;
374 }
375 (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => {
376 item.value = v;
377 }
378 (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => {
379 item.value = v;
380 }
381 (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
382 item.size = size;
383 item.value = NonNull::new(value);
384 }
385 (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => {
386 }
388 (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => {
389 }
391 _ => {
392 panic!("Mismatched filter config item type");
393 }
394 }
395 }
396}
397
398#[derive(Debug, Clone)]
400pub struct FilterConfigTrack {
401 pub name: String,
403
404 pub value: f64,
406
407 pub range: std::ops::RangeInclusive<f64>,
409
410 pub step: f64,
412}
413
414#[derive(Debug, Clone)]
416pub struct FilterConfigCheckbox {
417 pub name: String,
419
420 pub value: bool,
422}
423
424#[derive(Debug, Clone)]
426pub struct FilterConfigColor {
427 pub name: String,
429 pub value: FilterConfigColorValue,
431}
432
433#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
435pub struct FilterConfigColorValue(pub u32);
436impl FilterConfigColorValue {
437 pub fn to_rgb(&self) -> (u8, u8, u8) {
439 let r = ((self.0 >> 16) & 0xFF) as u8;
440 let g = ((self.0 >> 8) & 0xFF) as u8;
441 let b = (self.0 & 0xFF) as u8;
442 (r, g, b)
443 }
444
445 pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
447 let value = (r as u32) << 16 | (g as u32) << 8 | (b as u32);
448 FilterConfigColorValue(value)
449 }
450}
451impl From<u32> for FilterConfigColorValue {
452 fn from(value: u32) -> Self {
453 FilterConfigColorValue(value)
454 }
455}
456impl From<FilterConfigColorValue> for u32 {
457 fn from(value: FilterConfigColorValue) -> Self {
458 value.0
459 }
460}
461impl From<aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE> for FilterConfigColorValue {
462 fn from(value: aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE) -> Self {
463 unsafe { FilterConfigColorValue(value.code) }
464 }
465}
466impl From<FilterConfigColorValue> for aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE {
467 fn from(value: FilterConfigColorValue) -> Self {
468 aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE { code: value.0 }
469 }
470}
471impl std::fmt::Display for FilterConfigColorValue {
472 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
473 let (r, g, b) = self.to_rgb();
474 write!(f, "#{:02X}{:02X}{:02X}", r, g, b)
475 }
476}
477impl std::fmt::LowerHex for FilterConfigColorValue {
478 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
479 write!(f, "{:06x}", self.0 & 0xFFFFFF)
480 }
481}
482impl std::fmt::UpperHex for FilterConfigColorValue {
483 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
484 write!(f, "{:06X}", self.0 & 0xFFFFFF)
485 }
486}
487
488#[derive(Debug, Clone)]
490pub struct FilterConfigSelect {
491 pub name: String,
493 pub value: i32,
495 pub items: Vec<FilterConfigSelectItem>,
497}
498
499#[derive(Debug, Clone)]
501pub struct FilterConfigSelectItem {
502 pub name: String,
504 pub value: i32,
506}
507
508pub trait FilterConfigSelectItems {
524 fn to_select_items() -> Vec<crate::filter::FilterConfigSelectItem>;
526
527 fn from_select_item_value(item: i32) -> Self;
533
534 fn to_select_item_value(&self) -> i32;
536}
537
538#[doc(inline)]
539pub use aviutl2_macros::FilterConfigSelectItems;
540
541#[derive(Debug, Clone)]
543pub struct FilterConfigFile {
544 pub name: String,
546 pub value: String,
548 pub filters: Vec<crate::common::FileFilter>,
550}
551
552#[derive(Debug, Clone)]
554pub struct FilterConfigButton {
555 pub name: String,
557 pub callback: extern "C" fn(*mut EDIT_SECTION),
559}
560
561#[derive(Debug, Clone)]
563pub struct FilterConfigString {
564 pub name: String,
566 pub value: String,
568}
569
570#[derive(Debug, Clone)]
572pub struct FilterConfigText {
573 pub name: String,
575 pub value: String,
577}
578
579#[derive(Debug, Clone)]
581pub struct FilterConfigFolder {
582 pub name: String,
584 pub value: String,
586}
587
588#[derive(Debug, Clone)]
595pub struct ErasedFilterConfigData {
596 pub name: String,
598 pub size: usize,
604 pub value: Option<NonNull<std::ffi::c_void>>,
606 default_value: [u8; 1024],
607}
608
609impl ErasedFilterConfigData {
610 pub fn new<T: Copy + Default + 'static>(name: String) -> Self {
617 Self::with_default_value(name, T::default())
618 }
619
620 pub fn with_default_value<T: Copy + 'static>(name: String, default_value: T) -> Self {
627 assert!(
628 std::mem::size_of::<T>() <= 1024,
629 "FilterConfigData<T> size must be <= 1024 bytes"
630 );
631 let size = std::mem::size_of::<T>();
632 let mut default_value_bytes = [0u8; 1024];
633 let default_value_ptr = (&raw const default_value).cast::<u8>();
634 default_value_bytes[..size]
635 .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
636
637 ErasedFilterConfigData {
638 name,
639 size,
640 value: None,
641 default_value: default_value_bytes,
642 }
643 }
644
645 pub fn default_value(&self) -> &[u8] {
647 &self.default_value[..self.size]
648 }
649
650 pub unsafe fn into_typed<T: Copy + 'static>(self) -> FilterConfigData<T> {
659 let expected_size = std::mem::size_of::<T>();
660 assert_eq!(
661 self.size, expected_size,
662 "Size mismatch when converting ErasedFilterConfigData to FilterConfigData<T>"
663 );
664 let value = self
665 .value
666 .map(|v| NonNull::new(v.as_ptr().cast::<T>()).unwrap());
667 let default_value_ptr = self.default_value.as_ptr().cast::<T>();
668 let default_value = unsafe { *default_value_ptr };
669 FilterConfigData {
670 name: self.name,
671 value,
672 default_value,
673 }
674 }
675}
676
677#[derive(Debug, Clone)]
683pub struct FilterConfigData<T: Copy + 'static> {
684 pub name: String,
686 pub value: Option<NonNull<T>>,
688 pub default_value: T,
690}
691
692impl<T: Copy + 'static> FilterConfigData<T> {
693 pub fn erase_type(&self) -> ErasedFilterConfigData {
699 assert!(
700 std::mem::size_of::<T>() <= 1024,
701 "FilterConfigData<T> size must be <= 1024 bytes"
702 );
703 let size = std::mem::size_of::<T>();
704 let mut default_value = [0u8; 1024];
705 let default_value_ptr = (&raw const self.default_value).cast::<u8>();
706 default_value[..size]
707 .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
708
709 ErasedFilterConfigData {
710 name: self.name.clone(),
711 size,
712 value: self
713 .value
714 .map(|v| NonNull::new(v.as_ptr().cast::<c_void>()).unwrap()),
715 default_value,
716 }
717 }
718}
719
720impl<T: Copy + 'static> From<FilterConfigData<T>> for ErasedFilterConfigData {
721 fn from(value: FilterConfigData<T>) -> Self {
722 value.erase_type()
723 }
724}
725
726#[derive(Debug, Clone)]
728pub struct FilterConfigGroup {
729 pub name: Option<String>,
732
733 pub opened: bool,
735}
736
737impl FilterConfigGroup {
738 pub fn start(name: String) -> Self {
740 Self::start_with_opened(name, true)
741 }
742
743 pub fn start_with_opened(name: String, opened: bool) -> Self {
745 FilterConfigGroup {
746 name: Some(name),
747 opened,
748 }
749 }
750
751 pub fn end() -> Self {
753 FilterConfigGroup {
754 name: None,
755 opened: false,
756 }
757 }
758}
759
760#[derive(Debug, Clone)]
762pub struct FilterConfigSeparator {
763 pub name: String,
765}