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<FilterConfigItem>;
23
24 fn from_config_items(items: &[FilterConfigItem]) -> Self;
30}
31#[doc(inline)]
32pub use aviutl2_macros::filter_config_items;
33
34pub trait FilterConfigItemSliceExt {
36 fn to_struct<T: 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 Button(FilterConfigButton),
72}
73
74#[derive(Debug, Clone, PartialEq)]
75pub(crate) enum FilterConfigItemValue {
76 Track(f64),
77 Checkbox(bool),
78 Color(FilterConfigColorValue),
79 Select(i32),
80 File(String),
81 String(String),
82 Text(String),
83 Folder(String),
84 Data {
85 value: *mut std::ffi::c_void,
86 size: usize,
87 },
88 Group,
89 Button,
90}
91
92impl FilterConfigItem {
93 pub fn name(&self) -> &str {
99 match self {
100 FilterConfigItem::Track(item) => &item.name,
101 FilterConfigItem::Checkbox(item) => &item.name,
102 FilterConfigItem::Color(item) => &item.name,
103 FilterConfigItem::Select(item) => &item.name,
104 FilterConfigItem::File(item) => &item.name,
105 FilterConfigItem::String(item) => &item.name,
106 FilterConfigItem::Text(item) => &item.name,
107 FilterConfigItem::Folder(item) => &item.name,
108 FilterConfigItem::Data(item) => &item.name,
109 FilterConfigItem::Group(item) => item.name.as_deref().unwrap_or(""),
110 FilterConfigItem::Button(item) => &item.name,
111 }
112 }
113
114 pub(crate) fn to_raw(&self, leak_manager: &LeakManager) -> aviutl2_sys::filter2::FILTER_ITEM {
115 match self {
116 FilterConfigItem::Track(item) => {
117 let step: f64 = item.step.into();
118 aviutl2_sys::filter2::FILTER_ITEM {
119 track: aviutl2_sys::filter2::FILTER_ITEM_TRACK {
120 r#type: leak_manager.leak_as_wide_string("track"),
121 name: leak_manager.leak_as_wide_string(&item.name),
122 value: item.value,
123 s: *item.range.start(),
124 e: *item.range.end(),
125 step,
126 },
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::Button(item) => aviutl2_sys::filter2::FILTER_ITEM {
228 button: aviutl2_sys::filter2::FILTER_ITEM_BUTTON {
229 r#type: leak_manager.leak_as_wide_string("button"),
230 name: leak_manager.leak_as_wide_string(&item.name),
231 callback: item.callback,
232 },
233 },
234 }
235 }
236
237 pub(crate) unsafe fn get_value(
241 raw: *const aviutl2_sys::filter2::FILTER_ITEM,
242 ) -> FilterConfigItemValue {
243 let item_type = unsafe {
244 crate::common::load_wide_string(
245 *(raw.cast::<aviutl2_sys::common::LPCWSTR>()),
247 )
248 };
249 match item_type.as_str() {
250 "track" => {
251 let raw_track = unsafe { &(*raw).track };
252 FilterConfigItemValue::Track(raw_track.value)
253 }
254 "check" => {
255 let raw_checkbox = unsafe { &(*raw).checkbox };
256 FilterConfigItemValue::Checkbox(raw_checkbox.value)
257 }
258 "color" => {
259 let raw_color = unsafe { &(*raw).color };
260 FilterConfigItemValue::Color(raw_color.value.into())
261 }
262 "select" => {
263 let raw_select = unsafe { &(*raw).select };
264 FilterConfigItemValue::Select(raw_select.value)
265 }
266 "file" => {
267 let raw_file = unsafe { &(*raw).file };
268 let value = unsafe { crate::common::load_wide_string(raw_file.value) };
269 FilterConfigItemValue::File(value)
270 }
271 "string" => {
272 let raw_string = unsafe { &(*raw).string };
273 let value = unsafe { crate::common::load_wide_string(raw_string.value) };
274 FilterConfigItemValue::String(value)
275 }
276 "text" => {
277 let raw_text = unsafe { &(*raw).text };
278 let value = unsafe { crate::common::load_wide_string(raw_text.value) };
279 FilterConfigItemValue::Text(value)
280 }
281 "folder" => {
282 let raw_folder = unsafe { &(*raw).folder };
283 let value = unsafe { crate::common::load_wide_string(raw_folder.value) };
284 FilterConfigItemValue::Folder(value)
285 }
286 "data" => {
287 let raw_size = unsafe { (*raw).data.size };
290 let raw_data = unsafe { (*raw).data.value };
291 let size =
292 usize::try_from(raw_size).expect("FILTER_ITEM_DATA size must not be negative");
293 assert!(
294 size <= 1024,
295 "FILTER_ITEM_DATA size must be 1024 bytes or less"
296 );
297 FilterConfigItemValue::Data {
298 value: raw_data,
299 size,
300 }
301 }
302 "group" => FilterConfigItemValue::Group,
303 "button" => FilterConfigItemValue::Button,
304 _ => panic!("Unknown filter config item type: {}", item_type),
305 }
306 }
307
308 pub(crate) unsafe fn should_apply_from_raw(
312 &self,
313 raw: *const aviutl2_sys::filter2::FILTER_ITEM,
314 ) -> bool {
315 let value = unsafe { Self::get_value(raw) };
316 match (self, value) {
317 (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => item.value != v,
318 (FilterConfigItem::Checkbox(item), FilterConfigItemValue::Checkbox(v)) => {
319 item.value != v
320 }
321 (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => item.value != v,
322 (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => item.value != v,
323 (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => item.value != v,
324 (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => item.value != v,
325 (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => item.value != v,
326 (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => item.value != v,
327 (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
328 let size_changed = item.size != size;
329 let ptr_changed = match (item.value, NonNull::new(value)) {
330 (Some(old), Some(new)) => old != new,
331 (None, None) => false,
332 _ => true,
333 };
334
335 size_changed || ptr_changed
336 }
337 (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => false,
338 (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => false,
339 _ => {
340 panic!("Mismatched filter config item type");
341 }
342 }
343 }
344
345 pub(crate) unsafe fn apply_from_raw(&mut self, raw: *const aviutl2_sys::filter2::FILTER_ITEM) {
349 let value = unsafe { Self::get_value(raw) };
350 match (self, value) {
351 (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => {
352 item.value = v;
353 }
354 (FilterConfigItem::Checkbox(item), FilterConfigItemValue::Checkbox(v)) => {
355 item.value = v;
356 }
357 (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => {
358 item.value = v;
359 }
360 (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => {
361 item.value = v;
362 }
363 (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => {
364 item.value = v;
365 }
366 (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => {
367 item.value = v;
368 }
369 (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => {
370 item.value = v;
371 }
372 (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => {
373 item.value = v;
374 }
375 (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
376 item.size = size;
377 item.value = NonNull::new(value);
378 }
379 (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => {
380 }
382 (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => {
383 }
385 _ => {
386 panic!("Mismatched filter config item type");
387 }
388 }
389 }
390}
391
392#[derive(Debug, Clone)]
394pub struct FilterConfigTrack {
395 pub name: String,
397
398 pub value: f64,
400
401 pub range: std::ops::RangeInclusive<f64>,
403
404 pub step: FilterConfigTrackStep,
406}
407
408#[derive(Clone, Copy)]
410pub enum FilterConfigTrackStep {
411 One,
413 PointOne,
415 PointZeroOne,
417 PointZeroZeroOne,
419}
420impl TryFrom<f64> for FilterConfigTrackStep {
421 type Error = anyhow::Error;
422 fn try_from(value: f64) -> Result<Self, Self::Error> {
423 match value {
424 1.0 => Ok(FilterConfigTrackStep::One),
425 0.1 => Ok(FilterConfigTrackStep::PointOne),
426 0.01 => Ok(FilterConfigTrackStep::PointZeroOne),
427 0.001 => Ok(FilterConfigTrackStep::PointZeroZeroOne),
428 _ => Err(anyhow::anyhow!("Invalid step value: {}", value)),
429 }
430 }
431}
432impl From<FilterConfigTrackStep> for f64 {
433 fn from(value: FilterConfigTrackStep) -> Self {
434 match value {
435 FilterConfigTrackStep::One => 1.0,
436 FilterConfigTrackStep::PointOne => 0.1,
437 FilterConfigTrackStep::PointZeroOne => 0.01,
438 FilterConfigTrackStep::PointZeroZeroOne => 0.001,
439 }
440 }
441}
442impl std::fmt::Debug for FilterConfigTrackStep {
443 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444 let value: f64 = (*self).into();
445 f.debug_tuple("FilterConfigTrackStep")
446 .field(&value)
447 .finish()
448 }
449}
450
451#[derive(Debug, Clone)]
453pub struct FilterConfigCheckbox {
454 pub name: String,
456
457 pub value: bool,
459}
460
461#[derive(Debug, Clone)]
463pub struct FilterConfigColor {
464 pub name: String,
466 pub value: FilterConfigColorValue,
468}
469
470#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
472pub struct FilterConfigColorValue(pub u32);
473impl FilterConfigColorValue {
474 pub fn to_rgb(&self) -> (u8, u8, u8) {
476 let r = ((self.0 >> 16) & 0xFF) as u8;
477 let g = ((self.0 >> 8) & 0xFF) as u8;
478 let b = (self.0 & 0xFF) as u8;
479 (r, g, b)
480 }
481
482 pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
484 let value = (r as u32) << 16 | (g as u32) << 8 | (b as u32);
485 FilterConfigColorValue(value)
486 }
487}
488impl From<u32> for FilterConfigColorValue {
489 fn from(value: u32) -> Self {
490 FilterConfigColorValue(value)
491 }
492}
493impl From<FilterConfigColorValue> for u32 {
494 fn from(value: FilterConfigColorValue) -> Self {
495 value.0
496 }
497}
498impl From<aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE> for FilterConfigColorValue {
499 fn from(value: aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE) -> Self {
500 unsafe { FilterConfigColorValue(value.code) }
501 }
502}
503impl From<FilterConfigColorValue> for aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE {
504 fn from(value: FilterConfigColorValue) -> Self {
505 aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE { code: value.0 }
506 }
507}
508impl std::fmt::Display for FilterConfigColorValue {
509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510 let (r, g, b) = self.to_rgb();
511 write!(f, "#{:02X}{:02X}{:02X}", r, g, b)
512 }
513}
514impl std::fmt::LowerHex for FilterConfigColorValue {
515 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516 write!(f, "{:06x}", self.0 & 0xFFFFFF)
517 }
518}
519impl std::fmt::UpperHex for FilterConfigColorValue {
520 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
521 write!(f, "{:06X}", self.0 & 0xFFFFFF)
522 }
523}
524
525#[derive(Debug, Clone)]
527pub struct FilterConfigSelect {
528 pub name: String,
530 pub value: i32,
532 pub items: Vec<FilterConfigSelectItem>,
534}
535
536#[derive(Debug, Clone)]
538pub struct FilterConfigSelectItem {
539 pub name: String,
541 pub value: i32,
543}
544
545pub trait FilterConfigSelectItems {
561 fn to_select_items() -> Vec<FilterConfigSelectItem>;
563
564 fn from_select_item_value(item: i32) -> Self;
570
571 fn to_select_item_value(&self) -> i32;
573}
574
575#[doc(inline)]
576pub use aviutl2_macros::FilterConfigSelectItems;
577
578#[derive(Debug, Clone)]
580pub struct FilterConfigFile {
581 pub name: String,
583 pub value: String,
585 pub filters: Vec<crate::common::FileFilter>,
587}
588
589#[derive(Debug, Clone)]
591pub struct FilterConfigButton {
592 pub name: String,
594 pub callback: extern "C" fn(*mut EDIT_SECTION),
596}
597
598#[derive(Debug, Clone)]
600pub struct FilterConfigString {
601 pub name: String,
603 pub value: String,
605}
606
607#[derive(Debug, Clone)]
609pub struct FilterConfigText {
610 pub name: String,
612 pub value: String,
614}
615
616#[derive(Debug, Clone)]
618pub struct FilterConfigFolder {
619 pub name: String,
621 pub value: String,
623}
624
625#[derive(Debug, Clone)]
632pub struct ErasedFilterConfigData {
633 pub name: String,
635 pub size: usize,
641 pub value: Option<NonNull<std::ffi::c_void>>,
643 default_value: [u8; 1024],
644}
645
646impl ErasedFilterConfigData {
647 pub fn new<T: Copy + Default + 'static>(name: String) -> Self {
654 Self::with_default_value(name, T::default())
655 }
656
657 pub fn with_default_value<T: Copy + 'static>(name: String, default_value: T) -> Self {
664 assert!(
665 std::mem::size_of::<T>() <= 1024,
666 "FilterConfigData<T> size must be <= 1024 bytes"
667 );
668 let size = std::mem::size_of::<T>();
669 let mut default_value_bytes = [0u8; 1024];
670 let default_value_ptr = (&raw const default_value).cast::<u8>();
671 default_value_bytes[..size]
672 .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
673
674 ErasedFilterConfigData {
675 name,
676 size,
677 value: None,
678 default_value: default_value_bytes,
679 }
680 }
681
682 pub fn default_value(&self) -> &[u8] {
684 &self.default_value[..self.size]
685 }
686
687 pub unsafe fn into_typed<T: Copy + 'static>(self) -> FilterConfigData<T> {
696 let expected_size = std::mem::size_of::<T>();
697 assert_eq!(
698 self.size, expected_size,
699 "Size mismatch when converting ErasedFilterConfigData to FilterConfigData<T>"
700 );
701 let value = self
702 .value
703 .map(|v| NonNull::new(v.as_ptr().cast::<T>()).unwrap());
704 let default_value_ptr = self.default_value.as_ptr().cast::<T>();
705 let default_value = unsafe { *default_value_ptr };
706 FilterConfigData {
707 name: self.name,
708 value,
709 default_value,
710 }
711 }
712}
713
714#[derive(Debug, Clone)]
720pub struct FilterConfigData<T: Copy + 'static> {
721 pub name: String,
723 pub value: Option<NonNull<T>>,
725 pub default_value: T,
727}
728
729impl<T: Copy + 'static> FilterConfigData<T> {
730 pub fn erase_type(&self) -> ErasedFilterConfigData {
736 assert!(
737 std::mem::size_of::<T>() <= 1024,
738 "FilterConfigData<T> size must be <= 1024 bytes"
739 );
740 let size = std::mem::size_of::<T>();
741 let mut default_value = [0u8; 1024];
742 let default_value_ptr = (&raw const self.default_value).cast::<u8>();
743 default_value[..size]
744 .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
745
746 ErasedFilterConfigData {
747 name: self.name.clone(),
748 size,
749 value: self
750 .value
751 .map(|v| NonNull::new(v.as_ptr().cast::<c_void>()).unwrap()),
752 default_value,
753 }
754 }
755}
756
757impl<T: Copy + 'static> From<FilterConfigData<T>> for ErasedFilterConfigData {
758 fn from(value: FilterConfigData<T>) -> Self {
759 value.erase_type()
760 }
761}
762
763#[derive(Debug, Clone)]
765pub struct FilterConfigGroup {
766 pub name: Option<String>,
769
770 pub opened: bool,
772}
773
774impl FilterConfigGroup {
775 pub fn start(name: String) -> Self {
777 Self::start_with_opened(name, true)
778 }
779
780 pub fn start_with_opened(name: String, opened: bool) -> Self {
782 FilterConfigGroup {
783 name: Some(name),
784 opened,
785 }
786 }
787
788 pub fn end() -> Self {
790 FilterConfigGroup {
791 name: None,
792 opened: false,
793 }
794 }
795}