libobs_wrapper/
services.rs1use std::{
8 ffi::CStr,
9 sync::{Arc, RwLock},
10};
11
12use crate::{
13 data::{
14 object::{inner_fn_update_settings, ObsObjectTrait, ObsObjectTraitPrivate},
15 ImmutableObsData, ObsDataPointers,
16 },
17 impl_obs_drop,
18 macros::impl_eq_of_obs_object,
19 run_with_obs,
20 runtime::ObsRuntime,
21 unsafe_send::{Sendable, SmartPointerSendable},
22 utils::{ObjectInfo, ObsDropGuard, ObsError, ObsString},
23};
24
25#[derive(Debug)]
26struct _ObsServiceDropGuard {
27 service: Sendable<*mut libobs::obs_service_t>,
28 runtime: ObsRuntime,
29}
30
31impl ObsDropGuard for _ObsServiceDropGuard {}
32
33impl_obs_drop!(_ObsServiceDropGuard, (service), move || unsafe {
34 libobs::obs_service_release(service.0);
36});
37
38#[derive(Clone, Debug)]
40pub struct ObsServiceRef {
41 id: ObsString,
42 name: ObsString,
43 settings: Arc<RwLock<ImmutableObsData>>,
44 hotkey_data: Arc<RwLock<ImmutableObsData>>,
45 runtime: ObsRuntime,
46 service: SmartPointerSendable<*mut libobs::obs_service_t>,
47}
48
49impl ObsServiceRef {
50 pub fn protocol(&self) -> Result<Option<String>, ObsError> {
52 let service = self.__native_handle();
53 run_with_obs!(self.runtime, (service), move || {
54 let protocol = unsafe { libobs::obs_service_get_protocol(service.get_ptr()) };
56 if protocol.is_null() {
57 None
58 } else {
59 Some(
61 unsafe { CStr::from_ptr(protocol) }
62 .to_string_lossy()
63 .into_owned(),
64 )
65 }
66 })
67 }
68
69 pub(crate) fn new_from_info(
70 info: ObjectInfo,
71 runtime: ObsRuntime,
72 ) -> Result<Arc<Self>, ObsError> {
73 let ObjectInfo {
74 id,
75 name,
76 settings,
77 hotkey_data,
78 } = info;
79
80 let settings_ptr = settings.as_ref().map(ObsDataPointers::as_ptr);
81 let hotkey_data_ptr = hotkey_data.as_ref().map(ObsDataPointers::as_ptr);
82
83 let service = run_with_obs!(
84 runtime,
85 (id, name, settings_ptr, hotkey_data_ptr),
86 move || {
87 let settings_raw = settings_ptr
88 .as_ref()
89 .map_or(std::ptr::null_mut(), SmartPointerSendable::get_ptr);
90 let hotkey_raw = hotkey_data_ptr
91 .as_ref()
92 .map_or(std::ptr::null_mut(), SmartPointerSendable::get_ptr);
93
94 let ptr = unsafe {
97 libobs::obs_service_create(
98 id.as_ptr().0,
99 name.as_ptr().0,
100 settings_raw,
101 hotkey_raw,
102 )
103 };
104 if ptr.is_null() {
105 Err(ObsError::NullPointer(Some("OBS service creation".into())))
106 } else {
107 Ok(Sendable(ptr))
108 }
109 }
110 )??;
111
112 let service = SmartPointerSendable::new(
113 service.0,
114 Arc::new(_ObsServiceDropGuard {
115 service: service.clone(),
116 runtime: runtime.clone(),
117 }),
118 runtime.native_registry(),
119 );
120
121 let current_settings = run_with_obs!(runtime, (service), move || {
122 let ptr = unsafe { libobs::obs_service_get_settings(service.get_ptr()) };
124 if ptr.is_null() {
125 Err(ObsError::NullPointer(Some("OBS service settings".into())))
126 } else {
127 Ok(Sendable(ptr))
128 }
129 })??;
130 let settings = ImmutableObsData::from_raw_pointer(current_settings, runtime.clone());
131
132 let hotkey_data = match hotkey_data {
133 Some(data) => data,
134 None => ImmutableObsData::new(&runtime)?,
135 };
136
137 Ok(Arc::new(Self {
138 id,
139 name,
140 settings: Arc::new(RwLock::new(settings)),
141 hotkey_data: Arc::new(RwLock::new(hotkey_data)),
142 runtime,
143 service,
144 }))
145 }
146}
147
148impl ObsObjectTraitPrivate for ObsServiceRef {
149 fn __internal_replace_settings(&self, settings: ImmutableObsData) -> Result<(), ObsError> {
150 *self
151 .settings
152 .write()
153 .map_err(|_| ObsError::LockError("Failed to acquire service settings lock".into()))? =
154 settings;
155 Ok(())
156 }
157
158 fn __internal_replace_hotkey_data(
159 &self,
160 hotkey_data: ImmutableObsData,
161 ) -> Result<(), ObsError> {
162 *self
163 .hotkey_data
164 .write()
165 .map_err(|_| ObsError::LockError("Failed to acquire service hotkey lock".into()))? =
166 hotkey_data;
167 Ok(())
168 }
169}
170
171impl ObsObjectTrait for ObsServiceRef {
172 type Native = *mut libobs::obs_service_t;
173
174 fn runtime(&self) -> &ObsRuntime {
175 &self.runtime
176 }
177
178 fn settings(&self) -> Result<ImmutableObsData, ObsError> {
179 self.settings
180 .read()
181 .map_err(|_| ObsError::LockError("Failed to acquire service settings lock".into()))
182 .map(|settings| settings.clone())
183 }
184
185 fn hotkey_data(&self) -> Result<ImmutableObsData, ObsError> {
186 self.hotkey_data
187 .read()
188 .map_err(|_| ObsError::LockError("Failed to acquire service hotkey lock".into()))
189 .map(|data| data.clone())
190 }
191
192 fn id(&self) -> ObsString {
193 self.id.clone()
194 }
195
196 fn name(&self) -> ObsString {
197 self.name.clone()
198 }
199
200 fn update_settings(&self, settings: crate::data::ObsData) -> Result<(), ObsError> {
201 inner_fn_update_settings!(self, libobs::obs_service_update, settings)
202 }
203
204 fn __native_handle(&self) -> SmartPointerSendable<Self::Native> {
205 self.service.clone()
206 }
207}
208
209impl_eq_of_obs_object!(ObsServiceRef);