From 095b8971613682bb0f5a71d3a1d87b9f4247386b Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 6 Apr 2024 06:39:25 +0800 Subject: [PATCH 01/18] add error --- Cargo.lock | 9 +++--- Cargo.toml | 1 + plugins/rnnoise-denoiser-filter/src/lib.rs | 2 +- plugins/scroll-focus-filter/src/lib.rs | 4 +-- src/graphics/mod.rs | 4 +-- src/lib.rs | 15 ++------- src/result.rs | 27 ++++++++++++++++ src/source/ffi.rs | 4 +-- src/source/mod.rs | 36 ++++++++++++++-------- src/source/traits.rs | 4 +-- 10 files changed, 69 insertions(+), 37 deletions(-) create mode 100644 src/result.rs diff --git a/Cargo.lock b/Cargo.lock index 6e8486e..c77f3e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -438,6 +438,7 @@ dependencies = [ "num-traits", "obs-sys", "paste", + "thiserror", ] [[package]] @@ -633,18 +634,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 70149ee..54b736a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ obs-sys = { path = "./obs-sys", version = "0.3.0" } paste = "0.1.7" log = {version = "0.4.11", features = ["std"]} num-traits = "0.2.14" +thiserror = "1.0.58" diff --git a/plugins/rnnoise-denoiser-filter/src/lib.rs b/plugins/rnnoise-denoiser-filter/src/lib.rs index 27bf327..1e11c4d 100644 --- a/plugins/rnnoise-denoiser-filter/src/lib.rs +++ b/plugins/rnnoise-denoiser-filter/src/lib.rs @@ -39,7 +39,7 @@ impl Sourceable for RnnoiseDenoiserFilter { fn get_type() -> SourceType { SourceType::FILTER } - fn create(create: &mut CreatableSourceContext, _source: SourceContext) -> Self { + fn create(create: &mut CreatableSourceContext, _source: SourceRef) -> Self { let (sample_rate, channels) = create.with_audio(|audio| (audio.sample_rate(), audio.channels())); diff --git a/plugins/scroll-focus-filter/src/lib.rs b/plugins/scroll-focus-filter/src/lib.rs index 1744d05..159008b 100644 --- a/plugins/scroll-focus-filter/src/lib.rs +++ b/plugins/scroll-focus-filter/src/lib.rs @@ -17,7 +17,7 @@ enum ServerMessage { } struct ScrollFocusFilter { - source: SourceContext, + source: SourceRef, effect: GraphicsEffect, base_dimension: GraphicsEffectVec2Param, @@ -70,7 +70,7 @@ impl Sourceable for ScrollFocusFilter { fn get_type() -> SourceType { SourceType::FILTER } - fn create(create: &mut CreatableSourceContext, mut source: SourceContext) -> Self { + fn create(create: &mut CreatableSourceContext, mut source: SourceRef) -> Self { let mut effect = GraphicsEffect::from_effect_string( obs_string!(include_str!("./crop_filter.effect")), obs_string!("crop_filter.effect"), diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs index 7b6cfd8..13521e6 100644 --- a/src/graphics/mod.rs +++ b/src/graphics/mod.rs @@ -221,7 +221,7 @@ macro_rules! impl_graphics_effects { fn try_from(effect: GraphicsEffectParam) -> Result { match effect.shader_type { ShaderParamType::[<$t>] => Ok([] { effect }), - _ => Err(Error), + _ => Err(Error::EnumOutOfRange("ShaderParamType", effect.shader_type as _)), } } } @@ -700,7 +700,7 @@ impl<'tex> MappedTexture<'tex> { gs_texture_map(tex.as_ptr(), &mut ptr, &mut linesize) }); if !map_result { - return Err(Error); + return Err(Error::ObsError(-1)); } let len = (linesize * tex.height()) as usize; Ok(Self { tex, ptr, len }) diff --git a/src/lib.rs b/src/lib.rs index ec374a5..40d1b03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,6 +140,8 @@ pub mod output; pub mod properties; /// Tools for creating sources pub mod source; +/// Error handling +pub mod result; /// String macros pub mod string; /// FFI pointer wrapper @@ -155,15 +157,4 @@ pub mod prelude { pub use crate::string::*; } -#[derive(Debug)] -pub struct Error; - -impl std::error::Error for Error {} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "OBS Error") - } -} - -pub type Result = std::result::Result; +pub use result::{Error, Result}; diff --git a/src/result.rs b/src/result.rs new file mode 100644 index 0000000..b308d6f --- /dev/null +++ b/src/result.rs @@ -0,0 +1,27 @@ + +pub type Result = std::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Error from OBS API + #[error("OBS Error: {0}")] + ObsError(i32), + /// Null Pointer + #[error("Null Pointer Error: {0}")] + NulPointer(&'static str), + /// Error converting string + #[error("Utf8 Error: {0}")] + Utf8Error(#[from] std::str::Utf8Error), + /// Error converting enum + #[error("Enum Out of Range: {0} {1}")] + EnumOutOfRange(&'static str, i64), +} + +pub trait OptionExt { + fn null_pointer(self, msg: &'static str) -> Result<()>; +} +impl OptionExt for Option<()> { + fn null_pointer(self, msg: &'static str) -> Result<()> { + self.ok_or(Error::NulPointer(msg)) + } +} diff --git a/src/source/ffi.rs b/src/source/ffi.rs index b40df7e..830a3c4 100644 --- a/src/source/ffi.rs +++ b/src/source/ffi.rs @@ -1,5 +1,5 @@ use super::context::{CreatableSourceContext, GlobalContext, VideoRenderContext}; -use super::{traits::*, SourceContext}; +use super::{traits::*, SourceRef}; use super::{EnumActiveContext, EnumAllContext}; use crate::media::{audio::AudioDataContext, video::VideoDataSourceContext}; use crate::{ @@ -89,7 +89,7 @@ pub unsafe extern "C" fn create( let mut global = GlobalContext; let settings = DataObj::from_raw(settings); let mut context = CreatableSourceContext::from_raw(settings, &mut global); - let source_context = SourceContext::from_raw(source); + let source_context = SourceRef::from_raw(source).expect("create"); let data = D::create(&mut context, source_context); diff --git a/src/source/mod.rs b/src/source/mod.rs index 38a54e4..d1a8e5e 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -104,49 +104,61 @@ impl SourceType { } } +#[deprecated = "use `SourceRef` instead"] +pub type SourceContext = SourceRef; + /// Context wrapping an OBS source - video / audio elements which are displayed /// to the screen. /// /// See [OBS documentation](https://obsproject.com/docs/reference-sources.html#c.obs_source_t) -pub struct SourceContext { +pub struct SourceRef { inner: *mut obs_source_t, } -impl SourceContext { +impl SourceRef { /// # Safety /// /// Must call with a valid pointer. - pub unsafe fn from_raw(source: *mut obs_source_t) -> Self { - Self { - inner: obs_source_get_ref(source), + pub fn from_raw(source: *mut obs_source_t) -> Option { + unsafe { + Self::from_raw_unchecked(obs_source_get_ref(source)) + } + } + + pub unsafe fn from_raw_unchecked(source: *mut obs_source_t) -> Option { + if source.is_null() { + None + } else { + Some(Self { inner: source }) } } } -impl Clone for SourceContext { +impl Clone for SourceRef { fn clone(&self) -> Self { - unsafe { Self::from_raw(self.inner) } + Self::from_raw(self.inner).expect("clone") } } -impl Drop for SourceContext { +impl Drop for SourceRef { fn drop(&mut self) { unsafe { obs_source_release(self.inner) } } } -impl SourceContext { +impl SourceRef { /// Run a function on the next source in the filter chain. /// /// Note: only works with sources that are filters. - pub fn do_with_target(&mut self, func: F) { + pub fn do_with_target(&mut self, func: F) { unsafe { if let Some(SourceType::FILTER) = SourceType::from_native(obs_source_get_type(self.inner)) { let target = obs_filter_get_target(self.inner); - let mut context = SourceContext::from_raw(target); - func(&mut context); + if let Some(mut context) = SourceRef::from_raw(target) { + func(&mut context); + } } } } diff --git a/src/source/traits.rs b/src/source/traits.rs index ab83101..113ec12 100644 --- a/src/source/traits.rs +++ b/src/source/traits.rs @@ -1,7 +1,7 @@ use obs_sys::{obs_key_event, obs_mouse_event}; use super::context::{CreatableSourceContext, GlobalContext, VideoRenderContext}; -use super::{EnumActiveContext, EnumAllContext, SourceContext, SourceType}; +use super::{EnumActiveContext, EnumAllContext, SourceRef, SourceType}; use crate::data::DataObj; use crate::media::state::MediaState; use crate::media::{audio::AudioDataContext, video::VideoDataSourceContext}; @@ -11,7 +11,7 @@ use crate::string::ObsString; pub trait Sourceable: Sized { fn get_id() -> ObsString; fn get_type() -> SourceType; - fn create(create: &mut CreatableSourceContext, source: SourceContext) -> Self; + fn create(create: &mut CreatableSourceContext, source: SourceRef) -> Self; } macro_rules! simple_trait { From f93f67169f94b5f4ff46461c550336b2e00e35e9 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 6 Apr 2024 07:26:48 +0800 Subject: [PATCH 02/18] improve SourceRef --- src/lib.rs | 4 +-- src/result.rs | 4 ++- src/source/mod.rs | 52 +++++++++++++-------------- src/string.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 40d1b03..f8da9dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,10 +138,10 @@ pub mod module; pub mod output; /// Tools for creating properties pub mod properties; -/// Tools for creating sources -pub mod source; /// Error handling pub mod result; +/// Tools for creating sources +pub mod source; /// String macros pub mod string; /// FFI pointer wrapper diff --git a/src/result.rs b/src/result.rs index b308d6f..e2394c1 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,4 +1,3 @@ - pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] @@ -9,6 +8,9 @@ pub enum Error { /// Null Pointer #[error("Null Pointer Error: {0}")] NulPointer(&'static str), + /// Null Error + #[error("Null String Error: {0}")] + NulError(#[from] std::ffi::NulError), /// Error converting string #[error("Utf8 Error: {0}")] Utf8Error(#[from] std::str::Utf8Error), diff --git a/src/source/mod.rs b/src/source/mod.rs index d1a8e5e..055b6c8 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -4,7 +4,11 @@ pub mod context; mod ffi; pub mod traits; -use crate::media::state::MediaState; +use crate::{ + media::state::MediaState, + string::{DisplayExt as _, TryIntoObsString}, + Result, +}; pub use context::*; pub use traits::*; @@ -43,10 +47,7 @@ use super::{ }; use crate::{data::DataObj, native_enum, wrapper::PtrWrapper}; -use std::{ - ffi::{CStr, CString}, - marker::PhantomData, -}; +use std::{ffi::CString, marker::PhantomData}; native_enum!(MouseButton, obs_mouse_button_type { Left => MOUSE_LEFT, @@ -115,14 +116,27 @@ pub struct SourceRef { inner: *mut obs_source_t, } +impl std::fmt::Debug for SourceRef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SourceRef") + .field("id", &self.id()) + .field("name", &self.name().display()) + .field("source_id", &self.source_id().display()) + .field("width", &self.width()) + .field("height", &self.height()) + .field("showing", &self.showing()) + .field("active", &self.active()) + .field("enabled", &self.enabled()) + .finish() + } +} + impl SourceRef { /// # Safety /// /// Must call with a valid pointer. pub fn from_raw(source: *mut obs_source_t) -> Option { - unsafe { - Self::from_raw_unchecked(obs_source_get_ref(source)) - } + unsafe { Self::from_raw_unchecked(obs_source_get_ref(source)) } } pub unsafe fn from_raw_unchecked(source: *mut obs_source_t) -> Option { @@ -192,26 +206,12 @@ impl SourceRef { unsafe { obs_source_set_enabled(self.inner, enabled) } } - pub fn source_id(&self) -> Option<&str> { - unsafe { - let ptr = obs_source_get_id(self.inner); - if ptr.is_null() { - None - } else { - Some(CStr::from_ptr(ptr).to_str().unwrap()) - } - } + pub fn source_id(&self) -> Result { + unsafe { obs_source_get_id(self.inner) }.try_into_obs_string() } - pub fn name(&self) -> Option<&str> { - unsafe { - let ptr = obs_source_get_name(self.inner); - if ptr.is_null() { - None - } else { - Some(CStr::from_ptr(ptr).to_str().unwrap()) - } - } + pub fn name(&self) -> Result { + unsafe { obs_source_get_name(self.inner) }.try_into_obs_string() } pub fn set_name(&mut self, name: &str) { diff --git a/src/string.rs b/src/string.rs index 0b9dad2..7667c50 100644 --- a/src/string.rs +++ b/src/string.rs @@ -1,4 +1,9 @@ -use std::{ffi::CString, ptr::null}; +use std::{ + ffi::{CStr, CString}, + ptr::null, +}; + +use crate::Result; #[derive(Debug, Eq, PartialEq, Hash, Clone)] pub enum ObsString { @@ -45,3 +50,86 @@ macro_rules! obs_string { unsafe { $crate::string::ObsString::from_nul_terminted_str(concat!($e, "\0")) } }; } + +pub trait TryIntoObsString { + fn try_into_obs_string(self) -> Result; +} + +impl TryIntoObsString for &str { + fn try_into_obs_string(self) -> Result { + Ok(ObsString::Dynamic(CString::new(self)?)) + } +} +impl TryIntoObsString for String { + fn try_into_obs_string(self) -> Result { + Ok(ObsString::Dynamic(CString::new(self)?)) + } +} +impl TryIntoObsString for *const std::os::raw::c_char { + fn try_into_obs_string(self) -> Result { + if self.is_null() { + return Err(crate::Error::NulPointer("ObsString")); + } + Ok(ObsString::Dynamic( + unsafe { CStr::from_ptr(self) }.to_owned(), + )) + } +} + +pub struct DisplayStr<'a, T>(&'a T); +pub trait DisplayExt: Sized { + fn display(&self) -> DisplayStr<'_, Self> { + DisplayStr(self) + } +} +impl DisplayExt for ObsString {} +impl DisplayExt for Option {} +impl<'a> DisplayExt for Option<&'a ObsString> {} +impl DisplayExt for Result {} +impl std::fmt::Display for DisplayStr<'_, ObsString> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DisplayStr(ObsString::Static(s)) => return write!(f, "{}", &s[..s.len() - 1]), + DisplayStr(ObsString::Dynamic(s)) => return write!(f, "{}", s.to_string_lossy()), + } + } +} +impl std::fmt::Debug for DisplayStr<'_, ObsString> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.to_string()) + } +} + +impl<'a, T: DisplayExt> std::fmt::Display for DisplayStr<'a, Option> +where + DisplayStr<'a, T>: std::fmt::Display, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + Some(s) => write!(f, "{}", s.display()), + None => write!(f, ""), + } + } +} +impl<'a, T: DisplayExt> std::fmt::Debug for DisplayStr<'a, Option> +where + DisplayStr<'a, T>: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + Some(s) => write!(f, "{:?}", s.display()), + None => write!(f, "None"), + } + } +} +impl<'a, T: DisplayExt> std::fmt::Debug for DisplayStr<'a, Result> +where + DisplayStr<'a, T>: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + Ok(s) => write!(f, "{:?}", s.display()), + _ => write!(f, "Error"), + } + } +} From dd5ecb534c77e0017c6e0ba9f8aed9843653ab7a Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 6 Apr 2024 07:47:10 +0800 Subject: [PATCH 03/18] fix --- src/source/mod.rs | 17 +++++++++++++++++ src/string.rs | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/source/mod.rs b/src/source/mod.rs index 055b6c8..fc66629 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -131,6 +131,19 @@ impl std::fmt::Debug for SourceRef { } } +// impl PtrWrapper for SourceRef { +// type Pointer = obs_source_t; + +// fn as_ptr(&self) -> *const Self::Pointer { +// self.inner +// } + + +// unsafe fn from_raw(raw: *mut Self::Pointer) -> Self { +// todo!() +// } +// } + impl SourceRef { /// # Safety /// @@ -146,6 +159,10 @@ impl SourceRef { Some(Self { inner: source }) } } + + pub unsafe fn as_raw(&self) -> *mut obs_source_t { + self.inner + } } impl Clone for SourceRef { diff --git a/src/string.rs b/src/string.rs index 7667c50..0ee6528 100644 --- a/src/string.rs +++ b/src/string.rs @@ -85,7 +85,7 @@ pub trait DisplayExt: Sized { impl DisplayExt for ObsString {} impl DisplayExt for Option {} impl<'a> DisplayExt for Option<&'a ObsString> {} -impl DisplayExt for Result {} +impl DisplayExt for Result {} impl std::fmt::Display for DisplayStr<'_, ObsString> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -122,7 +122,7 @@ where } } } -impl<'a, T: DisplayExt> std::fmt::Debug for DisplayStr<'a, Result> +impl<'a, T: DisplayExt, E> std::fmt::Debug for DisplayStr<'a, Result> where DisplayStr<'a, T>: std::fmt::Debug, { From f5459b5c1794ac794c43af43f41e271e5c361139 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 6 Apr 2024 08:01:51 +0800 Subject: [PATCH 04/18] improve module --- plugins/rnnoise-denoiser-filter/src/lib.rs | 6 +-- plugins/scroll-focus-filter/src/lib.rs | 6 +-- src/module.rs | 55 ++++++++++++++++++---- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/plugins/rnnoise-denoiser-filter/src/lib.rs b/plugins/rnnoise-denoiser-filter/src/lib.rs index 1e11c4d..ea52772 100644 --- a/plugins/rnnoise-denoiser-filter/src/lib.rs +++ b/plugins/rnnoise-denoiser-filter/src/lib.rs @@ -29,7 +29,7 @@ struct RnnoiseDenoiserFilter { } struct TheModule { - context: ModuleContext, + context: ModuleRef, } impl Sourceable for RnnoiseDenoiserFilter { @@ -172,10 +172,10 @@ impl FilterAudioSource for RnnoiseDenoiserFilter { } impl Module for TheModule { - fn new(context: ModuleContext) -> Self { + fn new(context: ModuleRef) -> Self { Self { context } } - fn get_ctx(&self) -> &ModuleContext { + fn get_ctx(&self) -> &ModuleRef { &self.context } diff --git a/plugins/scroll-focus-filter/src/lib.rs b/plugins/scroll-focus-filter/src/lib.rs index 159008b..6132b49 100644 --- a/plugins/scroll-focus-filter/src/lib.rs +++ b/plugins/scroll-focus-filter/src/lib.rs @@ -60,7 +60,7 @@ impl Drop for ScrollFocusFilter { } struct TheModule { - context: ModuleContext, + context: ModuleRef, } impl Sourceable for ScrollFocusFilter { @@ -382,10 +382,10 @@ impl UpdateSource for ScrollFocusFilter { } impl Module for TheModule { - fn new(context: ModuleContext) -> Self { + fn new(context: ModuleRef) -> Self { Self { context } } - fn get_ctx(&self) -> &ModuleContext { + fn get_ctx(&self) -> &ModuleRef { &self.context } diff --git a/src/module.rs b/src/module.rs index 0aa3ac9..03d9311 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,9 +1,11 @@ use crate::output::{traits::Outputable, OutputInfo, OutputInfoBuilder}; use crate::source::{traits::Sourceable, SourceInfo, SourceInfoBuilder}; -use crate::string::ObsString; +use crate::string::{DisplayExt as _, ObsString, TryIntoObsString as _}; +use crate::{Error, Result}; use obs_sys::{ - obs_module_t, obs_output_info, obs_register_output_s, obs_register_source_s, obs_source_info, - size_t, + obs_get_module_author, obs_get_module_description, obs_get_module_file_name, + obs_get_module_name, obs_module_t, obs_output_info, obs_register_output_s, + obs_register_source_s, obs_source_info, size_t, }; use std::marker::PhantomData; @@ -65,8 +67,8 @@ impl Drop for LoadContext { } pub trait Module { - fn new(ctx: ModuleContext) -> Self; - fn get_ctx(&self) -> &ModuleContext; + fn new(ctx: ModuleRef) -> Self; + fn get_ctx(&self) -> &ModuleRef; fn load(&mut self, _load_context: &mut LoadContext) -> bool { true } @@ -86,7 +88,7 @@ macro_rules! obs_register_module { #[allow(missing_safety_doc)] #[no_mangle] pub unsafe extern "C" fn obs_module_set_pointer(raw: *mut $crate::obs_sys::obs_module_t) { - OBS_MODULE = Some(<$t>::new(ModuleContext::new(raw))); + OBS_MODULE = ModuleRef::from_raw(raw).ok().map(<$t>::new); } #[allow(missing_safety_doc)] @@ -150,16 +152,31 @@ macro_rules! obs_register_module { }; } -pub struct ModuleContext { +pub struct ModuleRef { raw: *mut obs_module_t, } -impl ModuleContext { +impl std::fmt::Debug for ModuleRef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ModuleRef") + .field("name", &self.name().display()) + .field("description", &self.description().display()) + .field("author", &self.author().display()) + .field("file_name", &self.file_name().display()) + .finish() + } +} + +impl ModuleRef { /// # Safety /// Creates a ModuleContext from a pointer to the raw obs_module data which /// if modified could cause UB. - pub unsafe fn new(raw: *mut obs_module_t) -> Self { - Self { raw } + pub fn from_raw(raw: *mut obs_module_t) -> Result { + if raw.is_null() { + Err(Error::NulPointer("obs_module_t")) + } else { + Ok(Self { raw }) + } } /// # Safety @@ -169,3 +186,21 @@ impl ModuleContext { self.raw } } + +impl ModuleRef { + pub fn name(&self) -> Result { + unsafe { obs_get_module_name(self.raw) }.try_into_obs_string() + } + + pub fn description(&self) -> Result { + unsafe { obs_get_module_description(self.raw) }.try_into_obs_string() + } + + pub fn author(&self) -> Result { + unsafe { obs_get_module_author(self.raw) }.try_into_obs_string() + } + + pub fn file_name(&self) -> Result { + unsafe { obs_get_module_file_name(self.raw) }.try_into_obs_string() + } +} From e3d66a7f118ee385ee956246055b148fc80fa4e8 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 6 Apr 2024 09:05:28 +0800 Subject: [PATCH 05/18] handle null_ptr --- src/data.rs | 151 ++++++++++++++++++++++++------------------ src/lib.rs | 5 +- src/output/context.rs | 2 +- src/output/ffi.rs | 6 +- src/properties.rs | 42 +++++++++--- src/source/ffi.rs | 6 +- src/source/mod.rs | 51 ++------------ src/wrapper.rs | 63 ++++++++++++++++-- 8 files changed, 191 insertions(+), 135 deletions(-) diff --git a/src/data.rs b/src/data.rs index c02bdbe..24ba0bf 100644 --- a/src/data.rs +++ b/src/data.rs @@ -6,21 +6,24 @@ use std::{ }; use obs_sys::{ - obs_data_array_count, obs_data_array_item, obs_data_array_release, obs_data_array_t, - obs_data_clear, obs_data_create, obs_data_create_from_json, obs_data_create_from_json_file, - obs_data_create_from_json_file_safe, obs_data_erase, obs_data_get_json, obs_data_item_byname, - obs_data_item_get_array, obs_data_item_get_bool, obs_data_item_get_double, - obs_data_item_get_int, obs_data_item_get_obj, obs_data_item_get_string, obs_data_item_gettype, - obs_data_item_numtype, obs_data_item_release, obs_data_item_t, obs_data_number_type, - obs_data_number_type_OBS_DATA_NUM_DOUBLE, obs_data_number_type_OBS_DATA_NUM_INT, - obs_data_release, obs_data_set_default_bool, obs_data_set_default_double, - obs_data_set_default_int, obs_data_set_default_obj, obs_data_set_default_string, obs_data_t, - obs_data_type, obs_data_type_OBS_DATA_ARRAY, obs_data_type_OBS_DATA_BOOLEAN, - obs_data_type_OBS_DATA_NUMBER, obs_data_type_OBS_DATA_OBJECT, obs_data_type_OBS_DATA_STRING, - size_t, + obs_data_addref, obs_data_array_addref, obs_data_array_count, obs_data_array_item, + obs_data_array_release, obs_data_array_t, obs_data_clear, obs_data_create, + obs_data_create_from_json, obs_data_create_from_json_file, obs_data_create_from_json_file_safe, + obs_data_erase, obs_data_get_json, obs_data_item_byname, obs_data_item_get_array, + obs_data_item_get_bool, obs_data_item_get_double, obs_data_item_get_int, obs_data_item_get_obj, + obs_data_item_get_string, obs_data_item_gettype, obs_data_item_numtype, obs_data_item_release, + obs_data_item_t, obs_data_number_type, obs_data_number_type_OBS_DATA_NUM_DOUBLE, + obs_data_number_type_OBS_DATA_NUM_INT, obs_data_release, obs_data_set_default_bool, + obs_data_set_default_double, obs_data_set_default_int, obs_data_set_default_obj, + obs_data_set_default_string, obs_data_t, obs_data_type, obs_data_type_OBS_DATA_ARRAY, + obs_data_type_OBS_DATA_BOOLEAN, obs_data_type_OBS_DATA_NUMBER, obs_data_type_OBS_DATA_OBJECT, + obs_data_type_OBS_DATA_STRING, size_t, }; -use crate::{string::ObsString, wrapper::PtrWrapper}; +use crate::{ + string::{ObsString, TryIntoObsString}, + wrapper::PtrWrapper, +}; #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum DataType { @@ -57,12 +60,12 @@ impl DataType { } } -pub trait FromDataItem { +pub trait FromDataItem: Sized { fn typ() -> DataType; /// # Safety /// /// Pointer must be valid. - unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self; + unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Option; /// # Safety /// @@ -74,9 +77,12 @@ impl FromDataItem for Cow<'_, str> { fn typ() -> DataType { DataType::String } - unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self { + unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Option { let ptr = obs_data_item_get_string(item); - CStr::from_ptr(ptr).to_string_lossy() + if ptr.is_null() { + return None; + } + Some(CStr::from_ptr(ptr).to_string_lossy()) } unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) { let s = CString::new(val.as_ref()).unwrap(); @@ -88,9 +94,9 @@ impl FromDataItem for ObsString { fn typ() -> DataType { DataType::String } - unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self { + unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Option { let ptr = obs_data_item_get_string(item); - ObsString::Dynamic(CStr::from_ptr(ptr).into()) + ptr.try_into_obs_string().ok() } unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) { obs_data_set_default_string(obj, name.as_ptr(), val.as_ptr()); @@ -104,8 +110,8 @@ macro_rules! impl_get_int { fn typ() -> DataType { DataType::Int } - unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self { - obs_data_item_get_int(item) as $t + unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Option { + Some(obs_data_item_get_int(item) as $t) } unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) { obs_data_set_default_int(obj, name.as_ptr(), val as i64) @@ -121,8 +127,8 @@ impl FromDataItem for f64 { fn typ() -> DataType { DataType::Double } - unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self { - obs_data_item_get_double(item) + unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Option { + Some(obs_data_item_get_double(item)) } unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) { obs_data_set_default_double(obj, name.as_ptr(), val) @@ -133,8 +139,8 @@ impl FromDataItem for f32 { fn typ() -> DataType { DataType::Double } - unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self { - obs_data_item_get_double(item) as f32 + unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Option { + Some(obs_data_item_get_double(item) as f32) } unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) { obs_data_set_default_double(obj, name.as_ptr(), val as f64) @@ -145,8 +151,8 @@ impl FromDataItem for bool { fn typ() -> DataType { DataType::Boolean } - unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self { - obs_data_item_get_bool(item) + unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Option { + Some(obs_data_item_get_bool(item)) } unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) { obs_data_set_default_bool(obj, name.as_ptr(), val) @@ -157,10 +163,12 @@ impl FromDataItem for DataObj<'_> { fn typ() -> DataType { DataType::Object } - unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self { - Self::from_raw(obs_data_item_get_obj(item)) + unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Option { + // https://github.com/obsproject/obs-studio/blob/01610d8c06edb08d0cc3155cb91b3e52e9a6473e/libobs/obs-data.c#L1798 + // `os_atomic_inc_long(&obj->ref);` + Self::from_raw_unchecked(obs_data_item_get_obj(item)) } - unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, mut val: Self) { + unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) { obs_data_set_default_obj(obj, name.as_ptr(), val.as_ptr_mut()) } } @@ -169,8 +177,10 @@ impl FromDataItem for DataArray<'_> { fn typ() -> DataType { DataType::Array } - unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self { - Self::from_raw(obs_data_item_get_array(item)) + unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Option { + // https://github.com/obsproject/obs-studio/blob/01610d8c06edb08d0cc3155cb91b3e52e9a6473e/libobs/obs-data.c#L1811 + // `os_atomic_inc_long(&array->ref);` + Self::from_raw_unchecked(obs_data_item_get_array(item)) } unsafe fn set_default_unchecked(_obj: *mut obs_data_t, _name: ObsString, _val: Self) { unimplemented!("obs_data_set_default_array function doesn't exist") @@ -186,16 +196,29 @@ pub struct DataObj<'parent> { impl PtrWrapper for DataObj<'_> { type Pointer = obs_data_t; - unsafe fn from_raw(raw: *mut Self::Pointer) -> Self { - Self { - raw, - _parent: PhantomData, + unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { + if raw.is_null() { + None + } else { + Some(Self { + raw, + _parent: PhantomData, + }) } } - fn as_ptr(&self) -> *const Self::Pointer { + unsafe fn as_ptr(&self) -> *const Self::Pointer { self.raw } + + unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { + obs_data_addref(ptr); + ptr + } + + unsafe fn release(ptr: *mut Self::Pointer) { + obs_data_release(ptr); + } } impl Default for DataObj<'_> { @@ -209,7 +232,7 @@ impl DataObj<'_> { pub fn new() -> Self { unsafe { let raw = obs_data_create(); - Self::from_raw(raw) + Self::from_raw_unchecked(raw).expect("obs_data_create") } } @@ -218,11 +241,7 @@ impl DataObj<'_> { let json_str = json_str.into(); unsafe { let raw = obs_data_create_from_json(json_str.as_ptr()); - if raw.is_null() { - None - } else { - Some(Self::from_raw(raw)) - } + Self::from_raw_unchecked(raw) } } @@ -241,11 +260,7 @@ impl DataObj<'_> { } else { obs_data_create_from_json_file(json_file.as_ptr()) }; - if raw.is_null() { - None - } else { - Some(Self::from_raw(raw)) - } + Self::from_raw_unchecked(raw) } } @@ -265,7 +280,7 @@ impl DataObj<'_> { let typ = unsafe { DataType::from_item(item_ptr) }; if typ == T::typ() { - Some(unsafe { T::from_item_unchecked(item_ptr) }) + unsafe { T::from_item_unchecked(item_ptr) } } else { None } @@ -288,12 +303,7 @@ impl DataObj<'_> { pub fn get_json(&self) -> Option { unsafe { let ptr = obs_data_get_json(self.raw); - if ptr.is_null() { - None - } else { - let slice = CStr::from_ptr(ptr); - Some(slice.to_string_lossy().into_owned()) - } + Some(ptr.try_into_obs_string().ok()?.as_str().to_string()) } } @@ -328,16 +338,29 @@ pub struct DataArray<'parent> { impl PtrWrapper for DataArray<'_> { type Pointer = obs_data_array_t; - unsafe fn from_raw(raw: *mut Self::Pointer) -> Self { - Self { - raw, - _parent: PhantomData, + unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { + if raw.is_null() { + None + } else { + Some(Self { + raw, + _parent: PhantomData, + }) } } - fn as_ptr(&self) -> *const Self::Pointer { + unsafe fn as_ptr(&self) -> *const Self::Pointer { self.raw } + + unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { + obs_data_array_addref(ptr); + ptr + } + + unsafe fn release(ptr: *mut Self::Pointer) { + obs_data_array_release(ptr) + } } impl DataArray<'_> { @@ -350,12 +373,10 @@ impl DataArray<'_> { } pub fn get(&self, index: usize) -> Option { + // https://github.com/obsproject/obs-studio/blob/01610d8c06edb08d0cc3155cb91b3e52e9a6473e/libobs/obs-data.c#L1395 + // os_atomic_inc_long(&data->ref); let ptr = unsafe { obs_data_array_item(self.raw, index as size_t) }; - if ptr.is_null() { - None - } else { - Some(unsafe { DataObj::from_raw(ptr) }) - } + unsafe { DataObj::from_raw_unchecked(ptr) } } } diff --git a/src/lib.rs b/src/lib.rs index f8da9dd..2a93daf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,6 +123,9 @@ /// Raw bindings of OBS C API pub use obs_sys; +/// FFI pointer wrapper +#[macro_use] +pub mod wrapper; /// `obs_data_t` handling pub mod data; /// Tools required for manipulating graphics in OBS @@ -144,8 +147,6 @@ pub mod result; pub mod source; /// String macros pub mod string; -/// FFI pointer wrapper -pub mod wrapper; mod native_enum; diff --git a/src/output/context.rs b/src/output/context.rs index cb3f570..9cbbf00 100644 --- a/src/output/context.rs +++ b/src/output/context.rs @@ -57,7 +57,7 @@ extern "C" fn enum_proc(params: *mut std::ffi::c_void, output: *mut obs_output_t impl OutputContext { pub fn new(id: ObsString, name: ObsString, settings: Option>) -> Self { let settings = match settings { - Some(mut data) => data.as_ptr_mut(), + Some(data) => unsafe { data.as_ptr_mut() }, None => std::ptr::null_mut(), }; let output = unsafe { diff --git a/src/output/ffi.rs b/src/output/ffi.rs index 68975be..0b674ce 100644 --- a/src/output/ffi.rs +++ b/src/output/ffi.rs @@ -53,7 +53,7 @@ pub unsafe extern "C" fn create( settings: *mut obs_data_t, output: *mut obs_output_t, ) -> *mut c_void { - let settings = DataObj::from_raw(settings); + let settings = DataObj::from_raw(settings).unwrap(); let mut context = CreatableOutputContext::from_raw(settings); let output_context = OutputContext::from_raw(output); @@ -129,13 +129,13 @@ pub unsafe extern "C" fn encoded_packet( pub unsafe extern "C" fn update(data: *mut c_void, settings: *mut obs_data_t) { let data: &mut DataWrapper = &mut *(data as *mut DataWrapper); - let mut settings = DataObj::from_raw(settings); + let mut settings = DataObj::from_raw(settings).unwrap(); D::update(&mut data.data, &mut settings); forget(settings); } pub unsafe extern "C" fn get_defaults(settings: *mut obs_data_t) { - let mut settings = DataObj::from_raw(settings); + let mut settings = DataObj::from_raw(settings).unwrap(); D::get_defaults(&mut settings); forget(settings); } diff --git a/src/properties.rs b/src/properties.rs index 9650650..a61e07a 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -66,13 +66,23 @@ pub struct Properties { impl PtrWrapper for Properties { type Pointer = obs_properties_t; - unsafe fn from_raw(raw: *mut Self::Pointer) -> Self { - Self { pointer: raw } + unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { + if raw.is_null() { + None + } else { + Some(Self { pointer: raw }) + } } - fn as_ptr(&self) -> *const Self::Pointer { + unsafe fn as_ptr(&self) -> *const Self::Pointer { self.pointer } + + unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { + ptr + } + + unsafe fn release(_ptr: *mut Self::Pointer) {} } impl Default for Properties { @@ -85,7 +95,7 @@ impl Properties { pub fn new() -> Self { unsafe { let ptr = obs_properties_create(); - Self::from_raw(ptr) + Self::from_raw_unchecked(ptr).expect("obs_properties_create") } } @@ -120,7 +130,7 @@ impl Properties { .into(), T::format().into(), ); - ListProp::from_raw(raw) + ListProp::from_raw(raw).expect("obs_properties_add_list") } } } @@ -142,17 +152,27 @@ pub struct ListProp<'props, T> { impl PtrWrapper for ListProp<'_, T> { type Pointer = obs_property_t; - unsafe fn from_raw(raw: *mut Self::Pointer) -> Self { - Self { - raw, - _props: PhantomData, - _type: PhantomData, + unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { + if raw.is_null() { + None + } else { + Some(Self { + raw, + _props: PhantomData, + _type: PhantomData, + }) } } - fn as_ptr(&self) -> *const Self::Pointer { + unsafe fn as_ptr(&self) -> *const Self::Pointer { self.raw } + + unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { + ptr + } + + unsafe fn release(_ptr: *mut Self::Pointer) {} } impl ListProp<'_, T> { diff --git a/src/source/ffi.rs b/src/source/ffi.rs index 830a3c4..534ed35 100644 --- a/src/source/ffi.rs +++ b/src/source/ffi.rs @@ -87,7 +87,7 @@ pub unsafe extern "C" fn create( source: *mut obs_source_t, ) -> *mut c_void { let mut global = GlobalContext; - let settings = DataObj::from_raw(settings); + let settings = DataObj::from_raw(settings).unwrap(); let mut context = CreatableSourceContext::from_raw(settings, &mut global); let source_context = SourceRef::from_raw(source).expect("create"); @@ -115,7 +115,7 @@ pub unsafe extern "C" fn destroy(data: *mut c_void) { pub unsafe extern "C" fn update(data: *mut c_void, settings: *mut obs_data_t) { let mut global = GlobalContext; let data: &mut DataWrapper = &mut *(data as *mut DataWrapper); - let mut settings = DataObj::from_raw(settings); + let mut settings = DataObj::from_raw(settings).unwrap(); D::update(&mut data.data, &mut settings, &mut global); forget(settings); } @@ -252,7 +252,7 @@ impl_media!( ); pub unsafe extern "C" fn get_defaults(settings: *mut obs_data_t) { - let mut settings = DataObj::from_raw(settings); + let mut settings = DataObj::from_raw(settings).unwrap(); D::get_defaults(&mut settings); forget(settings); } diff --git a/src/source/mod.rs b/src/source/mod.rs index fc66629..4a9b650 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -131,51 +131,12 @@ impl std::fmt::Debug for SourceRef { } } -// impl PtrWrapper for SourceRef { -// type Pointer = obs_source_t; - -// fn as_ptr(&self) -> *const Self::Pointer { -// self.inner -// } - - -// unsafe fn from_raw(raw: *mut Self::Pointer) -> Self { -// todo!() -// } -// } - -impl SourceRef { - /// # Safety - /// - /// Must call with a valid pointer. - pub fn from_raw(source: *mut obs_source_t) -> Option { - unsafe { Self::from_raw_unchecked(obs_source_get_ref(source)) } - } - - pub unsafe fn from_raw_unchecked(source: *mut obs_source_t) -> Option { - if source.is_null() { - None - } else { - Some(Self { inner: source }) - } - } - - pub unsafe fn as_raw(&self) -> *mut obs_source_t { - self.inner - } -} - -impl Clone for SourceRef { - fn clone(&self) -> Self { - Self::from_raw(self.inner).expect("clone") - } -} - -impl Drop for SourceRef { - fn drop(&mut self) { - unsafe { obs_source_release(self.inner) } - } -} +impl_ptr_wrapper!( + SourceRef, + obs_source_t, + obs_source_get_ref, + obs_source_release +); impl SourceRef { /// Run a function on the next source in the filter chain. diff --git a/src/wrapper.rs b/src/wrapper.rs index 0f492da..7de0fb2 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -2,27 +2,80 @@ use std::mem::forget; pub trait PtrWrapper: Sized { type Pointer; + + unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer; + unsafe fn release(ptr: *mut Self::Pointer); + /// Wraps the pointer into a **owned** wrapper. /// /// # Safety /// /// Pointer must be valid. - unsafe fn from_raw(raw: *mut Self::Pointer) -> Self; + fn from_raw(raw: *mut Self::Pointer) -> Option { + unsafe { Self::from_raw_unchecked(Self::get_ref(raw)) } + } + + /// Wraps a **owned** pointer into wrapper. + /// + /// # Safety + /// + /// You have to make sure you owned the pointer + unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option; /// Returns the inner pointer. - fn as_ptr(&self) -> *const Self::Pointer; + unsafe fn as_ptr(&self) -> *const Self::Pointer; /// Consumes the wrapper and transfers ownershop to the pointer /// /// This does **NOT** drop the wrapper internally. - fn into_raw(mut self) -> *mut Self::Pointer { - let raw = self.as_ptr_mut(); + fn into_raw(self) -> *mut Self::Pointer { + let raw = unsafe { self.as_ptr_mut() }; forget(self); raw } /// Returns the inner pointer (mutable version). - fn as_ptr_mut(&mut self) -> *mut Self::Pointer { + unsafe fn as_ptr_mut(&self) -> *mut Self::Pointer { self.as_ptr() as *mut _ } } + +macro_rules! impl_ptr_wrapper { + ($ref:ident, $ptr:ty, $get_ref:expr, $release:expr) => { + impl PtrWrapper for $ref { + type Pointer = $ptr; + + unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { + unsafe { $get_ref(ptr) } + } + + unsafe fn release(ptr: *mut Self::Pointer) { + unsafe { $release(ptr) } + } + + unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { + if raw.is_null() { + None + } else { + Some(Self { inner: raw }) + } + } + + unsafe fn as_ptr(&self) -> *const Self::Pointer { + self.inner + } + } + + impl Clone for $ref { + fn clone(&self) -> Self { + Self::from_raw(self.inner).expect("clone") + } + } + + impl Drop for $ref { + fn drop(&mut self) { + unsafe { Self::release(self.inner) } + } + } + }; +} From f98b87135180ee2c4c134924f769d14df3b5d2ce Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 6 Apr 2024 09:20:47 +0800 Subject: [PATCH 06/18] add scene --- src/source/mod.rs | 1 + src/source/scene.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/source/scene.rs diff --git a/src/source/mod.rs b/src/source/mod.rs index 4a9b650..9fe7140 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -2,6 +2,7 @@ use paste::item; pub mod context; mod ffi; +pub mod scene; pub mod traits; use crate::{ diff --git a/src/source/scene.rs b/src/source/scene.rs new file mode 100644 index 0000000..1e4cd7b --- /dev/null +++ b/src/source/scene.rs @@ -0,0 +1,72 @@ +use crate::{ + source::SourceRef, + string::{DisplayExt as _, ObsString}, + wrapper::PtrWrapper, +}; +use obs_sys::{ + obs_scene_add, obs_scene_get_ref, obs_scene_get_source, obs_scene_release, obs_scene_t, + obs_sceneitem_addref, obs_sceneitem_release, obs_sceneitem_t, obs_sceneitem_visible, +}; + +use super::Result; + +pub struct SceneRef { + inner: *mut obs_scene_t, +} + +impl std::fmt::Debug for SceneRef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("SceneRef") + .field(&self.name().display()) + .field(&self.inner) + .finish() + } +} + +impl_ptr_wrapper!(SceneRef, obs_scene_t, obs_scene_get_ref, obs_scene_release); + +impl SceneRef { + pub fn name(&self) -> Result { + self.as_source().name() + } + + pub fn as_source(&self) -> SourceRef { + let ptr = unsafe { + // as doc said "The scene’s source. Does not increment the reference" + // we should manually add_ref for it + obs_scene_get_source(self.inner) + }; + SourceRef::from_raw(ptr).expect("obs_scene_get_source") + } + + pub fn add_source(&self, source: SourceRef) -> SceneItemRef { + let ptr = unsafe { + let ptr = obs_scene_add(self.inner, source.as_ptr_mut()); + // add ref for source, Docs said "A new scene item for a source within a scene. Does not + // increment the reference" + obs_sceneitem_addref(ptr); + ptr + }; + SceneItemRef::from_raw(ptr).expect("obs_scene_add") + } +} + +pub struct SceneItemRef { + inner: *mut obs_sceneitem_t, +} + +impl_ptr_wrapper!( + SceneItemRef, + obs_sceneitem_t, + |ptr| { + obs_sceneitem_addref(ptr); + ptr + }, + obs_sceneitem_release +); + +impl SceneItemRef { + pub fn visible(&self) -> bool { + unsafe { obs_sceneitem_visible(self.inner) } + } +} From e7e86dc5077ca9b2accc21c847116101d869fcc5 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 6 Apr 2024 09:52:27 +0800 Subject: [PATCH 07/18] fix test --- README.md | 6 +++--- src/lib.rs | 6 +++--- src/module.rs | 2 +- src/source/scene.rs | 10 ++++++---- src/string.rs | 5 +++-- src/wrapper.rs | 22 ++++++++++++++++++---- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ce62a37..849e533 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ use obs_wrapper::{ // The module that will handle creating the source. struct TestModule { - context: ModuleContext + context: ModuleRef } // The source that will be shown inside OBS. @@ -80,11 +80,11 @@ impl GetNameSource for TestSource { // Implement the Module trait for TestModule. This will handle the creation of the source and // has some methods for telling OBS a bit about itself. impl Module for TestModule { - fn new(context: ModuleContext) -> Self { + fn new(context: ModuleRef) -> Self { Self { context } } - fn get_ctx(&self) -> &ModuleContext { + fn get_ctx(&self) -> &ModuleRef { &self.context } diff --git a/src/lib.rs b/src/lib.rs index 2a93daf..0880724 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,7 +37,7 @@ //! //! // The module that will handle creating the source. //! struct TestModule { -//! context: ModuleContext +//! context: ModuleRef //! }; //! //! // The source that will be shown inside OBS. @@ -73,11 +73,11 @@ //! // Implement the Module trait for TestModule. This will handle the creation //! // of the source and has some methods for telling OBS a bit about itself. //! impl Module for TestModule { -//! fn new(context: ModuleContext) -> Self { +//! fn new(context: ModuleRef) -> Self { //! Self { context } //! } //! -//! fn get_ctx(&self) -> &ModuleContext { +//! fn get_ctx(&self) -> &ModuleRef { //! &self.context //! } //! diff --git a/src/module.rs b/src/module.rs index 03d9311..8a85926 100644 --- a/src/module.rs +++ b/src/module.rs @@ -169,7 +169,7 @@ impl std::fmt::Debug for ModuleRef { impl ModuleRef { /// # Safety - /// Creates a ModuleContext from a pointer to the raw obs_module data which + /// Creates a ModuleRef from a pointer to the raw obs_module data which /// if modified could cause UB. pub fn from_raw(raw: *mut obs_module_t) -> Result { if raw.is_null() { diff --git a/src/source/scene.rs b/src/source/scene.rs index 1e4cd7b..f77f1ca 100644 --- a/src/source/scene.rs +++ b/src/source/scene.rs @@ -55,13 +55,15 @@ pub struct SceneItemRef { inner: *mut obs_sceneitem_t, } +unsafe fn scene_item_get_ref(ptr: *mut obs_sceneitem_t) -> *mut obs_sceneitem_t { + obs_sceneitem_addref(ptr); + ptr +} + impl_ptr_wrapper!( SceneItemRef, obs_sceneitem_t, - |ptr| { - obs_sceneitem_addref(ptr); - ptr - }, + scene_item_get_ref, obs_sceneitem_release ); diff --git a/src/string.rs b/src/string.rs index 0ee6528..9af1dce 100644 --- a/src/string.rs +++ b/src/string.rs @@ -66,6 +66,7 @@ impl TryIntoObsString for String { } } impl TryIntoObsString for *const std::os::raw::c_char { + #[allow(clippy::not_unsafe_ptr_arg_deref)] fn try_into_obs_string(self) -> Result { if self.is_null() { return Err(crate::Error::NulPointer("ObsString")); @@ -89,8 +90,8 @@ impl DisplayExt for Result {} impl std::fmt::Display for DisplayStr<'_, ObsString> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - DisplayStr(ObsString::Static(s)) => return write!(f, "{}", &s[..s.len() - 1]), - DisplayStr(ObsString::Dynamic(s)) => return write!(f, "{}", s.to_string_lossy()), + DisplayStr(ObsString::Static(s)) => write!(f, "{}", &s[..s.len() - 1]), + DisplayStr(ObsString::Dynamic(s)) => write!(f, "{}", s.to_string_lossy()), } } } diff --git a/src/wrapper.rs b/src/wrapper.rs index 7de0fb2..99cb5ca 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -3,14 +3,18 @@ use std::mem::forget; pub trait PtrWrapper: Sized { type Pointer; + /// # Safety + /// + /// This function called extern C api, and should not be called directly. unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer; - unsafe fn release(ptr: *mut Self::Pointer); - /// Wraps the pointer into a **owned** wrapper. - /// /// # Safety /// - /// Pointer must be valid. + /// This function called extern C api, and should not be called directly. + unsafe fn release(ptr: *mut Self::Pointer); + + /// Wraps the pointer into a **owned** wrapper. + #[allow(clippy::not_unsafe_ptr_arg_deref)] fn from_raw(raw: *mut Self::Pointer) -> Option { unsafe { Self::from_raw_unchecked(Self::get_ref(raw)) } } @@ -23,6 +27,11 @@ pub trait PtrWrapper: Sized { unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option; /// Returns the inner pointer. + /// + /// # Safety + /// + /// This function would return a pointer not managed, should only called + /// when interacting with extern C api. unsafe fn as_ptr(&self) -> *const Self::Pointer; /// Consumes the wrapper and transfers ownershop to the pointer @@ -35,6 +44,11 @@ pub trait PtrWrapper: Sized { } /// Returns the inner pointer (mutable version). + /// + /// # Safety + /// + /// This function would return a pointer not managed, should only called + /// when interacting with extern C api. unsafe fn as_ptr_mut(&self) -> *mut Self::Pointer { self.as_ptr() as *mut _ } From 6c1c296d91eb0bb098b4967c3bdcd9d1e843c551 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 6 Apr 2024 10:10:52 +0800 Subject: [PATCH 08/18] add Path into ObsString --- src/result.rs | 3 +++ src/string.rs | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/result.rs b/src/result.rs index e2394c1..1d8a49c 100644 --- a/src/result.rs +++ b/src/result.rs @@ -17,6 +17,9 @@ pub enum Error { /// Error converting enum #[error("Enum Out of Range: {0} {1}")] EnumOutOfRange(&'static str, i64), + /// Error converting path to str + #[error("Path Error: utf8")] + PathUtf8, } pub trait OptionExt { diff --git a/src/string.rs b/src/string.rs index 9af1dce..6d139ef 100644 --- a/src/string.rs +++ b/src/string.rs @@ -1,9 +1,10 @@ use std::{ ffi::{CStr, CString}, + path::Path, ptr::null, }; -use crate::Result; +use crate::{Error, Result}; #[derive(Debug, Eq, PartialEq, Hash, Clone)] pub enum ObsString { @@ -65,6 +66,13 @@ impl TryIntoObsString for String { Ok(ObsString::Dynamic(CString::new(self)?)) } } +impl TryIntoObsString for &Path { + fn try_into_obs_string(self) -> Result { + Ok(ObsString::Dynamic(CString::new( + self.to_str().ok_or(Error::PathUtf8)?, + )?)) + } +} impl TryIntoObsString for *const std::os::raw::c_char { #[allow(clippy::not_unsafe_ptr_arg_deref)] fn try_into_obs_string(self) -> Result { From 9aad331d258c7bffb23fee3e2f7bd87e57a17e98 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sun, 7 Apr 2024 00:00:23 +0800 Subject: [PATCH 09/18] fix add_source --- src/source/scene.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/source/scene.rs b/src/source/scene.rs index f77f1ca..3383259 100644 --- a/src/source/scene.rs +++ b/src/source/scene.rs @@ -41,11 +41,9 @@ impl SceneRef { pub fn add_source(&self, source: SourceRef) -> SceneItemRef { let ptr = unsafe { - let ptr = obs_scene_add(self.inner, source.as_ptr_mut()); // add ref for source, Docs said "A new scene item for a source within a scene. Does not // increment the reference" - obs_sceneitem_addref(ptr); - ptr + obs_scene_add(self.inner, source.as_ptr_mut()) }; SceneItemRef::from_raw(ptr).expect("obs_scene_add") } From e5a6a0df70da858defef88eb6b414c9492eac999 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sat, 6 Apr 2024 11:35:15 +0800 Subject: [PATCH 10/18] improve impl_ptr_wrapper --- src/data.rs | 99 +++++++++++++-------------------------------- src/properties.rs | 32 +++------------ src/source/mod.rs | 1 + src/source/scene.rs | 10 ++--- src/wrapper.rs | 88 +++++++++++++++++++++++++++++++++------- 5 files changed, 113 insertions(+), 117 deletions(-) diff --git a/src/data.rs b/src/data.rs index 24ba0bf..a5e428d 100644 --- a/src/data.rs +++ b/src/data.rs @@ -6,18 +6,18 @@ use std::{ }; use obs_sys::{ - obs_data_addref, obs_data_array_addref, obs_data_array_count, obs_data_array_item, - obs_data_array_release, obs_data_array_t, obs_data_clear, obs_data_create, - obs_data_create_from_json, obs_data_create_from_json_file, obs_data_create_from_json_file_safe, - obs_data_erase, obs_data_get_json, obs_data_item_byname, obs_data_item_get_array, - obs_data_item_get_bool, obs_data_item_get_double, obs_data_item_get_int, obs_data_item_get_obj, - obs_data_item_get_string, obs_data_item_gettype, obs_data_item_numtype, obs_data_item_release, - obs_data_item_t, obs_data_number_type, obs_data_number_type_OBS_DATA_NUM_DOUBLE, - obs_data_number_type_OBS_DATA_NUM_INT, obs_data_release, obs_data_set_default_bool, - obs_data_set_default_double, obs_data_set_default_int, obs_data_set_default_obj, - obs_data_set_default_string, obs_data_t, obs_data_type, obs_data_type_OBS_DATA_ARRAY, - obs_data_type_OBS_DATA_BOOLEAN, obs_data_type_OBS_DATA_NUMBER, obs_data_type_OBS_DATA_OBJECT, - obs_data_type_OBS_DATA_STRING, size_t, + obs_data_array_count, obs_data_array_item, obs_data_array_release, obs_data_array_t, + obs_data_clear, obs_data_create, obs_data_create_from_json, obs_data_create_from_json_file, + obs_data_create_from_json_file_safe, obs_data_erase, obs_data_get_json, obs_data_item_byname, + obs_data_item_get_array, obs_data_item_get_bool, obs_data_item_get_double, + obs_data_item_get_int, obs_data_item_get_obj, obs_data_item_get_string, obs_data_item_gettype, + obs_data_item_numtype, obs_data_item_release, obs_data_item_t, obs_data_number_type, + obs_data_number_type_OBS_DATA_NUM_DOUBLE, obs_data_number_type_OBS_DATA_NUM_INT, + obs_data_release, obs_data_set_default_bool, obs_data_set_default_double, + obs_data_set_default_int, obs_data_set_default_obj, obs_data_set_default_string, obs_data_t, + obs_data_type, obs_data_type_OBS_DATA_ARRAY, obs_data_type_OBS_DATA_BOOLEAN, + obs_data_type_OBS_DATA_NUMBER, obs_data_type_OBS_DATA_OBJECT, obs_data_type_OBS_DATA_STRING, + size_t, }; use crate::{ @@ -193,34 +193,22 @@ pub struct DataObj<'parent> { _parent: PhantomData<&'parent DataObj<'parent>>, } -impl PtrWrapper for DataObj<'_> { - type Pointer = obs_data_t; - - unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { - if raw.is_null() { - None - } else { - Some(Self { - raw, - _parent: PhantomData, - }) +impl crate::wrapper::PtrWrapperInternal for DataObj<'_> { + unsafe fn new_internal(ptr: *mut Self::Pointer) -> Self { + Self { + raw: ptr, + _parent: PhantomData, } } - unsafe fn as_ptr(&self) -> *const Self::Pointer { + unsafe fn get_internal(&self) -> *mut Self::Pointer { self.raw } - - unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { - obs_data_addref(ptr); - ptr - } - - unsafe fn release(ptr: *mut Self::Pointer) { - obs_data_release(ptr); - } } +// impl_ptr_wrapper!(DataObj, obs_data_t, @addref: obs_data_addref, obs_data_release); +impl_ptr_wrapper!(DataObj<'_>, obs_data_t, @identity, obs_data_release); + impl Default for DataObj<'_> { fn default() -> Self { DataObj::new() @@ -322,47 +310,26 @@ impl DataObj<'_> { } } -impl Drop for DataObj<'_> { - fn drop(&mut self) { - unsafe { - obs_data_release(self.raw); - } - } -} - pub struct DataArray<'parent> { raw: *mut obs_data_array_t, _parent: PhantomData<&'parent DataArray<'parent>>, } -impl PtrWrapper for DataArray<'_> { - type Pointer = obs_data_array_t; - - unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { - if raw.is_null() { - None - } else { - Some(Self { - raw, - _parent: PhantomData, - }) +impl crate::wrapper::PtrWrapperInternal for DataArray<'_> { + unsafe fn new_internal(raw: *mut Self::Pointer) -> Self { + Self { + raw, + _parent: PhantomData, } } - unsafe fn as_ptr(&self) -> *const Self::Pointer { + unsafe fn get_internal(&self) -> *mut Self::Pointer { self.raw } - - unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { - obs_data_array_addref(ptr); - ptr - } - - unsafe fn release(ptr: *mut Self::Pointer) { - obs_data_array_release(ptr) - } } +impl_ptr_wrapper!(DataArray<'_>, obs_data_array_t, @identity, obs_data_array_release); + impl DataArray<'_> { pub fn len(&self) -> usize { unsafe { obs_data_array_count(self.raw) } @@ -379,11 +346,3 @@ impl DataArray<'_> { unsafe { DataObj::from_raw_unchecked(ptr) } } } - -impl Drop for DataArray<'_> { - fn drop(&mut self) { - unsafe { - obs_data_array_release(self.raw); - } - } -} diff --git a/src/properties.rs b/src/properties.rs index a61e07a..91de4a4 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -63,26 +63,12 @@ pub struct Properties { pointer: *mut obs_properties_t, } -impl PtrWrapper for Properties { - type Pointer = obs_properties_t; - - unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { - if raw.is_null() { - None - } else { - Some(Self { pointer: raw }) - } - } - - unsafe fn as_ptr(&self) -> *const Self::Pointer { - self.pointer - } - - unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { - ptr - } - - unsafe fn release(_ptr: *mut Self::Pointer) {} +impl_ptr_wrapper! { + @ptr: pointer, + Properties, + obs_properties_t, + @identity, + obs_properties_destroy } impl Default for Properties { @@ -135,12 +121,6 @@ impl Properties { } } -impl Drop for Properties { - fn drop(&mut self) { - unsafe { obs_properties_destroy(self.pointer) } - } -} - /// Wrapper around [`obs_property_t`], which is a list of possible values for a /// property. pub struct ListProp<'props, T> { diff --git a/src/source/mod.rs b/src/source/mod.rs index 9fe7140..42a859b 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -133,6 +133,7 @@ impl std::fmt::Debug for SourceRef { } impl_ptr_wrapper!( + @ptr: inner, SourceRef, obs_source_t, obs_source_get_ref, diff --git a/src/source/scene.rs b/src/source/scene.rs index 3383259..7597a3a 100644 --- a/src/source/scene.rs +++ b/src/source/scene.rs @@ -23,7 +23,7 @@ impl std::fmt::Debug for SceneRef { } } -impl_ptr_wrapper!(SceneRef, obs_scene_t, obs_scene_get_ref, obs_scene_release); +impl_ptr_wrapper!(@ptr: inner, SceneRef, obs_scene_t, obs_scene_get_ref, obs_scene_release); impl SceneRef { pub fn name(&self) -> Result { @@ -53,15 +53,11 @@ pub struct SceneItemRef { inner: *mut obs_sceneitem_t, } -unsafe fn scene_item_get_ref(ptr: *mut obs_sceneitem_t) -> *mut obs_sceneitem_t { - obs_sceneitem_addref(ptr); - ptr -} - impl_ptr_wrapper!( + @ptr: inner, SceneItemRef, obs_sceneitem_t, - scene_item_get_ref, + @addref: obs_sceneitem_addref, obs_sceneitem_release ); diff --git a/src/wrapper.rs b/src/wrapper.rs index 99cb5ca..6a9e34e 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -1,5 +1,18 @@ use std::mem::forget; +pub trait PtrWrapperInternal: PtrWrapper { + /// # Safety + /// + /// This function should not be called directly, use `from_raw` and + /// `from_raw_unchecked` instead. + unsafe fn new_internal(ptr: *mut Self::Pointer) -> Self; + /// # Safety + /// + /// This function should not be called directly, use `from_raw` and + /// `from_raw_unchecked` instead. + unsafe fn get_internal(&self) -> *mut Self::Pointer; +} + pub trait PtrWrapper: Sized { type Pointer; @@ -55,40 +68,87 @@ pub trait PtrWrapper: Sized { } macro_rules! impl_ptr_wrapper { - ($ref:ident, $ptr:ty, $get_ref:expr, $release:expr) => { - impl PtrWrapper for $ref { - type Pointer = $ptr; + (@ptr: $field:ident, $ref:ty, $($tt:tt)*) => { + impl_ptr_wrapper!(@__impl.trait.internal $ref, $field); + impl_ptr_wrapper!($ref, $($tt)*); + }; + ($ref:ty, $ptr:ty, @identity, $release:expr) => { + impl_ptr_wrapper!(@__impl.trait.wrapper $ref, $ptr, impl_ptr_wrapper!{@__impl.fn.id}, $release); + // when get_ref is `@identity`, no `Clone implemented` + impl_ptr_wrapper!(@__impl.trait.drop $ref, $ptr); + }; + ($ref:ty, $ptr:ty, $get_ref:expr, $release:expr) => { + impl_ptr_wrapper!(@__impl.trait.wrapper $ref, $ptr, impl_ptr_wrapper!{@__impl.fn.get_ref $get_ref}, $release); + impl_ptr_wrapper!(@__impl.trait.clone $ref, $ptr); + impl_ptr_wrapper!(@__impl.trait.drop $ref, $ptr); + }; - unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { - unsafe { $get_ref(ptr) } + ($ref:ty, $ptr:ty, @addref: $add_ref:expr, $release:expr) => { + impl_ptr_wrapper!(@__impl.trait.wrapper $ref, $ptr, impl_ptr_wrapper!{@__impl.fn.add_ref $add_ref}, $release); + impl_ptr_wrapper!(@__impl.trait.clone $ref, $ptr); + impl_ptr_wrapper!(@__impl.trait.drop $ref, $ptr); + }; + (@__impl.fn.get_ref $get_ref:expr) => { + unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { + unsafe { $get_ref(ptr) } + } + }; + (@__impl.fn.add_ref $add_ref:expr) => { + unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { + unsafe { $add_ref(ptr); ptr } + } + }; + (@__impl.fn.id) => { + unsafe fn get_ref(ptr: *mut Self::Pointer) -> *mut Self::Pointer { + ptr + } + }; + (@__impl.trait.internal $ref:ty, $field:ident) => { + impl $crate::wrapper::PtrWrapperInternal for $ref { + unsafe fn new_internal(ptr: *mut Self::Pointer) -> Self { + Self { $field: ptr } } - - unsafe fn release(ptr: *mut Self::Pointer) { - unsafe { $release(ptr) } + unsafe fn get_internal(&self) -> *mut Self::Pointer { + self.$field } + } + }; + (@__impl.trait.wrapper $ref:ty, $ptr:ty, $get_ref:item, $release:expr) => { + impl PtrWrapper for $ref { + type Pointer = $ptr; unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { + use $crate::wrapper::PtrWrapperInternal; if raw.is_null() { None } else { - Some(Self { inner: raw }) + Some(Self::new_internal(raw)) } } + $get_ref + + unsafe fn release(ptr: *mut Self::Pointer) { + unsafe { $release(ptr) } + } + unsafe fn as_ptr(&self) -> *const Self::Pointer { - self.inner + use $crate::wrapper::PtrWrapperInternal; + self.get_internal() } } - + }; + (@__impl.trait.clone $ref:ty, $ptr:ty) => { impl Clone for $ref { fn clone(&self) -> Self { - Self::from_raw(self.inner).expect("clone") + Self::from_raw(unsafe { self.as_ptr_mut() }).expect("clone") } } - + }; + (@__impl.trait.drop $ref:ty, $ptr:ty) => { impl Drop for $ref { fn drop(&mut self) { - unsafe { Self::release(self.inner) } + unsafe { Self::release(self.as_ptr_mut()) } } } }; From 710f3ac8c7deb8de90e2ca728ecb0ea9445992a1 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sun, 7 Apr 2024 00:01:38 +0800 Subject: [PATCH 11/18] add display --- src/graphics/display.rs | 21 +++++++++++++++++++++ src/graphics/mod.rs | 2 ++ src/wrapper.rs | 3 ++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/graphics/display.rs diff --git a/src/graphics/display.rs b/src/graphics/display.rs new file mode 100644 index 0000000..9999314 --- /dev/null +++ b/src/graphics/display.rs @@ -0,0 +1,21 @@ +use obs_sys::{obs_display_destroy, obs_display_enabled, obs_display_resize, obs_display_t}; + +pub struct DisplayRef { + inner: *mut obs_display_t +} + +impl_ptr_wrapper!(@ptr: inner, DisplayRef, obs_display_t, @identity, obs_display_destroy); + +impl DisplayRef { + pub fn enabled(&self) -> bool { + unsafe { + obs_display_enabled(self.inner) + } + } + + pub fn set_size(&self, cx: u32, cy: u32) { + unsafe { + obs_display_resize(self.inner, cx, cy) + } + } +} diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs index 13521e6..bf2e7ea 100644 --- a/src/graphics/mod.rs +++ b/src/graphics/mod.rs @@ -1,3 +1,5 @@ +pub mod display; + use crate::{Error, Result}; use core::convert::TryFrom; use core::ptr::null_mut; diff --git a/src/wrapper.rs b/src/wrapper.rs index 6a9e34e..2348188 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -114,7 +114,7 @@ macro_rules! impl_ptr_wrapper { } }; (@__impl.trait.wrapper $ref:ty, $ptr:ty, $get_ref:item, $release:expr) => { - impl PtrWrapper for $ref { + impl $crate::wrapper::PtrWrapper for $ref { type Pointer = $ptr; unsafe fn from_raw_unchecked(raw: *mut Self::Pointer) -> Option { @@ -148,6 +148,7 @@ macro_rules! impl_ptr_wrapper { (@__impl.trait.drop $ref:ty, $ptr:ty) => { impl Drop for $ref { fn drop(&mut self) { + use $crate::wrapper::PtrWrapper; unsafe { Self::release(self.as_ptr_mut()) } } } From f6f24295d2d72eee592a1f9bd02e034102a0841a Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sun, 7 Apr 2024 04:27:25 +0800 Subject: [PATCH 12/18] add_draw_callback --- src/graphics/display.rs | 100 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/src/graphics/display.rs b/src/graphics/display.rs index 9999314..c3decec 100644 --- a/src/graphics/display.rs +++ b/src/graphics/display.rs @@ -1,21 +1,111 @@ -use obs_sys::{obs_display_destroy, obs_display_enabled, obs_display_resize, obs_display_t}; +use obs_sys::{ + obs_display_add_draw_callback, obs_display_destroy, obs_display_enabled, + obs_display_remove_draw_callback, obs_display_resize, obs_display_t, obs_render_main_texture, +}; pub struct DisplayRef { - inner: *mut obs_display_t + inner: *mut obs_display_t, } impl_ptr_wrapper!(@ptr: inner, DisplayRef, obs_display_t, @identity, obs_display_destroy); impl DisplayRef { pub fn enabled(&self) -> bool { + unsafe { obs_display_enabled(self.inner) } + } + + pub fn set_size(&self, cx: u32, cy: u32) { + unsafe { obs_display_resize(self.inner, cx, cy) } + } + + pub fn add_draw_callback<'a, S: DrawCallback>(&'a self, callback: S) -> DrawCallbackId<'a, S> { + let data = Box::into_raw(Box::new(callback)); + let callback = draw_callback::; unsafe { - obs_display_enabled(self.inner) + obs_display_add_draw_callback(self.inner, Some(callback), data as *mut std::ffi::c_void) } + DrawCallbackId::new(data, callback as *const _, self.inner) } - pub fn set_size(&self, cx: u32, cy: u32) { + pub fn remove_draw_callback(&self, data: DrawCallbackId) -> S { + data.take(self) + } +} + +pub struct RenderMainTexture; + +impl DrawCallback for RenderMainTexture { + fn draw(&self, _cx: u32, _cy: u32) { + unsafe { obs_render_main_texture() } + } +} + +pub trait DrawCallback { + fn draw(&self, cx: u32, cy: u32); +} + +pub unsafe extern "C" fn draw_callback( + data: *mut std::ffi::c_void, + cx: u32, + cy: u32, +) { + let callback = &*(data as *const S); + callback.draw(cx, cy); +} + +pub struct DrawCallbackId<'a, S> { + data: *mut S, + callback: *const std::ffi::c_void, + display: *mut obs_display_t, + _marker: std::marker::PhantomData<&'a S>, +} + +impl<'a, S> DrawCallbackId<'a, S> { + pub fn new( + data: *mut S, + callback: *const std::ffi::c_void, + display: *mut obs_display_t, + ) -> Self { + DrawCallbackId { + data, + callback, + display, + _marker: std::marker::PhantomData, + } + } + + pub fn take(self, display: &DisplayRef) -> S { + assert_eq!(self.display, display.inner); + let ptr = self.data; + unsafe { + obs_display_add_draw_callback( + self.display, + Some(std::mem::transmute(self.callback)), + ptr as *mut std::ffi::c_void, + ) + } + std::mem::forget(self); + unsafe { *Box::from_raw(ptr) } + } + + /// Forget the callback and keep it alive forever. + /// As long as the underlying display is alive, the callback will be called. + /// If the display is destroyed, the callback will be also dropped. + pub fn forever(self) { + std::mem::forget(self); + } +} + +impl<'a, S> Drop for DrawCallbackId<'a, S> { + fn drop(&mut self) { unsafe { - obs_display_resize(self.inner, cx, cy) + // we don't check validity of the display here + obs_display_remove_draw_callback( + self.display, + Some(std::mem::transmute(self.callback)), + self.data as *mut std::ffi::c_void, + ); + drop(Box::from_raw(self.data)); } } } From f1a54cd0fa3f00f6b2cb36e1b432d11cbf2a0856 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sun, 7 Apr 2024 05:17:33 +0800 Subject: [PATCH 13/18] add some comments about function pointer --- src/graphics/display.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/graphics/display.rs b/src/graphics/display.rs index c3decec..a3c5f56 100644 --- a/src/graphics/display.rs +++ b/src/graphics/display.rs @@ -18,13 +18,17 @@ impl DisplayRef { unsafe { obs_display_resize(self.inner, cx, cy) } } - pub fn add_draw_callback<'a, S: DrawCallback>(&'a self, callback: S) -> DrawCallbackId<'a, S> { + pub fn add_draw_callback(&self, callback: S) -> DrawCallbackId<'_, S> { let data = Box::into_raw(Box::new(callback)); - let callback = draw_callback::; + // force the pointer to be a function pointer, since it is not garantueed to be the same pointer + // for different instance of generic functions + // see https://users.rust-lang.org/t/generic-functions-and-their-pointer-uniquness/36989 + // clippy: #[deny(clippy::fn_address_comparisons)] + let callback: unsafe extern "C" fn(*mut std::ffi::c_void, u32, u32) = draw_callback::; unsafe { obs_display_add_draw_callback(self.inner, Some(callback), data as *mut std::ffi::c_void) } - DrawCallbackId::new(data, callback as *const _, self.inner) + DrawCallbackId::new(data, callback as *const _, self) } pub fn remove_draw_callback(&self, data: DrawCallbackId) -> S { @@ -44,6 +48,8 @@ pub trait DrawCallback { fn draw(&self, cx: u32, cy: u32); } +/// # Safety +/// This function is called by OBS, and it is guaranteed that the pointer is valid. pub unsafe extern "C" fn draw_callback( data: *mut std::ffi::c_void, cx: u32, @@ -61,15 +67,11 @@ pub struct DrawCallbackId<'a, S> { } impl<'a, S> DrawCallbackId<'a, S> { - pub fn new( - data: *mut S, - callback: *const std::ffi::c_void, - display: *mut obs_display_t, - ) -> Self { + pub fn new(data: *mut S, callback: *const std::ffi::c_void, display: &'a DisplayRef) -> Self { DrawCallbackId { data, callback, - display, + display: display.inner, _marker: std::marker::PhantomData, } } From 4a747e72fbfaa061e8b4f0b8ac43fb9d10778ccf Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sun, 7 Apr 2024 06:35:31 +0800 Subject: [PATCH 14/18] use native_enum --- src/graphics/mod.rs | 243 +++++++++++--------------------------------- src/media/state.rs | 54 +++------- src/media/video.rs | 110 ++++++-------------- src/native_enum.rs | 36 +++++-- src/properties.rs | 10 +- src/source/ffi.rs | 2 +- src/source/mod.rs | 10 +- 7 files changed, 143 insertions(+), 322 deletions(-) diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs index bf2e7ea..e8076a4 100644 --- a/src/graphics/mod.rs +++ b/src/graphics/mod.rs @@ -1,6 +1,6 @@ pub mod display; -use crate::{Error, Result}; +use crate::{native_enum, Error, Result}; use core::convert::TryFrom; use core::ptr::null_mut; use obs_sys::{ @@ -72,62 +72,21 @@ impl Drop for GraphicsGuard { } } -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum ShaderParamType { - Unknown, - Bool, - Float, - Int, - String, - Vec2, - Vec3, - Vec4, - Int2, - Int3, - Int4, - Mat4, - Texture, -} - -impl ShaderParamType { - pub fn as_raw(&self) -> gs_shader_param_type { - match self { - ShaderParamType::Unknown => gs_shader_param_type_GS_SHADER_PARAM_UNKNOWN, - ShaderParamType::Bool => gs_shader_param_type_GS_SHADER_PARAM_BOOL, - ShaderParamType::Float => gs_shader_param_type_GS_SHADER_PARAM_FLOAT, - ShaderParamType::Int => gs_shader_param_type_GS_SHADER_PARAM_INT, - ShaderParamType::String => gs_shader_param_type_GS_SHADER_PARAM_STRING, - ShaderParamType::Vec2 => gs_shader_param_type_GS_SHADER_PARAM_VEC2, - ShaderParamType::Vec3 => gs_shader_param_type_GS_SHADER_PARAM_VEC3, - ShaderParamType::Vec4 => gs_shader_param_type_GS_SHADER_PARAM_VEC4, - ShaderParamType::Int2 => gs_shader_param_type_GS_SHADER_PARAM_INT2, - ShaderParamType::Int3 => gs_shader_param_type_GS_SHADER_PARAM_INT3, - ShaderParamType::Int4 => gs_shader_param_type_GS_SHADER_PARAM_INT4, - ShaderParamType::Mat4 => gs_shader_param_type_GS_SHADER_PARAM_MATRIX4X4, - ShaderParamType::Texture => gs_shader_param_type_GS_SHADER_PARAM_TEXTURE, - } - } - - #[allow(non_upper_case_globals)] - pub fn from_raw(param_type: gs_shader_param_type) -> Self { - match param_type { - gs_shader_param_type_GS_SHADER_PARAM_UNKNOWN => ShaderParamType::Unknown, - gs_shader_param_type_GS_SHADER_PARAM_BOOL => ShaderParamType::Bool, - gs_shader_param_type_GS_SHADER_PARAM_FLOAT => ShaderParamType::Float, - gs_shader_param_type_GS_SHADER_PARAM_INT => ShaderParamType::Int, - gs_shader_param_type_GS_SHADER_PARAM_STRING => ShaderParamType::String, - gs_shader_param_type_GS_SHADER_PARAM_VEC2 => ShaderParamType::Vec2, - gs_shader_param_type_GS_SHADER_PARAM_VEC3 => ShaderParamType::Vec3, - gs_shader_param_type_GS_SHADER_PARAM_VEC4 => ShaderParamType::Vec4, - gs_shader_param_type_GS_SHADER_PARAM_INT2 => ShaderParamType::Int2, - gs_shader_param_type_GS_SHADER_PARAM_INT3 => ShaderParamType::Int3, - gs_shader_param_type_GS_SHADER_PARAM_INT4 => ShaderParamType::Int4, - gs_shader_param_type_GS_SHADER_PARAM_MATRIX4X4 => ShaderParamType::Mat4, - gs_shader_param_type_GS_SHADER_PARAM_TEXTURE => ShaderParamType::Texture, - _ => panic!("Invalid param_type!"), - } - } -} +native_enum!(ShaderParamType, gs_shader_param_type { + Unknown => GS_SHADER_PARAM_UNKNOWN, + Bool => GS_SHADER_PARAM_BOOL, + Float => GS_SHADER_PARAM_FLOAT, + Int => GS_SHADER_PARAM_INT, + String => GS_SHADER_PARAM_STRING, + Vec2 => GS_SHADER_PARAM_VEC2, + Vec3 => GS_SHADER_PARAM_VEC3, + Vec4 => GS_SHADER_PARAM_VEC4, + Int2 => GS_SHADER_PARAM_INT2, + Int3 => GS_SHADER_PARAM_INT3, + Int4 => GS_SHADER_PARAM_INT4, + Mat4 => GS_SHADER_PARAM_MATRIX4X4, + Texture => GS_SHADER_PARAM_TEXTURE, +}); pub struct GraphicsEffect { raw: *mut gs_effect_t, @@ -192,7 +151,7 @@ impl GraphicsEffectParam { let mut info = gs_effect_param_info::default(); gs_effect_get_param_info(raw, &mut info); - let shader_type = ShaderParamType::from_raw(info.type_); + let shader_type = ShaderParamType::from_raw(info.type_).unwrap(); let name = CString::from(CStr::from_ptr(info.name)) .into_string() .unwrap_or_else(|_| String::from("{unknown-param-name}")); @@ -263,65 +222,25 @@ impl GraphicsEffectTextureParam { } } -pub enum GraphicsAddressMode { - Clamp, - Wrap, - Mirror, - Border, - MirrorOnce, -} +native_enum!(GraphicsAddressMode, gs_address_mode { + Clamp => GS_ADDRESS_CLAMP, + Wrap => GS_ADDRESS_WRAP, + Mirror => GS_ADDRESS_MIRROR, + Border => GS_ADDRESS_BORDER, + MirrorOnce => GS_ADDRESS_MIRRORONCE, +}); -impl GraphicsAddressMode { - pub fn as_raw(&self) -> gs_address_mode { - match self { - GraphicsAddressMode::Clamp => gs_address_mode_GS_ADDRESS_CLAMP, - GraphicsAddressMode::Wrap => gs_address_mode_GS_ADDRESS_WRAP, - GraphicsAddressMode::Mirror => gs_address_mode_GS_ADDRESS_MIRROR, - GraphicsAddressMode::Border => gs_address_mode_GS_ADDRESS_BORDER, - GraphicsAddressMode::MirrorOnce => gs_address_mode_GS_ADDRESS_MIRRORONCE, - } - } -} - -pub enum GraphicsSampleFilter { - Point, - Linear, - Anisotropic, - MinMagPointMipLinear, - MinPointMagLinearMipPoint, - MinPointMagMipLinear, - MinLinearMapMipPoint, - MinLinearMagPointMipLinear, - MinMagLinearMipPoint, -} - -impl GraphicsSampleFilter { - fn as_raw(&self) -> gs_sample_filter { - match self { - GraphicsSampleFilter::Point => gs_sample_filter_GS_FILTER_POINT, - GraphicsSampleFilter::Linear => gs_sample_filter_GS_FILTER_LINEAR, - GraphicsSampleFilter::Anisotropic => gs_sample_filter_GS_FILTER_ANISOTROPIC, - GraphicsSampleFilter::MinMagPointMipLinear => { - gs_sample_filter_GS_FILTER_MIN_MAG_POINT_MIP_LINEAR - } - GraphicsSampleFilter::MinPointMagLinearMipPoint => { - gs_sample_filter_GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT - } - GraphicsSampleFilter::MinPointMagMipLinear => { - gs_sample_filter_GS_FILTER_MIN_POINT_MAG_MIP_LINEAR - } - GraphicsSampleFilter::MinLinearMapMipPoint => { - gs_sample_filter_GS_FILTER_MIN_LINEAR_MAG_MIP_POINT - } - GraphicsSampleFilter::MinLinearMagPointMipLinear => { - gs_sample_filter_GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR - } - GraphicsSampleFilter::MinMagLinearMipPoint => { - gs_sample_filter_GS_FILTER_MIN_MAG_LINEAR_MIP_POINT - } - } - } -} +native_enum!(GraphicsSampleFilter, gs_sample_filter { + Point => GS_FILTER_POINT, + Linear => GS_FILTER_LINEAR, + Anisotropic => GS_FILTER_ANISOTROPIC, + MinMagPointMipLinear => GS_FILTER_MIN_MAG_POINT_MIP_LINEAR, + MinPointMagLinearMipPoint => GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, + MinPointMagMipLinear => GS_FILTER_MIN_POINT_MAG_MIP_LINEAR, + MinLinearMapMipPoint => GS_FILTER_MIN_LINEAR_MAG_MIP_POINT, + MinLinearMagPointMipLinear => GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, + MinMagLinearMipPoint => GS_FILTER_MIN_MAG_LINEAR_MIP_POINT, +}); pub struct GraphicsSamplerInfo { info: gs_sampler_info, @@ -398,72 +317,32 @@ impl GraphicsEffectContext { } } -#[derive(Debug, Eq, PartialEq, Clone, Copy)] -pub enum GraphicsColorFormat { - UNKNOWN, - A8, - R8, - RGBA, - BGRX, - BGRA, - R10G10B10A2, - RGBA16, - R16, - RGBA16F, - RGBA32F, - RG16F, - RG32F, - R16F, - R32F, - DXT1, - DXT3, - DXT5, - R8G8, -} - -impl GraphicsColorFormat { - pub fn as_raw(&self) -> gs_color_format { - match self { - GraphicsColorFormat::UNKNOWN => gs_color_format_GS_UNKNOWN, - GraphicsColorFormat::A8 => gs_color_format_GS_A8, - GraphicsColorFormat::R8 => gs_color_format_GS_R8, - GraphicsColorFormat::RGBA => gs_color_format_GS_RGBA, - GraphicsColorFormat::BGRX => gs_color_format_GS_BGRX, - GraphicsColorFormat::BGRA => gs_color_format_GS_BGRA, - GraphicsColorFormat::R10G10B10A2 => gs_color_format_GS_R10G10B10A2, - GraphicsColorFormat::RGBA16 => gs_color_format_GS_RGBA16, - GraphicsColorFormat::R16 => gs_color_format_GS_R16, - GraphicsColorFormat::RGBA16F => gs_color_format_GS_RGBA16F, - GraphicsColorFormat::RGBA32F => gs_color_format_GS_RGBA32F, - GraphicsColorFormat::RG16F => gs_color_format_GS_RG16F, - GraphicsColorFormat::RG32F => gs_color_format_GS_RG32F, - GraphicsColorFormat::R16F => gs_color_format_GS_R16F, - GraphicsColorFormat::R32F => gs_color_format_GS_R32F, - GraphicsColorFormat::DXT1 => gs_color_format_GS_DXT1, - GraphicsColorFormat::DXT3 => gs_color_format_GS_DXT3, - GraphicsColorFormat::DXT5 => gs_color_format_GS_DXT5, - GraphicsColorFormat::R8G8 => gs_color_format_GS_R8G8, - } - } -} - -pub enum GraphicsAllowDirectRendering { - NoDirectRendering, - AllowDirectRendering, -} - -impl GraphicsAllowDirectRendering { - pub fn as_raw(&self) -> obs_allow_direct_render { - match self { - GraphicsAllowDirectRendering::NoDirectRendering => { - obs_allow_direct_render_OBS_NO_DIRECT_RENDERING - } - GraphicsAllowDirectRendering::AllowDirectRendering => { - obs_allow_direct_render_OBS_ALLOW_DIRECT_RENDERING - } - } - } -} +native_enum!(GraphicsColorFormat, gs_color_format { + UNKNOWN => GS_UNKNOWN, + A8 => GS_A8, + R8 => GS_R8, + RGBA => GS_RGBA, + BGRX => GS_BGRX, + BGRA => GS_BGRA, + R10G10B10A2 => GS_R10G10B10A2, + RGBA16 => GS_RGBA16, + R16 => GS_R16, + RGBA16F => GS_RGBA16F, + RGBA32F => GS_RGBA32F, + RG16F => GS_RG16F, + RG32F => GS_RG32F, + R16F => GS_R16F, + R32F => GS_R32F, + DXT1 => GS_DXT1, + DXT3 => GS_DXT3, + DXT5 => GS_DXT5, + R8G8 => GS_R8G8, +}); + +native_enum!(GraphicsAllowDirectRendering, obs_allow_direct_render { + NoDirectRendering => OBS_NO_DIRECT_RENDERING, + AllowDirectRendering => OBS_ALLOW_DIRECT_RENDERING, +}); macro_rules! vector_impls { ($($rust_name: ident, $name:ident => $($component:ident)*,)*) => ( diff --git a/src/media/state.rs b/src/media/state.rs index 90e1a2d..38375cb 100644 --- a/src/media/state.rs +++ b/src/media/state.rs @@ -6,45 +6,17 @@ use obs_sys::{ obs_media_state_OBS_MEDIA_STATE_STOPPED, }; -/// OBS media state -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum MediaState { - None, - Playing, - Opening, - Buffering, - Paused, - Stopped, - Ended, - Error, -} - -impl MediaState { - #[allow(non_upper_case_globals)] - pub fn from_native(state: obs_media_state) -> Option { - match state { - obs_media_state_OBS_MEDIA_STATE_NONE => Some(Self::None), - obs_media_state_OBS_MEDIA_STATE_PLAYING => Some(Self::Playing), - obs_media_state_OBS_MEDIA_STATE_OPENING => Some(Self::Opening), - obs_media_state_OBS_MEDIA_STATE_BUFFERING => Some(Self::Buffering), - obs_media_state_OBS_MEDIA_STATE_PAUSED => Some(Self::Paused), - obs_media_state_OBS_MEDIA_STATE_STOPPED => Some(Self::Stopped), - obs_media_state_OBS_MEDIA_STATE_ENDED => Some(Self::Ended), - obs_media_state_OBS_MEDIA_STATE_ERROR => Some(Self::Error), - _ => None, - } - } +use crate::native_enum; - pub fn to_native(self) -> obs_media_state { - match self { - Self::None => obs_media_state_OBS_MEDIA_STATE_NONE, - Self::Playing => obs_media_state_OBS_MEDIA_STATE_PLAYING, - Self::Opening => obs_media_state_OBS_MEDIA_STATE_OPENING, - Self::Buffering => obs_media_state_OBS_MEDIA_STATE_BUFFERING, - Self::Paused => obs_media_state_OBS_MEDIA_STATE_PAUSED, - Self::Stopped => obs_media_state_OBS_MEDIA_STATE_STOPPED, - Self::Ended => obs_media_state_OBS_MEDIA_STATE_ENDED, - Self::Error => obs_media_state_OBS_MEDIA_STATE_ERROR, - } - } -} +native_enum!( +/// OBS media state +MediaState, obs_media_state { + None => OBS_MEDIA_STATE_NONE, + Playing => OBS_MEDIA_STATE_PLAYING, + Opening => OBS_MEDIA_STATE_OPENING, + Buffering => OBS_MEDIA_STATE_BUFFERING, + Paused => OBS_MEDIA_STATE_PAUSED, + Stopped => OBS_MEDIA_STATE_STOPPED, + Ended => OBS_MEDIA_STATE_ENDED, + Error => OBS_MEDIA_STATE_ERROR, +}); diff --git a/src/media/video.rs b/src/media/video.rs index 849846a..2c28079 100644 --- a/src/media/video.rs +++ b/src/media/video.rs @@ -1,5 +1,3 @@ -use std::mem; - use obs_sys::{ obs_source_frame, video_data, video_format, video_format_VIDEO_FORMAT_AYUV, video_format_VIDEO_FORMAT_BGR3, video_format_VIDEO_FORMAT_BGRA, video_format_VIDEO_FORMAT_BGRX, @@ -13,98 +11,57 @@ use obs_sys::{ video_output_get_width, video_t, }; -#[derive(Debug, Clone, Copy, Eq)] -pub enum VideoFormat { - Unknown, - None, +use crate::native_enum; + +native_enum!(VideoFormat, video_format { + None => VIDEO_FORMAT_NONE, /// planar 4:2:0 formats, three-plane - I420, + I420 => VIDEO_FORMAT_I420, /// planar 4:2:0 formats, two-plane, luma and packed chroma - NV12, + NV12 => VIDEO_FORMAT_NV12, /// packed 4:2:2 formats - YVYU, + YVYU => VIDEO_FORMAT_YVYU, /// packed 4:2:2 formats, YUYV - YUY2, + YUY2 => VIDEO_FORMAT_YUY2, /// packed 4:2:2 formats - UYVY, + UYVY => VIDEO_FORMAT_UYVY, /// packed uncompressed formats - RGBA, + RGBA => VIDEO_FORMAT_RGBA, /// packed uncompressed formats - BGRA, + BGRA => VIDEO_FORMAT_BGRA, /// packed uncompressed formats - BGRX, + BGRX => VIDEO_FORMAT_BGRX, /// packed uncompressed formats, grayscale - Y800, + Y800 => VIDEO_FORMAT_Y800, /// planar 4:4:4 - I444, + I444 => VIDEO_FORMAT_I444, /// more packed uncompressed formats - BGR3, + BGR3 => VIDEO_FORMAT_BGR3, /// planar 4:2:2 - I422, + I422 => VIDEO_FORMAT_I422, /// planar 4:2:0 with alpha - I40A, + I40A => VIDEO_FORMAT_I40A, /// planar 4:2:2 with alpha - I42A, + I42A => VIDEO_FORMAT_I42A, /// planar 4:4:4 with alpha - YUVA, + YUVA => VIDEO_FORMAT_YUVA, /// packed 4:4:4 with alpha - AYUV, + AYUV => VIDEO_FORMAT_AYUV, /// planar 4:2:0 format, 10 bpp, three-plane - I010, + I010 => VIDEO_FORMAT_I010, /// planar 4:2:0 format, 10 bpp, two-plane, luma and packed chroma - P010, + P010 => VIDEO_FORMAT_P010, /// planar 4:2:2 10 bits, Little Endian - I210, + I210 => VIDEO_FORMAT_I210, /// planar 4:4:4 12 bits, Little Endian - I412, + I412 => VIDEO_FORMAT_I412, /// planar 4:4:4 12 bits with alpha, Little Endian - YA2L, -} - -impl PartialEq for VideoFormat { - fn eq(&self, other: &Self) -> bool { - if matches!(self, VideoFormat::Unknown) || matches!(other, VideoFormat::Unknown) { - false - } else { - mem::discriminant(self) == mem::discriminant(other) - } - } -} - -impl From for VideoFormat { - #[allow(non_upper_case_globals)] - fn from(raw: video_format) -> Self { - match raw { - video_format_VIDEO_FORMAT_NONE => VideoFormat::None, - video_format_VIDEO_FORMAT_I420 => VideoFormat::I420, - video_format_VIDEO_FORMAT_NV12 => VideoFormat::NV12, - video_format_VIDEO_FORMAT_YVYU => VideoFormat::YVYU, - video_format_VIDEO_FORMAT_YUY2 => VideoFormat::YUY2, - video_format_VIDEO_FORMAT_UYVY => VideoFormat::UYVY, - video_format_VIDEO_FORMAT_RGBA => VideoFormat::RGBA, - video_format_VIDEO_FORMAT_BGRA => VideoFormat::BGRA, - video_format_VIDEO_FORMAT_BGRX => VideoFormat::BGRX, - video_format_VIDEO_FORMAT_Y800 => VideoFormat::Y800, - video_format_VIDEO_FORMAT_I444 => VideoFormat::I444, - video_format_VIDEO_FORMAT_BGR3 => VideoFormat::BGR3, - video_format_VIDEO_FORMAT_I422 => VideoFormat::I422, - video_format_VIDEO_FORMAT_I40A => VideoFormat::I40A, - video_format_VIDEO_FORMAT_I42A => VideoFormat::I42A, - video_format_VIDEO_FORMAT_YUVA => VideoFormat::YUVA, - video_format_VIDEO_FORMAT_AYUV => VideoFormat::AYUV, - video_format_VIDEO_FORMAT_I010 => VideoFormat::I010, - video_format_VIDEO_FORMAT_P010 => VideoFormat::P010, - video_format_VIDEO_FORMAT_I210 => VideoFormat::I210, - video_format_VIDEO_FORMAT_I412 => VideoFormat::I412, - video_format_VIDEO_FORMAT_YA2L => VideoFormat::YA2L, - _ => VideoFormat::Unknown, - } - } -} + YA2L => VIDEO_FORMAT_YA2L, +}); pub struct VideoDataSourceContext { pointer: *mut obs_source_frame, @@ -115,10 +72,10 @@ impl VideoDataSourceContext { Self { pointer } } - pub fn format(&self) -> VideoFormat { + pub fn format(&self) -> Option { let raw = unsafe { (*self.pointer).format }; - VideoFormat::from(raw) + VideoFormat::from_raw(raw).ok() } pub fn width(&self) -> u32 { @@ -170,7 +127,7 @@ pub struct VideoInfo { pub width: u32, pub height: u32, pub frame_rate: f64, - pub format: VideoFormat, + pub format: Option, } pub enum FrameSize { @@ -193,10 +150,10 @@ impl VideoInfo { let full_size = width * height; let half_size = half_width * height; let quarter_size = half_width * half_height; - if self.format == VideoFormat::Unknown { + let Some(format) = self.format else { return FrameSize::Unknown; }; - match self.format { + match format { VideoFormat::None => FrameSize::Planes { size: 0, count: 0 }, I420 => FrameSize::ThreePlane(full_size, quarter_size, quarter_size), NV12 => FrameSize::TwoPlane(full_size, half_size * 2), @@ -226,7 +183,6 @@ impl VideoInfo { }, I010 => FrameSize::ThreePlane(full_size * 2, quarter_size * 2, quarter_size * 2), P010 => FrameSize::TwoPlane(full_size * 2, quarter_size * 4), - Unknown => FrameSize::Unknown, } } } @@ -263,9 +219,9 @@ impl VideoRef { unsafe { video_output_get_frame_rate(self.pointer) } } - pub fn format(&self) -> VideoFormat { + pub fn format(&self) -> Option { let raw = unsafe { video_output_get_format(self.pointer) }; - VideoFormat::from(raw) + VideoFormat::from_raw(raw).ok() } } diff --git a/src/native_enum.rs b/src/native_enum.rs index 08d8015..6f28c22 100644 --- a/src/native_enum.rs +++ b/src/native_enum.rs @@ -24,32 +24,46 @@ impl std::error::Error for NativeParsingError {} #[macro_export] macro_rules! native_enum { - ($name:ident,$native_name:ident { $($rust:ident => $native:ident),* }) => { + ($(#[$($attrs_enum:tt)*])* $name:ident,$native_name:ident { $($(#[$($attrss:tt)*])* $rust:ident => $native:ident,)* }) => { paste::item! { - #[derive(Debug, Clone, Copy, Eq, PartialEq)] + $(#[$($attrs_enum)*])* + #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum $name { - $($rust),* + $( + $(#[$($attrss)*])* + $rust, + )* } - #[allow(clippy::from_over_into)] - impl Into<$native_name> for $name { - fn into(self) -> obs_text_type { + impl $name { + pub fn as_raw(&self) -> $native_name { match self { - $(Self::$rust => [<$native_name _ $native>]),* + $(Self::$rust => [<$native_name _ $native>],)* } } - } - impl std::convert::TryFrom<$native_name> for $name { - type Error = $crate::native_enum::NativeParsingError; #[allow(non_upper_case_globals)] - fn try_from(value: $native_name) -> Result { + pub fn from_raw(value: $native_name) -> Result { match value { $([<$native_name _ $native>] => Ok(Self::$rust)),*, _ => Err($crate::native_enum::NativeParsingError::new(stringify!($name), value as i64)) } } } + + #[allow(clippy::from_over_into)] + impl Into<$native_name> for $name { + fn into(self) -> $native_name { + self.as_raw() + } + } + + impl std::convert::TryFrom<$native_name> for $name { + type Error = $crate::native_enum::NativeParsingError; + fn try_from(value: $native_name) -> Result { + Self::from_raw(value) + } + } } }; } diff --git a/src/properties.rs b/src/properties.rs index 91de4a4..9a2de81 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -29,32 +29,32 @@ use std::{marker::PhantomData, ops::RangeBounds, os::raw::c_int}; native_enum!(TextType, obs_text_type { Default => OBS_TEXT_DEFAULT, Password => OBS_TEXT_PASSWORD, - Multiline => OBS_TEXT_MULTILINE + Multiline => OBS_TEXT_MULTILINE, }); native_enum!(PathType, obs_path_type { File => OBS_PATH_FILE, FileSave => OBS_PATH_FILE_SAVE, - Directory => OBS_PATH_DIRECTORY + Directory => OBS_PATH_DIRECTORY, }); native_enum!(ComboFormat, obs_combo_format { Invalid => OBS_COMBO_FORMAT_INVALID, Int => OBS_COMBO_FORMAT_INT, Float => OBS_COMBO_FORMAT_FLOAT, - String => OBS_COMBO_FORMAT_STRING + String => OBS_COMBO_FORMAT_STRING, }); native_enum!(ComboType, obs_combo_type { Invalid => OBS_COMBO_TYPE_INVALID, Editable => OBS_COMBO_TYPE_EDITABLE, - List => OBS_COMBO_TYPE_LIST + List => OBS_COMBO_TYPE_LIST, }); native_enum!(EditableListType, obs_editable_list_type { Strings => OBS_EDITABLE_LIST_TYPE_STRINGS, Files => OBS_EDITABLE_LIST_TYPE_FILES, - FilesAndUrls => OBS_EDITABLE_LIST_TYPE_FILES_AND_URLS + FilesAndUrls => OBS_EDITABLE_LIST_TYPE_FILES_AND_URLS, }); /// Wrapper around [`obs_properties_t`], which is used by diff --git a/src/source/ffi.rs b/src/source/ffi.rs index 534ed35..0df4cb2 100644 --- a/src/source/ffi.rs +++ b/src/source/ffi.rs @@ -218,7 +218,7 @@ pub unsafe extern "C" fn media_get_state( data: *mut std::os::raw::c_void, ) -> obs_media_state { let wrapper = &mut *(data as *mut DataWrapper); - D::get_state(&mut wrapper.data).to_native() + D::get_state(&mut wrapper.data).as_raw() } pub unsafe extern "C" fn media_set_time( diff --git a/src/source/mod.rs b/src/source/mod.rs index 42a859b..b7cb277 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -36,8 +36,8 @@ use obs_sys::{ obs_source_set_name, obs_source_showing, obs_source_skip_video_filter, obs_source_t, obs_source_type, obs_source_type_OBS_SOURCE_TYPE_FILTER, obs_source_type_OBS_SOURCE_TYPE_INPUT, obs_source_type_OBS_SOURCE_TYPE_SCENE, obs_source_type_OBS_SOURCE_TYPE_TRANSITION, - obs_source_update, obs_text_type, OBS_SOURCE_AUDIO, OBS_SOURCE_CONTROLLABLE_MEDIA, - OBS_SOURCE_INTERACTION, OBS_SOURCE_VIDEO, + obs_source_update, OBS_SOURCE_AUDIO, OBS_SOURCE_CONTROLLABLE_MEDIA, OBS_SOURCE_INTERACTION, + OBS_SOURCE_VIDEO, }; use super::{ @@ -53,7 +53,7 @@ use std::{ffi::CString, marker::PhantomData}; native_enum!(MouseButton, obs_mouse_button_type { Left => MOUSE_LEFT, Middle => MOUSE_MIDDLE, - Right => MOUSE_RIGHT + Right => MOUSE_RIGHT, }); native_enum!(Icon, obs_icon_type { @@ -70,7 +70,7 @@ native_enum!(Icon, obs_icon_type { Text => OBS_ICON_TYPE_TEXT, Media => OBS_ICON_TYPE_MEDIA, Browser => OBS_ICON_TYPE_BROWSER, - Custom => OBS_ICON_TYPE_CUSTOM + Custom => OBS_ICON_TYPE_CUSTOM, }); /// OBS source type @@ -253,7 +253,7 @@ impl SourceRef { pub fn media_state(&self) -> MediaState { let ret = unsafe { obs_source_media_get_state(self.inner) }; - MediaState::from_native(ret).expect("Invalid media state value") + MediaState::from_raw(ret).expect("Invalid media state value") } pub fn media_started(&mut self) { From 34a53b245314ffa8b465863dba3063025707de26 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sun, 7 Apr 2024 06:39:11 +0800 Subject: [PATCH 15/18] rename SourceType --- plugins/rnnoise-denoiser-filter/src/lib.rs | 2 +- plugins/scroll-focus-filter/src/lib.rs | 2 +- src/source/mod.rs | 50 +++++----------------- 3 files changed, 13 insertions(+), 41 deletions(-) diff --git a/plugins/rnnoise-denoiser-filter/src/lib.rs b/plugins/rnnoise-denoiser-filter/src/lib.rs index ea52772..932d185 100644 --- a/plugins/rnnoise-denoiser-filter/src/lib.rs +++ b/plugins/rnnoise-denoiser-filter/src/lib.rs @@ -37,7 +37,7 @@ impl Sourceable for RnnoiseDenoiserFilter { obs_string!("rnnoise_noise_suppression_filter") } fn get_type() -> SourceType { - SourceType::FILTER + SourceType::Filter } fn create(create: &mut CreatableSourceContext, _source: SourceRef) -> Self { let (sample_rate, channels) = diff --git a/plugins/scroll-focus-filter/src/lib.rs b/plugins/scroll-focus-filter/src/lib.rs index 6132b49..f085de4 100644 --- a/plugins/scroll-focus-filter/src/lib.rs +++ b/plugins/scroll-focus-filter/src/lib.rs @@ -68,7 +68,7 @@ impl Sourceable for ScrollFocusFilter { obs_string!("scroll_focus_filter") } fn get_type() -> SourceType { - SourceType::FILTER + SourceType::Filter } fn create(create: &mut CreatableSourceContext, mut source: SourceRef) -> Self { let mut effect = GraphicsEffect::from_effect_string( diff --git a/src/source/mod.rs b/src/source/mod.rs index b7cb277..bdf14a7 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -73,38 +73,16 @@ native_enum!(Icon, obs_icon_type { Custom => OBS_ICON_TYPE_CUSTOM, }); +native_enum!( /// OBS source type /// /// See [OBS documentation](https://obsproject.com/docs/reference-sources.html#c.obs_source_get_type) -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum SourceType { - INPUT, - SCENE, - FILTER, - TRANSITION, -} - -impl SourceType { - #[allow(non_upper_case_globals)] - pub(crate) fn from_native(source_type: obs_source_type) -> Option { - match source_type { - obs_source_type_OBS_SOURCE_TYPE_INPUT => Some(SourceType::INPUT), - obs_source_type_OBS_SOURCE_TYPE_SCENE => Some(SourceType::SCENE), - obs_source_type_OBS_SOURCE_TYPE_FILTER => Some(SourceType::FILTER), - obs_source_type_OBS_SOURCE_TYPE_TRANSITION => Some(SourceType::TRANSITION), - _ => None, - } - } - - pub(crate) fn to_native(self) -> obs_source_type { - match self { - SourceType::INPUT => obs_source_type_OBS_SOURCE_TYPE_INPUT, - SourceType::SCENE => obs_source_type_OBS_SOURCE_TYPE_SCENE, - SourceType::FILTER => obs_source_type_OBS_SOURCE_TYPE_FILTER, - SourceType::TRANSITION => obs_source_type_OBS_SOURCE_TYPE_TRANSITION, - } - } -} +SourceType, obs_source_type { + Input => OBS_SOURCE_TYPE_INPUT, + Scene => OBS_SOURCE_TYPE_SCENE, + Filter => OBS_SOURCE_TYPE_FILTER, + Transition => OBS_SOURCE_TYPE_TRANSITION, +}); #[deprecated = "use `SourceRef` instead"] pub type SourceContext = SourceRef; @@ -146,9 +124,7 @@ impl SourceRef { /// Note: only works with sources that are filters. pub fn do_with_target(&mut self, func: F) { unsafe { - if let Some(SourceType::FILTER) = - SourceType::from_native(obs_source_get_type(self.inner)) - { + if let Ok(SourceType::Filter) = SourceType::from_raw(obs_source_get_type(self.inner)) { let target = obs_filter_get_target(self.inner); if let Some(mut context) = SourceRef::from_raw(target) { func(&mut context); @@ -292,9 +268,7 @@ impl SourceRef { func: F, ) { unsafe { - if let Some(SourceType::FILTER) = - SourceType::from_native(obs_source_get_type(self.inner)) - { + if let Ok(SourceType::Filter) = SourceType::from_raw(obs_source_get_type(self.inner)) { if obs_source_process_filter_begin(self.inner, format.as_raw(), direct.as_raw()) { let mut context = GraphicsEffectContext::new(); func(&mut context, effect); @@ -316,9 +290,7 @@ impl SourceRef { func: F, ) { unsafe { - if let Some(SourceType::FILTER) = - SourceType::from_native(obs_source_get_type(self.inner)) - { + if let Ok(SourceType::Filter) = SourceType::from_raw(obs_source_get_type(self.inner)) { if obs_source_process_filter_begin(self.inner, format.as_raw(), direct.as_raw()) { let mut context = GraphicsEffectContext::new(); func(&mut context, effect); @@ -396,7 +368,7 @@ impl SourceInfoBuilder { __data: PhantomData, info: obs_source_info { id: D::get_id().as_ptr(), - type_: D::get_type().to_native(), + type_: D::get_type().as_raw(), create: Some(ffi::create::), destroy: Some(ffi::destroy::), type_data: std::ptr::null_mut(), From 48f58a9fc8642b777f9f5d96137de8920fbcaaca Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Sun, 7 Apr 2024 06:45:26 +0800 Subject: [PATCH 16/18] add color to display --- src/graphics/display.rs | 97 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/src/graphics/display.rs b/src/graphics/display.rs index a3c5f56..7786cfc 100644 --- a/src/graphics/display.rs +++ b/src/graphics/display.rs @@ -1,8 +1,88 @@ use obs_sys::{ obs_display_add_draw_callback, obs_display_destroy, obs_display_enabled, - obs_display_remove_draw_callback, obs_display_resize, obs_display_t, obs_render_main_texture, + obs_display_remove_draw_callback, obs_display_resize, obs_display_set_background_color, + obs_display_set_enabled, obs_display_size, obs_display_t, obs_render_main_texture, }; +use super::GraphicsColorFormat; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +fn srgb_nonlinear_to_linear(c: f32) -> f32 { + if c <= 0.04045 { + c / 12.92 + } else { + ((c + 0.055) / 1.055).powf(2.4) + } +} +fn srgb_linear_to_nonlinear(c: f32) -> f32 { + if c <= 0.0031308 { + c * 12.92 + } else { + 1.055 * c.powf(1.0 / 2.4) - 0.055 + } +} +impl Color { + pub const BLACK: Color = Color::new(0, 0, 0, 255); + pub const WHITE: Color = Color::new(255, 255, 255, 255); + pub const RED: Color = Color::new(255, 0, 0, 255); + pub const GREEN: Color = Color::new(0, 255, 0, 255); + pub const BLUE: Color = Color::new(0, 0, 255, 255); + + pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self { + Color { r, g, b, a } + } + + pub fn as_format(self, format: GraphicsColorFormat) -> u32 { + match format { + GraphicsColorFormat::RGBA => self.as_rgba(), + GraphicsColorFormat::BGRA => self.as_bgra(), + _ => unimplemented!("unsupported color format"), + } + } + + pub fn as_rgba(self) -> u32 { + u32::from_ne_bytes([self.r, self.g, self.b, self.a]) + } + + pub fn as_bgra(self) -> u32 { + u32::from_ne_bytes([self.b, self.g, self.r, self.a]) + } + + /// gs_float3_srgb_nonlinear_to_linear + pub fn srgb_nonlinear_to_linear(self) -> Self { + let r = srgb_nonlinear_to_linear(self.r as f32 / 255.0); + let g = srgb_nonlinear_to_linear(self.g as f32 / 255.0); + let b = srgb_nonlinear_to_linear(self.b as f32 / 255.0); + Color { + r: (r * 255.0) as u8, + g: (g * 255.0) as u8, + b: (b * 255.0) as u8, + a: self.a, + } + } + + pub fn srgb_linear_to_nonlinear(self) -> Self { + let r = srgb_linear_to_nonlinear(self.r as f32 / 255.0); + let g = srgb_linear_to_nonlinear(self.g as f32 / 255.0); + let b = srgb_linear_to_nonlinear(self.b as f32 / 255.0); + Color { + r: (r * 255.0) as u8, + g: (g * 255.0) as u8, + b: (b * 255.0) as u8, + a: self.a, + } + } +} + +/// A reference to a display, inner pointer is not managed by reference count. +/// So no `Clone` is implemented. you might want to use `Arc` if you need to clone it. pub struct DisplayRef { inner: *mut obs_display_t, } @@ -14,10 +94,25 @@ impl DisplayRef { unsafe { obs_display_enabled(self.inner) } } + pub fn set_enabled(&self, enabled: bool) { + unsafe { obs_display_set_enabled(self.inner, enabled) } + } + + pub fn size(&self) -> (u32, u32) { + let mut cx = 0; + let mut cy = 0; + unsafe { obs_display_size(self.inner, &mut cx, &mut cy) } + (cx, cy) + } + pub fn set_size(&self, cx: u32, cy: u32) { unsafe { obs_display_resize(self.inner, cx, cy) } } + pub fn set_background_color(&self, color: Color) { + unsafe { obs_display_set_background_color(self.inner, color.as_rgba()) } + } + pub fn add_draw_callback(&self, callback: S) -> DrawCallbackId<'_, S> { let data = Box::into_raw(Box::new(callback)); // force the pointer to be a function pointer, since it is not garantueed to be the same pointer From 6dc1878610a8db1cb8c5645b0b8982d961a3429d Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Tue, 9 Apr 2024 14:43:35 +0800 Subject: [PATCH 17/18] fix test --- README.md | 2 +- src/lib.rs | 4 ++-- src/module.rs | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 849e533..06f9bbe 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ impl Sourceable for TestSource { } fn get_type() -> SourceType { - SourceType::FILTER + SourceType::Filter } fn create(create: &mut CreatableSourceContext, source: SourceContext) -> Self { diff --git a/src/lib.rs b/src/lib.rs index 0880724..9cb355f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,12 +52,12 @@ //! } //! //! fn get_type() -> SourceType { -//! SourceType::FILTER +//! SourceType::Filter //! } //! //! fn create( //! create: &mut CreatableSourceContext, -//! _source: SourceContext +//! _source: SourceRef //! ) -> Self { //! Self //! } diff --git a/src/module.rs b/src/module.rs index 8a85926..7450959 100644 --- a/src/module.rs +++ b/src/module.rs @@ -152,6 +152,9 @@ macro_rules! obs_register_module { }; } +#[deprecated = "use `ModuleRef` instead"] +pub type ModuleContext = ModuleRef; + pub struct ModuleRef { raw: *mut obs_module_t, } From 9f13699763909cea621abd185eb9eca79dd6ca83 Mon Sep 17 00:00:00 2001 From: Clouds Flowing Date: Tue, 9 Apr 2024 15:36:31 +0800 Subject: [PATCH 18/18] apply output --- src/output/context.rs | 67 ++++++++++++++----------------------------- src/output/ffi.rs | 13 +++++---- src/output/traits.rs | 4 +-- src/source/ffi.rs | 8 ++++-- src/source/mod.rs | 1 + src/wrapper.rs | 4 +-- 6 files changed, 40 insertions(+), 57 deletions(-) diff --git a/src/output/context.rs b/src/output/context.rs index 9cbbf00..e529587 100644 --- a/src/output/context.rs +++ b/src/output/context.rs @@ -14,38 +14,28 @@ use obs_sys::{ use crate::hotkey::HotkeyCallbacks; use crate::media::{audio::AudioRef, video::VideoRef}; +use crate::string::TryIntoObsString; use crate::{hotkey::Hotkey, prelude::DataObj, string::ObsString, wrapper::PtrWrapper}; +use crate::{Error, Result}; + +#[deprecated = "use `OutputRef` instead"] +pub type OutputContext = OutputRef; /// Context wrapping an OBS output - video / audio elements which are displayed /// to the screen. /// /// See [OBS documentation](https://obsproject.com/docs/reference-outputs.html#c.obs_output_t) -pub struct OutputContext { +pub struct OutputRef { pub(crate) inner: *mut obs_output_t, } -impl OutputContext { - /// # Safety - /// - /// Pointer must be valid. - pub unsafe fn from_raw(output: *mut obs_output_t) -> Self { - Self { - inner: obs_output_get_ref(output), - } - } -} - -impl Clone for OutputContext { - fn clone(&self) -> Self { - unsafe { Self::from_raw(self.inner) } - } -} - -impl Drop for OutputContext { - fn drop(&mut self) { - unsafe { obs_output_release(self.inner) } - } -} +impl_ptr_wrapper!( + @ptr: inner, + OutputRef, + obs_output_t, + obs_output_get_ref, + obs_output_release +); extern "C" fn enum_proc(params: *mut std::ffi::c_void, output: *mut obs_output_t) -> bool { let mut v = unsafe { Box::>::from_raw(params as *mut _) }; @@ -54,8 +44,8 @@ extern "C" fn enum_proc(params: *mut std::ffi::c_void, output: *mut obs_output_t true } -impl OutputContext { - pub fn new(id: ObsString, name: ObsString, settings: Option>) -> Self { +impl OutputRef { + pub fn new(id: ObsString, name: ObsString, settings: Option>) -> Result { let settings = match settings { Some(data) => unsafe { data.as_ptr_mut() }, None => std::ptr::null_mut(), @@ -64,18 +54,19 @@ impl OutputContext { obs_output_create(id.as_ptr(), name.as_ptr(), settings, std::ptr::null_mut()) }; - unsafe { Self::from_raw(output) } + unsafe { Self::from_raw_unchecked(output) }.ok_or(Error::NulPointer("obs_output_cretae")) } pub fn all_outputs() -> Vec { let outputs = Vec::<*mut obs_output_t>::new(); let params = Box::into_raw(Box::new(outputs)); unsafe { + // `obs_enum_outputs` would return `weak_ref`, so `get_ref` needed obs_enum_outputs(Some(enum_proc), params as *mut _); } let outputs = unsafe { Box::from_raw(params) }; outputs .into_iter() - .map(|i| unsafe { OutputContext::from_raw(i) }) + .filter_map(OutputRef::from_raw) .collect() } pub fn all_types() -> Vec { @@ -96,26 +87,12 @@ impl OutputContext { types } - pub fn output_id(&self) -> Option<&str> { - unsafe { - let ptr = obs_output_get_id(self.inner); - if ptr.is_null() { - None - } else { - Some(CStr::from_ptr(ptr).to_str().unwrap()) - } - } + pub fn output_id(&self) -> Result { + unsafe { obs_output_get_id(self.inner) }.try_into_obs_string() } - pub fn name(&self) -> Option<&str> { - unsafe { - let ptr = obs_output_get_name(self.inner); - if ptr.is_null() { - None - } else { - Some(CStr::from_ptr(ptr).to_str().unwrap()) - } - } + pub fn name(&self) -> Result { + unsafe { obs_output_get_name(self.inner) }.try_into_obs_string() } pub fn start(&mut self) -> bool { diff --git a/src/output/ffi.rs b/src/output/ffi.rs index 0b674ce..84ae395 100644 --- a/src/output/ffi.rs +++ b/src/output/ffi.rs @@ -1,4 +1,4 @@ -use super::{traits::*, CreatableOutputContext, OutputContext}; +use super::{traits::*, CreatableOutputContext, OutputRef}; use crate::hotkey::{Hotkey, HotkeyCallbacks}; use crate::{data::DataObj, wrapper::PtrWrapper}; use obs_sys::{ @@ -53,9 +53,10 @@ pub unsafe extern "C" fn create( settings: *mut obs_data_t, output: *mut obs_output_t, ) -> *mut c_void { - let settings = DataObj::from_raw(settings).unwrap(); + // this is later forgotten + let settings = DataObj::from_raw_unchecked(settings).unwrap(); let mut context = CreatableOutputContext::from_raw(settings); - let output_context = OutputContext::from_raw(output); + let output_context = OutputRef::from_raw(output).expect("create"); let data = D::create(&mut context, output_context); let wrapper = Box::new(DataWrapper::from(data)); @@ -129,13 +130,15 @@ pub unsafe extern "C" fn encoded_packet( pub unsafe extern "C" fn update(data: *mut c_void, settings: *mut obs_data_t) { let data: &mut DataWrapper = &mut *(data as *mut DataWrapper); - let mut settings = DataObj::from_raw(settings).unwrap(); + // this is later forgotten + let mut settings = DataObj::from_raw_unchecked(settings).unwrap(); D::update(&mut data.data, &mut settings); forget(settings); } pub unsafe extern "C" fn get_defaults(settings: *mut obs_data_t) { - let mut settings = DataObj::from_raw(settings).unwrap(); + // this is later forgotten + let mut settings = DataObj::from_raw_unchecked(settings).unwrap(); D::get_defaults(&mut settings); forget(settings); } diff --git a/src/output/traits.rs b/src/output/traits.rs index 4382833..1d0ef93 100644 --- a/src/output/traits.rs +++ b/src/output/traits.rs @@ -2,11 +2,11 @@ use obs_sys::{audio_data, encoder_packet, video_data}; use crate::{prelude::DataObj, properties::Properties, string::ObsString}; -use super::{CreatableOutputContext, OutputContext}; +use super::{CreatableOutputContext, OutputRef}; pub trait Outputable: Sized { fn get_id() -> ObsString; - fn create(context: &mut CreatableOutputContext<'_, Self>, output: OutputContext) -> Self; + fn create(context: &mut CreatableOutputContext<'_, Self>, output: OutputRef) -> Self; fn start(&mut self) -> bool { true diff --git a/src/source/ffi.rs b/src/source/ffi.rs index 0df4cb2..1e148e7 100644 --- a/src/source/ffi.rs +++ b/src/source/ffi.rs @@ -87,7 +87,8 @@ pub unsafe extern "C" fn create( source: *mut obs_source_t, ) -> *mut c_void { let mut global = GlobalContext; - let settings = DataObj::from_raw(settings).unwrap(); + // this is later forgotten + let settings = DataObj::from_raw_unchecked(settings).unwrap(); let mut context = CreatableSourceContext::from_raw(settings, &mut global); let source_context = SourceRef::from_raw(source).expect("create"); @@ -115,7 +116,7 @@ pub unsafe extern "C" fn destroy(data: *mut c_void) { pub unsafe extern "C" fn update(data: *mut c_void, settings: *mut obs_data_t) { let mut global = GlobalContext; let data: &mut DataWrapper = &mut *(data as *mut DataWrapper); - let mut settings = DataObj::from_raw(settings).unwrap(); + let mut settings = DataObj::from_raw_unchecked(settings).unwrap(); D::update(&mut data.data, &mut settings, &mut global); forget(settings); } @@ -252,7 +253,8 @@ impl_media!( ); pub unsafe extern "C" fn get_defaults(settings: *mut obs_data_t) { - let mut settings = DataObj::from_raw(settings).unwrap(); + // this is later forgotten + let mut settings = DataObj::from_raw_unchecked(settings).unwrap(); D::get_defaults(&mut settings); forget(settings); } diff --git a/src/source/mod.rs b/src/source/mod.rs index bdf14a7..40f02dd 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -125,6 +125,7 @@ impl SourceRef { pub fn do_with_target(&mut self, func: F) { unsafe { if let Ok(SourceType::Filter) = SourceType::from_raw(obs_source_get_type(self.inner)) { + // doc says "Does not increment the reference." let target = obs_filter_get_target(self.inner); if let Some(mut context) = SourceRef::from_raw(target) { func(&mut context); diff --git a/src/wrapper.rs b/src/wrapper.rs index 2348188..098afe4 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -8,8 +8,8 @@ pub trait PtrWrapperInternal: PtrWrapper { unsafe fn new_internal(ptr: *mut Self::Pointer) -> Self; /// # Safety /// - /// This function should not be called directly, use `from_raw` and - /// `from_raw_unchecked` instead. + /// This function should not be called directly, use `as_ptr`, + /// `as_ptr_mut` and `into_raw` instead. unsafe fn get_internal(&self) -> *mut Self::Pointer; }