mod traits;
pub use traits::SceneItemExtSceneTrait;
use std::{fmt::Debug, hash::Hash, sync::Arc};
use libobs::{obs_scene_item, obs_transform_info, obs_video_info};
use crate::{
enums::ObsBoundsType,
graphics::Vec2,
impl_obs_drop,
macros::trait_with_optional_send_sync,
run_with_obs,
runtime::ObsRuntime,
scenes::{ObsSceneRef, ObsTransformInfo, ObsTransformInfoBuilder},
sources::ObsSourceTrait,
unsafe_send::{Sendable, SmartPointerSendable},
utils::{ObsDropGuard, ObsError},
};
#[derive(Debug)]
pub(super) struct _ObsSceneItemDropGuard {
scene_item: Sendable<*mut obs_scene_item>,
runtime: ObsRuntime,
}
impl ObsDropGuard for _ObsSceneItemDropGuard {}
impl_obs_drop!(_ObsSceneItemDropGuard, (scene_item), move || unsafe {
libobs::obs_sceneitem_remove(scene_item.0);
});
#[derive(Debug, Clone)]
pub struct ObsSceneItemRef<T: ObsSourceTrait + Clone> {
scene_item_ptr: SmartPointerSendable<*mut obs_scene_item>,
runtime: ObsRuntime,
_scene_ptr: SmartPointerSendable<*mut libobs::obs_scene>,
underlying_source: T,
}
impl<T: ObsSourceTrait + Clone> ObsSceneItemRef<T> {
pub(crate) fn new(
scene: &ObsSceneRef,
source: T,
runtime: ObsRuntime,
) -> Result<Self, ObsError> {
let scene_ptr = scene.as_ptr();
let source_ptr = source.as_ptr();
let scene_item_ptr = run_with_obs!(runtime, (scene_ptr, source_ptr), move || {
let ptr = unsafe {
libobs::obs_scene_add(scene_ptr.get_ptr(), source_ptr.get_ptr())
};
if ptr.is_null() {
Err(ObsError::NullPointer(None))
} else {
Ok(Sendable(ptr))
}
})??;
let drop_guard = _ObsSceneItemDropGuard {
scene_item: scene_item_ptr.clone(),
runtime: runtime.clone(),
};
let scene_item_ptr = SmartPointerSendable::new(scene_item_ptr.0, Arc::new(drop_guard));
Ok(Self {
underlying_source: source,
_scene_ptr: scene.as_ptr().clone(),
scene_item_ptr,
runtime,
})
}
}
trait_with_optional_send_sync! {
pub trait SceneItemTrait: Debug {
fn as_ptr(&self) -> &SmartPointerSendable<*mut obs_scene_item>;
fn runtime(&self) -> ObsRuntime;
fn inner_source_dyn(&self) -> &dyn ObsSourceTrait;
fn inner_source_dyn_mut(&mut self) -> &mut dyn ObsSourceTrait;
fn get_transform_info(&self) -> Result<ObsTransformInfo, ObsError> {
let self_ptr = self.as_ptr();
let item_info = run_with_obs!(self.runtime(), (self_ptr), move || {
let mut item_info: obs_transform_info = unsafe {
std::mem::zeroed()
};
unsafe {
libobs::obs_sceneitem_get_info2(self_ptr.get_ptr(), &mut item_info)
};
ObsTransformInfo(item_info)
})?;
Ok(item_info)
}
fn get_source_position(&self) -> Result<Vec2, ObsError> {
let self_ptr = self.as_ptr();
let position = run_with_obs!(self.runtime(), (self_ptr), move || {
let main_pos = unsafe {
let mut main_pos: libobs::vec2 = std::mem::zeroed();
libobs::obs_sceneitem_get_pos(self_ptr.get_ptr(), &mut main_pos);
main_pos
};
Vec2::from(main_pos)
})?;
Ok(position)
}
fn get_source_scale(&self) -> Result<Vec2, ObsError> {
let self_ptr = self.as_ptr();
let scale = run_with_obs!(self.runtime(), (self_ptr), move || {
let main_pos = unsafe {
let mut main_pos: libobs::vec2 = std::mem::zeroed();
libobs::obs_sceneitem_get_scale(self_ptr.get_ptr(), &mut main_pos);
main_pos
};
Vec2::from(main_pos)
})?;
Ok(scale)
}
fn set_source_position(&self, position: Vec2) -> Result<(), ObsError> {
let self_ptr = self.as_ptr();
run_with_obs!(self.runtime(), (self_ptr), move || {
let position: libobs::vec2 = position.into();
unsafe {
libobs::obs_sceneitem_set_pos(self_ptr.get_ptr(), &position);
}
})?;
Ok(())
}
fn set_transform_info(&self, info: &ObsTransformInfo) -> Result<(), ObsError> {
let item_info = Sendable(info.clone());
let self_ptr = self.as_ptr();
run_with_obs!(self.runtime(), (self_ptr, item_info), move || {
let item_info = item_info.0 .0;
unsafe {
libobs::obs_sceneitem_set_info2(self_ptr.get_ptr(), &item_info);
}
})?;
Ok(())
}
fn fit_source_to_screen(&self) -> Result<bool, ObsError> {
let self_ptr = self.as_ptr();
let is_locked = {
run_with_obs!(self.runtime(), (self_ptr), move || unsafe {
libobs::obs_sceneitem_locked(self_ptr.get_ptr())
})?
};
if is_locked {
return Ok(false);
}
let ovi = run_with_obs!(self.runtime(), (), move || {
let mut ovi = std::mem::MaybeUninit::<obs_video_info>::uninit();
let success = unsafe {
libobs::obs_get_video_info(ovi.as_mut_ptr())
};
if success {
let res = unsafe {
ovi.assume_init()
};
Ok(Sendable(res))
} else {
Err(ObsError::NullPointer(Some(
"Failed to get video info".to_string(),
)))
}
})??;
let bounds_crop = run_with_obs!(self.runtime(), (self_ptr), move || {
unsafe {
libobs::obs_sceneitem_get_bounds_crop(self_ptr.get_ptr())
}
})?;
let item_info = ObsTransformInfoBuilder::new()
.set_bounds_type(ObsBoundsType::ScaleInner)
.set_crop_to_bounds(bounds_crop)
.build(ovi.0.base_width, ovi.0.base_height);
self.set_transform_info(&item_info)?;
Ok(true)
}
fn set_source_scale(&self, scale: Vec2) -> Result<(), ObsError> {
let self_ptr = self.as_ptr();
run_with_obs!(self.runtime(), (self_ptr), move || {
let scale: libobs::vec2 = scale.into();
unsafe {
libobs::obs_sceneitem_set_scale(self_ptr.get_ptr(), &scale);
}
})?;
Ok(())
}
}
}
impl<T: ObsSourceTrait + Clone> SceneItemTrait for ObsSceneItemRef<T> {
fn as_ptr(&self) -> &SmartPointerSendable<*mut obs_scene_item> {
&self.scene_item_ptr
}
fn runtime(&self) -> ObsRuntime {
self.runtime.clone()
}
fn inner_source_dyn(&self) -> &dyn ObsSourceTrait {
&self.underlying_source
}
fn inner_source_dyn_mut(&mut self) -> &mut dyn ObsSourceTrait {
&mut self.underlying_source
}
}
impl<T> ObsSceneItemRef<T>
where
T: ObsSourceTrait + Clone,
{
pub fn inner_source(&self) -> &T {
&self.underlying_source
}
pub fn inner_source_mut(&mut self) -> &mut T {
&mut self.underlying_source
}
}
impl<T: ObsSourceTrait + Clone> PartialEq for ObsSceneItemRef<T> {
fn eq(&self, other: &Self) -> bool {
self.scene_item_ptr.get_ptr() == other.scene_item_ptr.get_ptr()
}
}
impl<T: ObsSourceTrait + Clone> Eq for ObsSceneItemRef<T> {}
impl<T: ObsSourceTrait + Clone> Hash for ObsSceneItemRef<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.scene_item_ptr.get_ptr().hash(state);
}
}