1 /******************************************************************************
2     Copyright (C) 2017 by Hugh Bailey <jim@obsproject.com>
3 
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17 
18 #include <obs-module.h>
19 #include <obs-frontend-api.h>
20 
21 #include "obs-scripting-python.h"
22 
23 #define libobs_to_py(type, obs_obj, ownership, py_obj)                        \
24 	libobs_to_py_(#type " *", obs_obj, ownership, py_obj, NULL, __func__, \
25 		      __LINE__)
26 #define py_to_libobs(type, py_obj, libobs_out) \
27 	py_to_libobs_(#type " *", py_obj, libobs_out, NULL, __func__, __LINE__)
28 
29 /* ----------------------------------- */
30 
get_scene_names(PyObject * self,PyObject * args)31 static PyObject *get_scene_names(PyObject *self, PyObject *args)
32 {
33 	char **names = obs_frontend_get_scene_names();
34 	char **name = names;
35 
36 	PyObject *list = PyList_New(0);
37 
38 	while (name && *name) {
39 		PyObject *py_name = PyUnicode_FromString(*name);
40 		if (py_name) {
41 			PyList_Append(list, py_name);
42 			Py_DECREF(py_name);
43 		}
44 		name++;
45 	}
46 
47 	UNUSED_PARAMETER(self);
48 	UNUSED_PARAMETER(args);
49 
50 	bfree(names);
51 	return list;
52 }
53 
get_scenes(PyObject * self,PyObject * args)54 static PyObject *get_scenes(PyObject *self, PyObject *args)
55 {
56 	struct obs_frontend_source_list list = {0};
57 	obs_frontend_get_scenes(&list);
58 
59 	PyObject *ret = PyList_New(0);
60 
61 	for (size_t i = 0; i < list.sources.num; i++) {
62 		obs_source_t *source = list.sources.array[i];
63 		PyObject *py_source;
64 
65 		if (libobs_to_py(obs_source_t, source, false, &py_source)) {
66 			PyList_Append(ret, py_source);
67 			Py_DECREF(py_source);
68 		}
69 	}
70 
71 	UNUSED_PARAMETER(self);
72 	UNUSED_PARAMETER(args);
73 
74 	da_free(list.sources);
75 	return ret;
76 }
77 
get_current_scene(PyObject * self,PyObject * args)78 static PyObject *get_current_scene(PyObject *self, PyObject *args)
79 {
80 	obs_source_t *source = obs_frontend_get_current_scene();
81 
82 	PyObject *py_source;
83 	if (!libobs_to_py(obs_source_t, source, false, &py_source)) {
84 		obs_source_release(source);
85 		return python_none();
86 	}
87 
88 	UNUSED_PARAMETER(self);
89 	UNUSED_PARAMETER(args);
90 	return py_source;
91 }
92 
set_current_scene(PyObject * self,PyObject * args)93 static PyObject *set_current_scene(PyObject *self, PyObject *args)
94 {
95 	PyObject *py_source;
96 	obs_source_t *source = NULL;
97 
98 	if (!parse_args(args, "O", &py_source))
99 		return python_none();
100 	if (!py_to_libobs(obs_source_t, py_source, &source))
101 		return python_none();
102 
103 	UNUSED_PARAMETER(self);
104 
105 	obs_frontend_set_current_scene(source);
106 	return python_none();
107 }
108 
get_transitions(PyObject * self,PyObject * args)109 static PyObject *get_transitions(PyObject *self, PyObject *args)
110 {
111 	struct obs_frontend_source_list list = {0};
112 	obs_frontend_get_transitions(&list);
113 
114 	PyObject *ret = PyList_New(0);
115 
116 	for (size_t i = 0; i < list.sources.num; i++) {
117 		obs_source_t *source = list.sources.array[i];
118 		PyObject *py_source;
119 
120 		if (libobs_to_py(obs_source_t, source, false, &py_source)) {
121 			PyList_Append(ret, py_source);
122 			Py_DECREF(py_source);
123 		}
124 	}
125 
126 	UNUSED_PARAMETER(self);
127 	UNUSED_PARAMETER(args);
128 
129 	da_free(list.sources);
130 	return ret;
131 }
132 
get_current_transition(PyObject * self,PyObject * args)133 static PyObject *get_current_transition(PyObject *self, PyObject *args)
134 {
135 	obs_source_t *source = obs_frontend_get_current_transition();
136 
137 	PyObject *py_source;
138 	if (!libobs_to_py(obs_source_t, source, false, &py_source)) {
139 		obs_source_release(source);
140 		return python_none();
141 	}
142 
143 	UNUSED_PARAMETER(self);
144 	UNUSED_PARAMETER(args);
145 	return py_source;
146 }
147 
set_current_transition(PyObject * self,PyObject * args)148 static PyObject *set_current_transition(PyObject *self, PyObject *args)
149 {
150 	PyObject *py_source;
151 	obs_source_t *source = NULL;
152 
153 	if (!parse_args(args, "O", &py_source))
154 		return python_none();
155 	if (!py_to_libobs(obs_source_t, py_source, &source))
156 		return python_none();
157 
158 	UNUSED_PARAMETER(self);
159 
160 	obs_frontend_set_current_transition(source);
161 	return python_none();
162 }
163 
get_scene_collections(PyObject * self,PyObject * args)164 static PyObject *get_scene_collections(PyObject *self, PyObject *args)
165 {
166 	char **names = obs_frontend_get_scene_collections();
167 	char **name = names;
168 
169 	PyObject *list = PyList_New(0);
170 
171 	while (name && *name) {
172 		PyObject *py_name = PyUnicode_FromString(*name);
173 		if (py_name) {
174 			PyList_Append(list, py_name);
175 			Py_DECREF(py_name);
176 		}
177 		name++;
178 	}
179 
180 	UNUSED_PARAMETER(self);
181 	UNUSED_PARAMETER(args);
182 
183 	bfree(names);
184 	return list;
185 }
186 
get_current_scene_collection(PyObject * self,PyObject * args)187 static PyObject *get_current_scene_collection(PyObject *self, PyObject *args)
188 {
189 	char *name = obs_frontend_get_current_scene_collection();
190 	PyObject *ret = PyUnicode_FromString(name);
191 	bfree(name);
192 
193 	UNUSED_PARAMETER(self);
194 	UNUSED_PARAMETER(args);
195 	return ret;
196 }
197 
set_current_scene_collection(PyObject * self,PyObject * args)198 static PyObject *set_current_scene_collection(PyObject *self, PyObject *args)
199 {
200 	const char *name;
201 	if (!parse_args(args, "s", &name))
202 		return python_none();
203 
204 	UNUSED_PARAMETER(self);
205 
206 	obs_frontend_set_current_scene_collection(name);
207 	return python_none();
208 }
209 
get_profiles(PyObject * self,PyObject * args)210 static PyObject *get_profiles(PyObject *self, PyObject *args)
211 {
212 	char **names = obs_frontend_get_profiles();
213 	char **name = names;
214 
215 	PyObject *list = PyList_New(0);
216 
217 	while (name && *name) {
218 		PyObject *py_name = PyUnicode_FromString(*name);
219 		if (py_name) {
220 			PyList_Append(list, py_name);
221 			Py_DECREF(py_name);
222 		}
223 		name++;
224 	}
225 
226 	UNUSED_PARAMETER(self);
227 	UNUSED_PARAMETER(args);
228 
229 	bfree(names);
230 	return list;
231 }
232 
get_current_profile(PyObject * self,PyObject * args)233 static PyObject *get_current_profile(PyObject *self, PyObject *args)
234 {
235 	char *name = obs_frontend_get_current_profile();
236 	PyObject *ret = PyUnicode_FromString(name);
237 	bfree(name);
238 
239 	UNUSED_PARAMETER(self);
240 	UNUSED_PARAMETER(args);
241 	return ret;
242 }
243 
set_current_profile(PyObject * self,PyObject * args)244 static PyObject *set_current_profile(PyObject *self, PyObject *args)
245 {
246 	const char *name;
247 	if (!parse_args(args, "s", &name))
248 		return python_none();
249 
250 	UNUSED_PARAMETER(self);
251 
252 	obs_frontend_set_current_profile(name);
253 	return python_none();
254 }
255 
256 /* ----------------------------------- */
257 
frontend_save_callback(obs_data_t * save_data,bool saving,void * priv)258 static void frontend_save_callback(obs_data_t *save_data, bool saving,
259 				   void *priv)
260 {
261 	struct python_obs_callback *cb = priv;
262 
263 	if (cb->base.removed) {
264 		obs_frontend_remove_save_callback(frontend_save_callback, cb);
265 		return;
266 	}
267 
268 	lock_python();
269 
270 	PyObject *py_save_data;
271 
272 	if (libobs_to_py(obs_data_t, save_data, false, &py_save_data)) {
273 		PyObject *args = Py_BuildValue("(Op)", py_save_data, saving);
274 
275 		struct python_obs_callback *last_cb = cur_python_cb;
276 		cur_python_cb = cb;
277 		cur_python_script = (struct obs_python_script *)cb->base.script;
278 
279 		PyObject *py_ret = PyObject_CallObject(cb->func, args);
280 		Py_XDECREF(py_ret);
281 		py_error();
282 
283 		cur_python_script = NULL;
284 		cur_python_cb = last_cb;
285 
286 		Py_XDECREF(args);
287 		Py_XDECREF(py_save_data);
288 	}
289 
290 	unlock_python();
291 }
292 
remove_save_callback(PyObject * self,PyObject * args)293 static PyObject *remove_save_callback(PyObject *self, PyObject *args)
294 {
295 	struct obs_python_script *script = cur_python_script;
296 	PyObject *py_cb = NULL;
297 
298 	UNUSED_PARAMETER(self);
299 
300 	if (!parse_args(args, "O", &py_cb))
301 		return python_none();
302 	if (!py_cb || !PyFunction_Check(py_cb))
303 		return python_none();
304 
305 	struct python_obs_callback *cb =
306 		find_python_obs_callback(script, py_cb);
307 	if (cb)
308 		remove_python_obs_callback(cb);
309 	return python_none();
310 }
311 
add_save_callback_defer(void * cb)312 static void add_save_callback_defer(void *cb)
313 {
314 	obs_frontend_add_save_callback(frontend_save_callback, cb);
315 }
316 
add_save_callback(PyObject * self,PyObject * args)317 static PyObject *add_save_callback(PyObject *self, PyObject *args)
318 {
319 	struct obs_python_script *script = cur_python_script;
320 	PyObject *py_cb = NULL;
321 
322 	UNUSED_PARAMETER(self);
323 
324 	if (!parse_args(args, "O", &py_cb))
325 		return python_none();
326 	if (!py_cb || !PyFunction_Check(py_cb))
327 		return python_none();
328 
329 	struct python_obs_callback *cb = add_python_obs_callback(script, py_cb);
330 	defer_call_post(add_save_callback_defer, cb);
331 	return python_none();
332 }
333 
frontend_event_callback(enum obs_frontend_event event,void * priv)334 static void frontend_event_callback(enum obs_frontend_event event, void *priv)
335 {
336 	struct python_obs_callback *cb = priv;
337 
338 	if (cb->base.removed) {
339 		obs_frontend_remove_event_callback(frontend_event_callback, cb);
340 		return;
341 	}
342 
343 	lock_python();
344 
345 	PyObject *args = Py_BuildValue("(i)", event);
346 
347 	struct python_obs_callback *last_cb = cur_python_cb;
348 	cur_python_cb = cb;
349 	cur_python_script = (struct obs_python_script *)cb->base.script;
350 
351 	PyObject *py_ret = PyObject_CallObject(cb->func, args);
352 	Py_XDECREF(py_ret);
353 	py_error();
354 
355 	cur_python_script = NULL;
356 	cur_python_cb = last_cb;
357 
358 	Py_XDECREF(args);
359 
360 	unlock_python();
361 }
362 
remove_event_callback(PyObject * self,PyObject * args)363 static PyObject *remove_event_callback(PyObject *self, PyObject *args)
364 {
365 	struct obs_python_script *script = cur_python_script;
366 	PyObject *py_cb = NULL;
367 
368 	UNUSED_PARAMETER(self);
369 
370 	if (!parse_args(args, "O", &py_cb))
371 		return python_none();
372 	if (!py_cb || !PyFunction_Check(py_cb))
373 		return python_none();
374 
375 	struct python_obs_callback *cb =
376 		find_python_obs_callback(script, py_cb);
377 	if (cb)
378 		remove_python_obs_callback(cb);
379 	return python_none();
380 }
381 
add_event_callback_defer(void * cb)382 static void add_event_callback_defer(void *cb)
383 {
384 	obs_frontend_add_event_callback(frontend_event_callback, cb);
385 }
386 
add_event_callback(PyObject * self,PyObject * args)387 static PyObject *add_event_callback(PyObject *self, PyObject *args)
388 {
389 	struct obs_python_script *script = cur_python_script;
390 	PyObject *py_cb = NULL;
391 
392 	UNUSED_PARAMETER(self);
393 
394 	if (!parse_args(args, "O", &py_cb))
395 		return python_none();
396 	if (!py_cb || !PyFunction_Check(py_cb))
397 		return python_none();
398 
399 	struct python_obs_callback *cb = add_python_obs_callback(script, py_cb);
400 	defer_call_post(add_event_callback_defer, cb);
401 	return python_none();
402 }
403 
404 /* ----------------------------------- */
405 
add_python_frontend_funcs(PyObject * module)406 void add_python_frontend_funcs(PyObject *module)
407 {
408 	static PyMethodDef funcs[] = {
409 #define DEF_FUNC(c) {"obs_frontend_" #c, c, METH_VARARGS, NULL}
410 
411 		DEF_FUNC(get_scene_names),
412 		DEF_FUNC(get_scenes),
413 		DEF_FUNC(get_current_scene),
414 		DEF_FUNC(set_current_scene),
415 		DEF_FUNC(get_transitions),
416 		DEF_FUNC(get_current_transition),
417 		DEF_FUNC(set_current_transition),
418 		DEF_FUNC(get_scene_collections),
419 		DEF_FUNC(get_current_scene_collection),
420 		DEF_FUNC(set_current_scene_collection),
421 		DEF_FUNC(get_profiles),
422 		DEF_FUNC(get_current_profile),
423 		DEF_FUNC(set_current_profile),
424 		DEF_FUNC(remove_save_callback),
425 		DEF_FUNC(add_save_callback),
426 		DEF_FUNC(remove_event_callback),
427 		DEF_FUNC(add_event_callback),
428 
429 #undef DEF_FUNC
430 		{0}};
431 
432 	add_functions_to_py_module(module, funcs);
433 }
434