提交 a12656bd 编写于 作者: J jp9000

Add module UI export capability

Add the ability to be able to call and use toolkit-specific or
program-specific user interface in modules.

User interface code can be either bundled with the module, or 'split'
out in to separate libraries (recommended).

There are three reasons why splitting is recommended:

  1.) It allows plugins to be able to create custom user interface for
      each toolkit if desired.

  2.) Often, UI will be programmed in one language (the language of the
      toolkit), and core logic may be programmed in another.  This
      allows plugins to keep the languages separated if necessary.

  3.) It prevents direct linkage of UI toolkits libraries with core
      module logic.

Splitting is not required, though is recommended if you want your plugin
to be more flexible with other user interface toolkits or programs.

Will implement a generic properties lookup next, which will be used for
automatic UI handling so that plugin UI isn't necessarily required.
上级 17333ddf
......@@ -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
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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.
*
* ===========================================
......
/******************************************************************************
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
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 <http://www.gnu.org/licenses/>.
******************************************************************************/
#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
......@@ -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};
......
......@@ -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 {
......
......@@ -19,6 +19,7 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\libobs\obs-ui.h" />
<ClInclude Include="..\..\..\obs\platform.hpp" />
<CustomBuild Include="..\..\..\obs\qt-display.hpp">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
......
......@@ -82,6 +82,9 @@
<ClInclude Include="GeneratedFiles\ui_NameDialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\libobs\obs-ui.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\obs\obs-app.cpp">
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册