diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 62ba025c3..e54af51c5 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -149,6 +149,7 @@ set(libobs_libobs_HEADERS obs-service.h obs-internal.h obs.h + obs-ui.h obs-data.h obs-module.h obs-scene.h diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 64b4a8fc3..39c72d717 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -49,6 +49,12 @@ struct obs_display { /* TODO: sound output target */ }; +/* TODO: optimize this later so it's not just O(N) string lookups */ +struct ui_callback { + struct obs_ui_info ui_info; + bool (*callback)(void *data, void *ui_data); +}; + /* ------------------------------------------------------------------------- */ struct obs_video { @@ -70,7 +76,7 @@ struct obs_video { }; struct obs_audio { - /* TODO: audio subsystem */ + /* TODO: sound output subsystem */ audio_t audio; }; @@ -100,6 +106,7 @@ struct obs_subsystem { DARRAY(struct output_info) output_types; DARRAY(struct encoder_info) encoder_types; DARRAY(struct service_info) service_types; + DARRAY(struct ui_callback) ui_callbacks; signal_handler_t signals; proc_handler_t procs; diff --git a/libobs/obs-module.c b/libobs/obs-module.c index 6b55289ca..6d43b4504 100644 --- a/libobs/obs-module.c +++ b/libobs/obs-module.c @@ -76,6 +76,41 @@ complete: dstr_free(&enum_name); } +static void module_load_ui_exports(struct obs_module *mod) +{ + bool (*enum_func)(size_t idx, struct obs_ui_info *info); + struct obs_ui_info ui_info; + size_t i = 0; + + enum_func = os_dlsym(mod->module, "enum_ui"); + if (!enum_func) + return; + + while (enum_func(i++, &ui_info)) { + struct ui_callback callback; + struct dstr name; + + dstr_init_copy(&name, ui_info.name); + dstr_cat(&name, "_"); + dstr_cat(&name, ui_info.task); + dstr_cat(&name, "_"); + dstr_cat(&name, ui_info.target); + + callback.ui_info = ui_info; + callback.callback = os_dlsym(mod->module, name.array); + + if (!callback.callback) { + blog(LOG_WARNING, "Module '%s' enumerated UI callback " + "'%s', but the function was not " + "found", mod->name, name.array); + } else { + da_push_back(obs->ui_callbacks, &callback); + } + + dstr_free(&name); + } +} + extern char *find_plugin(const char *plugin); /* checks API version of module and calls module_load if it exists. @@ -157,6 +192,8 @@ int obs_load_module(const char *path) module_load_exports(&mod, &obs->encoder_types.da, "encoders", sizeof(struct encoder_info), load_encoder_info); + module_load_ui_exports(&mod); + da_push_back(obs->modules, &mod); return MODULE_SUCCESS; } diff --git a/libobs/obs-source.c b/libobs/obs-source.c index b3c8d9081..ceaaaeb26 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -249,14 +249,19 @@ void obs_source_remove(obs_source_t source) struct obs_program_data *data = &obs->data; size_t id; - source->removed = true; - pthread_mutex_lock(&data->sources_mutex); - id = da_find(data->sources, &source, 0); - if (id != DARRAY_INVALID) { - da_erase_item(data->sources, &source); - obs_source_release(source); + if (!source) + return; + + if (!source->removed) { + source->removed = true; + + id = da_find(data->sources, &source, 0); + if (id != DARRAY_INVALID) { + da_erase_item(data->sources, &source); + obs_source_release(source); + } } pthread_mutex_unlock(&data->sources_mutex); diff --git a/libobs/obs-source.h b/libobs/obs-source.h index 384d18fa2..38c8a04cb 100644 --- a/libobs/obs-source.h +++ b/libobs/obs-source.h @@ -35,6 +35,17 @@ * A module with sources needs to export these functions: * + enum_[type] * + * [type] can be one of the following: + * + input + * + filter + * + transition + * + * input: A source used for directly playing video and/or sound. + * filter: A source that is used for filtering other sources, modifying + * the audio/video data before it is actually played. + * transition: A source used for transitioning between two different sources + * on screen. + * * Each individual source is then exported by it's name. For example, a * source named "mysource" would have the following exports: * + mysource_getname @@ -58,9 +69,9 @@ * =========================================== * Primary Exports * =========================================== - * const bool enum_[type](size_t idx, const char **name); - * idx: index of the source. - * type: pointer to variable that receives the type of the source + * bool enum_[type](size_t idx, const char **name); + * idx: index of the enumeration. + * name: pointer to variable that receives the type of the source * Return value: false when no more available. * * =========================================== diff --git a/libobs/obs-ui.h b/libobs/obs-ui.h new file mode 100644 index 000000000..a70dda930 --- /dev/null +++ b/libobs/obs-ui.h @@ -0,0 +1,139 @@ +/****************************************************************************** + Copyright (C) 2013 by Hugh Bailey + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#pragma once + +#include "obs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct obs_ui_info { + const char *name; + const char *task; + const char *target; +}; + +/* + * =========================================== + * Module UI calls + * =========================================== + * + * Modules can specify custom user-interface-specific exports. UI exports + * can be within the same library as the actual core logic, or separated in to + * different modules to split up UI logic and core module logic. + * + * The reasoning for this is to allow for custom user interface of differing + * toolkits or for automatically generated user interface, or to simply allow + * separation of UI code from core code (which may often be in differing + * languages) + * + * A module with UI calls needs to export this function: + * + enum_ui + * + * The enum_ui function provides an obs_ui_info structure for each + * input/output/etc. For example, to export Qt-specific configuration + * functions, the exports might be something like: + * + mysource_config_qt + * + myoutput_config_qt + * + myencoder_config_panel_qt + * + * ..And the values given to enum_ui would be something this: + * + * struct obs_ui_info ui_list[] = { + * {"mysource", "config", "qt"}, + * {"myoutput", "config", "qt"}, + * {"myencoder", "config_panel", "qt"} + * }; + * + * 'qt' could be replaced with 'wx' or something similar if using wxWidgets, + * or 'win32' if using bare windows API. + * + * =========================================== + * Primary Exports + * =========================================== + * bool enum_ui(size_t idx, struct obs_ui_info *ui_info); + * idx: index of the enumeration + * export: full name of the actual UI export. + * Return value: false when no more available. + * + * =========================================== + * Export Format + * =========================================== + * Although the 'export' variable specifies the full export name, each + * export should be formatted as so: + * + * bool [name]_[task]_[target](void *data, void *ui_data); + * + * [name]: specifies the name of the input/output/encoder/etc. + * [task]: specifies the task of the user interface, most often 'config', + * or 'config_panel' + * [target]: specifies the target or toolkit of the user interface, such as + * but not limited to 'qt', 'wx', 'win32', 'cocoa', etc. If + * a custom solution is desired, it can use a program-specific + * name, such as 'myprogram'. + * + * The 'data' variable points to the input/output/encoder/etc. The 'ui_data' + * varaible points to the UI parent or UI-specific data to be used with the + * custom user interface. + * + * What 'ui_data' points to differs depending on the target, and you should + * use discretion and consistency when using this variable to relay + * information to the UI function. For example, it would be ideal to have + * 'ui_data' point to a parent, QWidget for Qt, or a wxWindow for wxWidgets, + * etc. + * + * For example, if I had a source called 'mysource', and I wanted to export + * a function that handles the task 'config' with the Qt library, the export + * would be: + * + * bool mysource_config_qt(void *data, void *ui_data); + * + * In this example, the ui_data variable should ideally be a pointer to the + * QWidget parent, if any. + */ + +/** + * =========================================== + * obs_call_ui + * =========================================== + * Requests UI to be displayed + * + * This is typically used for things like creating dialogs/panels/etc for + * specific toolkits. + * + * name: Name of the input/output/etc type that UI was requested for + * task: Task of the user interface (i.e. "config", "config_panel") + * target: Desired target (i.e. "qt", "wx", "gtk3", "win32", etc) + * data: Pointer to the obs input/output/etc + * parent: Parent pointer for UI toolkit (if any) + * + * Return value: OBS_UI_SUCCESS if the UI was successful + * OBS_UI_CANCEL if the UI was cancelled by the user + * OBS_UI_NOTFOUND if the UI callback was not found + */ +#define OBS_UI_SUCCESS 0 +#define OBS_UI_CANCEL -1 +#define OBS_UI_NOTFOUND -2 + +EXPORT int obs_call_ui(const char *name, const char *task, const char *target, + void *data, void *ui_data); + +#ifdef __cplusplus +} +#endif diff --git a/libobs/obs.c b/libobs/obs.c index 20ef9fb23..9effa4152 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -326,6 +326,7 @@ void obs_shutdown(void) da_free(obs->transition_types); da_free(obs->output_types); da_free(obs->service_types); + da_free(obs->ui_callbacks); obs_free_data(); obs_free_video(); @@ -451,6 +452,37 @@ video_t obs_video(void) return (obs != NULL) ? obs->video.video : NULL; } +/* TODO: optimize this later so it's not just O(N) string lookups */ +static inline struct ui_callback *get_ui_callback(const char *name, + const char *task, const char *target) +{ + for (size_t i = 0; i < obs->ui_callbacks.num; i++) { + struct ui_callback *callback = obs->ui_callbacks.array+i; + + if (strcmp(callback->ui_info.name, name) == 0 && + strcmp(callback->ui_info.task, task) == 0 && + strcmp(callback->ui_info.target, target) == 0) + return callback; + } + + return NULL; +} + +int obs_call_ui(const char *name, const char *task, const char *target, + void *data, void *ui_data) +{ + struct ui_callback *callback; + int errorcode = OBS_UI_NOTFOUND; + + callback = get_ui_callback(name, task, target); + if (callback) { + bool success = callback->callback(data, ui_data); + errorcode = success ? OBS_UI_SUCCESS : OBS_UI_CANCEL; + } + + return errorcode; +} + bool obs_add_source(obs_source_t source) { struct calldata params = {0}; diff --git a/libobs/obs.h b/libobs/obs.h index 0c058a2c6..417b49afd 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -29,6 +29,8 @@ #include "obs-defs.h" #include "obs-data.h" +#include "obs-ui.h" + /* * Main libobs header used by applications. */ @@ -51,16 +53,6 @@ enum obs_source_type { SOURCE_SCENE }; -enum obs_encoder_type { - ENCODER_VIDEO, - ENCODER_AUDIO -}; - -enum obs_video_type { - OBS_VIDEO_YUV, - OBS_VIDEO_RGB -}; - /* used for changing the order of items (for example, filters in a source, * or items in a scene) */ enum order_movement { diff --git a/vs/2013/obs-studio/obs-studio.vcxproj b/vs/2013/obs-studio/obs-studio.vcxproj index 1aa7b5db3..06154bde6 100644 --- a/vs/2013/obs-studio/obs-studio.vcxproj +++ b/vs/2013/obs-studio/obs-studio.vcxproj @@ -19,6 +19,7 @@ + $(QTDIR)\bin\moc.exe;%(FullPath) diff --git a/vs/2013/obs-studio/obs-studio.vcxproj.filters b/vs/2013/obs-studio/obs-studio.vcxproj.filters index 15812a9f3..bb732c91b 100644 --- a/vs/2013/obs-studio/obs-studio.vcxproj.filters +++ b/vs/2013/obs-studio/obs-studio.vcxproj.filters @@ -82,6 +82,9 @@ Generated Files + + Header Files +