提交 8836592d 编写于 作者: J jp9000

UI: Add front-end API library

Allows manipulating and modifying the front-end via plugins.
上级 bfc21317
project(obs)
option(ENABLE_UI "Enables the OBS user interfaces" ON)
if(DISABLE_UI)
message(STATUS "UI disabled")
......@@ -10,6 +8,12 @@ else()
set(FIND_MODE QUIET)
endif()
add_subdirectory(obs-frontend-api)
# ----------------------------------------------------------------------------
project(obs)
if(DEFINED QTDIR${_lib_suffix})
list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}")
elseif(DEFINED QTDIR)
......@@ -40,6 +44,7 @@ if(NOT Qt5Widgets_FOUND)
endif()
endif()
include_directories(SYSTEM "obs-frontend-api")
include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
find_package(Libcurl REQUIRED)
......@@ -97,6 +102,7 @@ endif()
set(obs_SOURCES
${obs_PLATFORM_SOURCES}
obs-app.cpp
api-interface.cpp
window-basic-main.cpp
window-basic-filters.cpp
window-basic-settings.cpp
......@@ -219,6 +225,7 @@ target_link_libraries(obs
libobs
libff
Qt5::Widgets
obs-frontend-api
${LIBCURL_LIBRARIES}
${obs_PLATFORM_LIBRARIES})
......
#include <obs-frontend-internal.hpp>
#include "obs-app.hpp"
#include "qt-wrappers.hpp"
#include "window-basic-main.hpp"
#include "window-basic-main-outputs.hpp"
#include <functional>
using namespace std;
Q_DECLARE_METATYPE(OBSScene);
Q_DECLARE_METATYPE(OBSSource);
template <typename T>
static T GetOBSRef(QListWidgetItem *item)
{
return item->data(static_cast<int>(QtDataRole::OBSRef)).value<T>();
}
void EnumProfiles(function<bool (const char *, const char *)> &&cb);
void EnumSceneCollections(function<bool (const char *, const char *)> &&cb);
/* ------------------------------------------------------------------------- */
template<typename T> struct OBSStudioCallback {
T callback;
void *private_data;
inline OBSStudioCallback(T cb, void *p) :
callback(cb), private_data(p)
{}
};
template <typename T> inline size_t GetCallbackIdx(
vector<OBSStudioCallback<T>> &callbacks,
T callback, void *private_data)
{
for (size_t i = 0; i < callbacks.size(); i++) {
OBSStudioCallback<T> curCB = callbacks[i];
if (curCB.callback == callback &&
curCB.private_data == private_data)
return i;
}
return (size_t)-1;
}
struct OBSStudioAPI : obs_frontend_callbacks {
OBSBasic *main;
vector<OBSStudioCallback<obs_frontend_event_cb>> callbacks;
vector<OBSStudioCallback<obs_frontend_save_cb>> saveCallbacks;
inline OBSStudioAPI(OBSBasic *main_) : main(main_) {}
void *obs_frontend_get_main_window(void) override
{
return (void*)main;
}
void *obs_frontend_get_main_window_handle(void) override
{
return (void*)main->winId();
}
void obs_frontend_get_scenes(
struct obs_frontend_source_list *sources) override
{
for (int i = 0; i < main->ui->scenes->count(); i++) {
QListWidgetItem *item = main->ui->scenes->item(i);
OBSScene scene = GetOBSRef<OBSScene>(item);
obs_source_t *source = obs_scene_get_source(scene);
obs_source_addref(source);
da_push_back(sources->sources, &source);
}
}
obs_source_t *obs_frontend_get_current_scene(void) override
{
OBSSource source;
if (main->IsPreviewProgramMode()) {
source = obs_weak_source_get_source(main->programScene);
} else {
source = main->GetCurrentSceneSource();
obs_source_addref(source);
}
return source;
}
void obs_frontend_set_current_scene(obs_source_t *scene) override
{
if (main->IsPreviewProgramMode()) {
QMetaObject::invokeMethod(main, "TransitionToScene",
Q_ARG(OBSSource, OBSSource(scene)));
} else {
QMetaObject::invokeMethod(main, "SetCurrentScene",
Q_ARG(OBSSource, OBSSource(scene)),
Q_ARG(bool, false));
}
}
void obs_frontend_get_transitions(
struct obs_frontend_source_list *sources) override
{
for (int i = 0; i < main->ui->transitions->count(); i++) {
OBSSource tr = main->ui->transitions->itemData(i)
.value<OBSSource>();
obs_source_addref(tr);
da_push_back(sources->sources, &tr);
}
}
obs_source_t *obs_frontend_get_current_transition(void) override
{
OBSSource tr = main->GetCurrentTransition();
obs_source_addref(tr);
return tr;
}
void obs_frontend_set_current_transition(
obs_source_t *transition) override
{
QMetaObject::invokeMethod(main, "SetTransition",
Q_ARG(OBSSource, OBSSource(transition)));
}
void obs_frontend_get_scene_collections(
std::vector<std::string> &strings) override
{
auto addCollection = [&](const char *name, const char *)
{
strings.emplace_back(name);
return true;
};
EnumSceneCollections(addCollection);
}
char *obs_frontend_get_current_scene_collection(void) override
{
const char *cur_name = config_get_string(App()->GlobalConfig(),
"Basic", "SceneCollection");
return bstrdup(cur_name);
}
void obs_frontend_set_current_scene_collection(
const char *collection) override
{
QList<QAction*> menuActions =
main->ui->sceneCollectionMenu->actions();
QString qstrCollection = QT_UTF8(collection);
for (int i = 0; i < menuActions.count(); i++) {
QAction *action = menuActions[i];
QVariant v = action->property("file_name");
if (v.typeName() != nullptr) {
if (action->text() == qstrCollection) {
action->trigger();
break;
}
}
}
}
void obs_frontend_get_profiles(
std::vector<std::string> &strings) override
{
auto addProfile = [&](const char *name, const char *)
{
strings.emplace_back(name);
return true;
};
EnumProfiles(addProfile);
}
char *obs_frontend_get_current_profile(void) override
{
const char *name = config_get_string(App()->GlobalConfig(),
"Basic", "Profile");
return bstrdup(name);
}
void obs_frontend_set_current_profile(const char *profile) override
{
QList<QAction*> menuActions =
main->ui->profileMenu->actions();
QString qstrProfile = QT_UTF8(profile);
for (int i = 0; i < menuActions.count(); i++) {
QAction *action = menuActions[i];
QVariant v = action->property("file_name");
if (v.typeName() != nullptr) {
if (action->text() == qstrProfile) {
action->trigger();
break;
}
}
}
}
void obs_frontend_streaming_start(void) override
{
QMetaObject::invokeMethod(main, "StartStreaming");
}
void obs_frontend_streaming_stop(void) override
{
QMetaObject::invokeMethod(main, "StopStreaming");
}
bool obs_frontend_streaming_active(void) override
{
return main->outputHandler->StreamingActive();
}
void obs_frontend_recording_start(void) override
{
QMetaObject::invokeMethod(main, "StartRecording");
}
void obs_frontend_recording_stop(void) override
{
QMetaObject::invokeMethod(main, "StopRecording");
}
bool obs_frontend_recording_active(void) override
{
return main->outputHandler->StreamingActive();
}
void *obs_frontend_add_tools_menu_qaction(const char *name) override
{
main->ui->menuTools->setEnabled(true);
return (void*)main->ui->menuTools->addAction(QT_UTF8(name));
}
void obs_frontend_add_tools_menu_item(const char *name,
obs_frontend_cb callback, void *private_data) override
{
main->ui->menuTools->setEnabled(true);
auto func = [private_data, callback] ()
{
callback(private_data);
};
QAction *action = main->ui->menuTools->addAction(QT_UTF8(name));
QObject::connect(action, &QAction::triggered, func);
}
void obs_frontend_add_event_callback(obs_frontend_event_cb callback,
void *private_data) override
{
size_t idx = GetCallbackIdx(callbacks, callback, private_data);
if (idx == (size_t)-1)
callbacks.emplace_back(callback, private_data);
}
void obs_frontend_remove_event_callback(obs_frontend_event_cb callback,
void *private_data) override
{
size_t idx = GetCallbackIdx(callbacks, callback, private_data);
if (idx == (size_t)-1)
return;
callbacks.erase(callbacks.begin() + idx);
}
obs_output_t *obs_frontend_get_streaming_output(void) override
{
OBSOutput output = main->outputHandler->streamOutput;
obs_output_addref(output);
return output;
}
obs_output_t *obs_frontend_get_recording_output(void) override
{
OBSOutput out = main->outputHandler->fileOutput;
obs_output_addref(out);
return out;
}
config_t *obs_frontend_get_profile_config(void) override
{
return main->basicConfig;
}
config_t *obs_frontend_get_global_config(void) override
{
return App()->GlobalConfig();
}
void obs_frontend_save(void) override
{
main->SaveProject();
}
void obs_frontend_add_save_callback(obs_frontend_save_cb callback,
void *private_data) override
{
size_t idx = GetCallbackIdx(saveCallbacks, callback,
private_data);
if (idx == (size_t)-1)
saveCallbacks.emplace_back(callback, private_data);
}
void obs_frontend_remove_save_callback(obs_frontend_save_cb callback,
void *private_data) override
{
size_t idx = GetCallbackIdx(saveCallbacks, callback,
private_data);
if (idx == (size_t)-1)
return;
saveCallbacks.erase(saveCallbacks.begin() + idx);
}
void obs_frontend_push_ui_translation(
obs_frontend_translate_ui_cb translate) override
{
App()->PushUITranslation(translate);
}
void obs_frontend_pop_ui_translation(void) override
{
App()->PopUITranslation();
}
void on_load(obs_data_t *settings) override
{
for (auto cb : saveCallbacks)
cb.callback(settings, false, cb.private_data);
}
void on_save(obs_data_t *settings) override
{
for (auto cb : saveCallbacks)
cb.callback(settings, true, cb.private_data);
}
void on_event(enum obs_frontend_event event) override
{
if (main->disableSaving)
return;
for (auto cb : callbacks)
cb.callback(event, cb.private_data);
}
};
obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main)
{
obs_frontend_callbacks *api = new OBSStudioAPI(main);
obs_frontend_set_callbacks_internal(api);
return api;
}
......@@ -354,6 +354,9 @@ Basic.MainMenu.View.StatusBar="&Status Bar"
Basic.MainMenu.SceneCollection="&Scene Collection"
Basic.MainMenu.Profile="&Profile"
# basic mode help menu
Basic.MainMenu.Tools="&Tools"
# basic mode help menu
Basic.MainMenu.Help="&Help"
Basic.MainMenu.Help.Website="Visit &Website"
......
......@@ -229,7 +229,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>201</width>
<width>215</width>
<height>16</height>
</rect>
</property>
......@@ -913,11 +913,20 @@
<addaction name="toggleSceneTransitions"/>
<addaction name="toggleStatusBar"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title">
<string>Basic.MainMenu.Tools</string>
</property>
</widget>
<addaction name="menu_File"/>
<addaction name="menuBasic_MainMenu_Edit"/>
<addaction name="viewMenu"/>
<addaction name="profileMenu"/>
<addaction name="sceneCollectionMenu"/>
<addaction name="menuTools"/>
<addaction name="menuBasic_MainMenu_Help"/>
</widget>
<widget class="OBSBasicStatusBar" name="statusbar"/>
......
......@@ -937,12 +937,22 @@ const char *OBSApp::GetCurrentLog() const
return currentLogFile.c_str();
}
bool OBSApp::TranslateString(const char *lookupVal, const char **out) const
{
for (obs_frontend_translate_ui_cb cb : translatorHooks) {
if (cb(lookupVal, out))
return true;
}
return text_lookup_getstr(App()->GetTextLookup(), lookupVal, out);
}
QString OBSTranslator::translate(const char *context, const char *sourceText,
const char *disambiguation, int n) const
{
const char *out = nullptr;
if (!text_lookup_getstr(App()->GetTextLookup(), sourceText, &out))
return QString();
if (!App()->TranslateString(sourceText, &out))
return QString(sourceText);
UNUSED_PARAMETER(context);
UNUSED_PARAMETER(disambiguation);
......
......@@ -25,9 +25,11 @@
#include <util/profiler.h>
#include <util/util.hpp>
#include <util/platform.h>
#include <obs-frontend-api.h>
#include <string>
#include <memory>
#include <vector>
#include <deque>
#include "window-main.hpp"
......@@ -71,6 +73,8 @@ private:
os_inhibit_t *sleepInhibitor = nullptr;
int sleepInhibitRefs = 0;
std::deque<obs_frontend_translate_ui_cb> translatorHooks;
bool InitGlobalConfig();
bool InitGlobalConfigDefaults();
bool InitLocale();
......@@ -102,6 +106,8 @@ public:
return textLookup.GetString(lookupVal);
}
bool TranslateString(const char *lookupVal, const char **out) const;
profiler_name_store_t *GetProfilerNameStore() const
{
return profilerNameStore;
......@@ -131,6 +137,16 @@ public:
if (--sleepInhibitRefs == 0)
os_inhibit_sleep_set_active(sleepInhibitor, false);
}
inline void PushUITranslation(obs_frontend_translate_ui_cb cb)
{
translatorHooks.emplace_front(cb);
}
inline void PopUITranslation()
{
translatorHooks.pop_front();
}
};
int GetConfigPath(char *path, size_t size, const char *name);
......
project(obs-frontend-api)
include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
add_definitions(-DLIBOBS_EXPORTS)
set(obs-frontend-api_SOURCES
obs-frontend-api.cpp)
set(obs-frontend-api_HEADERS
obs-frontend-internal.hpp
obs-frontend-api.h)
add_library(obs-frontend-api SHARED
${obs-frontend-api_SOURCES}
${obs-frontend-api_HEADERS})
target_link_libraries(obs-frontend-api
libobs)
install_obs_core(obs-frontend-api)
#include "obs-frontend-internal.hpp"
#include <memory>
using namespace std;
static unique_ptr<obs_frontend_callbacks> c;
void obs_frontend_set_callbacks_internal(obs_frontend_callbacks *callbacks)
{
c.reset(callbacks);
}
static inline bool callbacks_valid_(const char *func_name)
{
if (!c) {
blog(LOG_WARNING, "Tried to call %s with no callbacks!",
func_name);
return false;
}
return true;
}
#define callbacks_valid() callbacks_valid_(__FUNCTION__)
static char **convert_string_list(vector<string> &strings)
{
size_t size = 0;
size_t string_data_offset = (strings.size() + 1) * sizeof(char*);
uint8_t *out;
char **ptr_list;
char *string_data;
size += string_data_offset;
for (auto &str : strings)
size += str.size() + 1;
if (!size)
return 0;
out = (uint8_t*)bmalloc(size);
ptr_list = (char**)out;
string_data = (char*)(out + string_data_offset);
for (auto &str : strings) {
*ptr_list = string_data;
memcpy(string_data, str.c_str(), str.size() + 1);
ptr_list++;
string_data += str.size() + 1;
}
*ptr_list = nullptr;
return (char**)out;
}
/* ------------------------------------------------------------------------- */
void *obs_frontend_get_main_window(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_main_window()
: nullptr;
}
void *obs_frontend_get_main_window_handle(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_main_window_handle()
: nullptr;
}
char **obs_frontend_get_scene_names(void)
{
if (!callbacks_valid())
return NULL;
struct obs_frontend_source_list sources = {};
vector<string> names;
c->obs_frontend_get_scenes(&sources);
for (size_t i = 0; i < sources.sources.num; i++) {
obs_source_t *source = sources.sources.array[i];
const char *name = obs_source_get_name(source);
names.emplace_back(name);
}
obs_frontend_source_list_free(&sources);
return convert_string_list(names);
}
void obs_frontend_get_scenes(struct obs_frontend_source_list *sources)
{
if (callbacks_valid()) c->obs_frontend_get_scenes(sources);
}
obs_source_t *obs_frontend_get_current_scene(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_current_scene()
: nullptr;
}
void obs_frontend_set_current_scene(obs_source_t *scene)
{
if (callbacks_valid()) c->obs_frontend_set_current_scene(scene);
}
void obs_frontend_get_transitions(struct obs_frontend_source_list *sources)
{
if (callbacks_valid()) c->obs_frontend_get_transitions(sources);
}
obs_source_t *obs_frontend_get_current_transition(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_current_transition()
: nullptr;
}
void obs_frontend_set_current_transition(obs_source_t *transition)
{
if (callbacks_valid())
c->obs_frontend_set_current_transition(transition);
}
char **obs_frontend_get_scene_collections(void)
{
if (!callbacks_valid())
return nullptr;
vector<string> strings;
c->obs_frontend_get_scene_collections(strings);
return convert_string_list(strings);
}
char *obs_frontend_get_current_scene_collection(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_current_scene_collection()
: nullptr;
}
void obs_frontend_set_current_scene_collection(const char *collection)
{
if (callbacks_valid())
c->obs_frontend_set_current_scene_collection(collection);
}
char **obs_frontend_get_profiles(void)
{
if (!callbacks_valid())
return nullptr;
vector<string> strings;
c->obs_frontend_get_profiles(strings);
return convert_string_list(strings);
}
char *obs_frontend_get_current_profile(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_current_profile()
: nullptr;
}
void obs_frontend_set_current_profile(const char *profile)
{
if (callbacks_valid())
c->obs_frontend_set_current_profile(profile);
}
void obs_frontend_streaming_start(void)
{
if (callbacks_valid()) c->obs_frontend_streaming_start();
}
void obs_frontend_streaming_stop(void)
{
if (callbacks_valid()) c->obs_frontend_streaming_stop();
}
bool obs_frontend_streaming_active(void)
{
return !!callbacks_valid()
? c->obs_frontend_streaming_active()
: false;
}
void obs_frontend_recording_start(void)
{
if (callbacks_valid()) c->obs_frontend_recording_start();
}
void obs_frontend_recording_stop(void)
{
if (callbacks_valid()) c->obs_frontend_recording_stop();
}
bool obs_frontend_recording_active(void)
{
return !!callbacks_valid()
? c->obs_frontend_recording_active()
: false;
}
void *obs_frontend_add_tools_menu_qaction(const char *name)
{
return !!callbacks_valid()
? c->obs_frontend_add_tools_menu_qaction(name)
: nullptr;
}
void obs_frontend_add_tools_menu_item(const char *name,
obs_frontend_cb callback, void *private_data)
{
if (callbacks_valid())
c->obs_frontend_add_tools_menu_item(name, callback,
private_data);
}
void obs_frontend_add_event_callback(obs_frontend_event_cb callback,
void *private_data)
{
if (callbacks_valid())
c->obs_frontend_add_event_callback(callback, private_data);
}
void obs_frontend_remove_event_callback(obs_frontend_event_cb callback,
void *private_data)
{
if (callbacks_valid())
c->obs_frontend_remove_event_callback(callback, private_data);
}
obs_output_t *obs_frontend_get_streaming_output(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_streaming_output()
: nullptr;
}
obs_output_t *obs_frontend_get_recording_output(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_recording_output()
: nullptr;
}
config_t *obs_frontend_get_profile_config(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_profile_config()
: nullptr;
}
config_t *obs_frontend_get_global_config(void)
{
return !!callbacks_valid()
? c->obs_frontend_get_global_config()
: nullptr;
}
void obs_frontend_save(void)
{
if (callbacks_valid())
c->obs_frontend_save();
}
void obs_frontend_add_save_callback(obs_frontend_save_cb callback,
void *private_data)
{
if (callbacks_valid())
c->obs_frontend_add_save_callback(callback, private_data);
}
void obs_frontend_remove_save_callback(obs_frontend_save_cb callback,
void *private_data)
{
if (callbacks_valid())
c->obs_frontend_remove_save_callback(callback, private_data);
}
void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate)
{
if (callbacks_valid())
c->obs_frontend_push_ui_translation(translate);
}
void obs_frontend_pop_ui_translation(void)
{
if (callbacks_valid())
c->obs_frontend_pop_ui_translation();
}
#pragma once
#include <obs.h>
#include <util/darray.h>
#ifdef __cplusplus
extern "C" {
#endif
struct config_data;
typedef struct config_data config_t;
struct obs_data;
typedef struct obs_data obs_data_t;
/* ------------------------------------------------------------------------- */
struct obs_frontend_source_list {
DARRAY(obs_source_t*) sources;
};
static inline void obs_frontend_source_list_free(
struct obs_frontend_source_list *source_list)
{
size_t num = source_list->sources.num;
for (size_t i = 0; i < num; i++)
obs_source_release(source_list->sources.array[i]);
da_free(source_list->sources);
}
/* ------------------------------------------------------------------------- */
/* NOTE: Functions that return char** string lists are a single allocation of
* memory with pointers to itself. Free with a single call to bfree on the
* base char** pointer. */
/* NOTE: User interface should not use typical Qt locale translation methods,
* as the OBS UI bypasses it to use a custom translation implementation. Use
* standard module translation methods, obs_module_text. For text in a Qt
* window, use obs_frontend_push_ui_translation when the text is about to be
* translated, and obs_frontend_pop_ui_translation when translation is
* complete. */
EXPORT void *obs_frontend_get_main_window(void);
EXPORT void *obs_frontend_get_main_window_handle(void);
EXPORT char **obs_frontend_get_scene_names(void);
EXPORT void obs_frontend_get_scenes(struct obs_frontend_source_list *sources);
EXPORT obs_source_t *obs_frontend_get_current_scene(void);
EXPORT void obs_frontend_set_current_scene(obs_source_t *scene);
EXPORT void obs_frontend_get_transitions(
struct obs_frontend_source_list *sources);
EXPORT obs_source_t *obs_frontend_get_current_transition(void);
EXPORT void obs_frontend_set_current_transition(obs_source_t *transition);
EXPORT char **obs_frontend_get_scene_collections(void);
EXPORT char *obs_frontend_get_current_scene_collection(void);
EXPORT void obs_frontend_set_current_scene_collection(const char *collection);
EXPORT char **obs_frontend_get_profiles(void);
EXPORT char *obs_frontend_get_current_profile(void);
EXPORT void obs_frontend_set_current_profile(const char *profile);
EXPORT void obs_frontend_streaming_start(void);
EXPORT void obs_frontend_streaming_stop(void);
EXPORT bool obs_frontend_streaming_active(void);
EXPORT void obs_frontend_recording_start(void);
EXPORT void obs_frontend_recording_stop(void);
EXPORT bool obs_frontend_recording_active(void);
typedef void (*obs_frontend_cb)(void *private_data);
EXPORT void *obs_frontend_add_tools_menu_qaction(const char *name);
EXPORT void obs_frontend_add_tools_menu_item(const char *name,
obs_frontend_cb callback, void *private_data);
enum obs_frontend_event {
OBS_FRONTEND_EVENT_STREAMING_STARTING,
OBS_FRONTEND_EVENT_STREAMING_STARTED,
OBS_FRONTEND_EVENT_STREAMING_STOPPING,
OBS_FRONTEND_EVENT_STREAMING_STOPPED,
OBS_FRONTEND_EVENT_RECORDING_STARTING,
OBS_FRONTEND_EVENT_RECORDING_STARTED,
OBS_FRONTEND_EVENT_RECORDING_STOPPING,
OBS_FRONTEND_EVENT_RECORDING_STOPPED,
OBS_FRONTEND_EVENT_SCENE_CHANGED,
OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED,
OBS_FRONTEND_EVENT_TRANSITION_CHANGED,
OBS_FRONTEND_EVENT_TRANSITION_STOPPED,
OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED,
OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED,
OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED,
OBS_FRONTEND_EVENT_PROFILE_CHANGED,
OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED,
OBS_FRONTEND_EVENT_EXIT
};
typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event,
void *private_data);
EXPORT void obs_frontend_add_event_callback(obs_frontend_event_cb callback,
void *private_data);
EXPORT void obs_frontend_remove_event_callback(obs_frontend_event_cb callback,
void *private_data);
typedef void (*obs_frontend_save_cb)(obs_data_t *save_data, bool saving,
void *private_data);
EXPORT void obs_frontend_save(void);
EXPORT void obs_frontend_add_save_callback(obs_frontend_save_cb callback,
void *private_data);
EXPORT void obs_frontend_remove_save_callback(obs_frontend_save_cb callback,
void *private_data);
EXPORT obs_output_t *obs_frontend_get_streaming_output(void);
EXPORT obs_output_t *obs_frontend_get_recording_output(void);
EXPORT config_t *obs_frontend_get_profile_config(void);
EXPORT config_t *obs_frontend_get_global_config(void);
typedef bool (*obs_frontend_translate_ui_cb)(const char *text,
const char **out);
EXPORT void obs_frontend_push_ui_translation(
obs_frontend_translate_ui_cb translate);
EXPORT void obs_frontend_pop_ui_translation(void);
/* ------------------------------------------------------------------------- */
#ifdef __cplusplus
}
#endif
#pragma once
#include "obs-frontend-api.h"
#include <vector>
#include <string>
struct obs_frontend_callbacks {
virtual void *obs_frontend_get_main_window(void)=0;
virtual void *obs_frontend_get_main_window_handle(void)=0;
virtual void obs_frontend_get_scenes(
struct obs_frontend_source_list *sources)=0;
virtual obs_source_t *obs_frontend_get_current_scene(void)=0;
virtual void obs_frontend_set_current_scene(obs_source_t *scene)=0;
virtual void obs_frontend_get_transitions(
struct obs_frontend_source_list *sources)=0;
virtual obs_source_t *obs_frontend_get_current_transition(void)=0;
virtual void obs_frontend_set_current_transition(
obs_source_t *transition)=0;
virtual void obs_frontend_get_scene_collections(
std::vector<std::string> &strings)=0;
virtual char *obs_frontend_get_current_scene_collection(void)=0;
virtual void obs_frontend_set_current_scene_collection(
const char *collection)=0;
virtual void obs_frontend_get_profiles(
std::vector<std::string> &strings)=0;
virtual char *obs_frontend_get_current_profile(void)=0;
virtual void obs_frontend_set_current_profile(const char *profile)=0;
virtual void obs_frontend_streaming_start(void)=0;
virtual void obs_frontend_streaming_stop(void)=0;
virtual bool obs_frontend_streaming_active(void)=0;
virtual void obs_frontend_recording_start(void)=0;
virtual void obs_frontend_recording_stop(void)=0;
virtual bool obs_frontend_recording_active(void)=0;
virtual void *obs_frontend_add_tools_menu_qaction(const char *name)=0;
virtual void obs_frontend_add_tools_menu_item(const char *name,
obs_frontend_cb callback, void *private_data)=0;
virtual void obs_frontend_add_event_callback(
obs_frontend_event_cb callback, void *private_data)=0;
virtual void obs_frontend_remove_event_callback(
obs_frontend_event_cb callback, void *private_data)=0;
virtual obs_output_t *obs_frontend_get_streaming_output(void)=0;
virtual obs_output_t *obs_frontend_get_recording_output(void)=0;
virtual config_t *obs_frontend_get_profile_config(void)=0;
virtual config_t *obs_frontend_get_global_config(void)=0;
virtual void obs_frontend_save(void)=0;
virtual void obs_frontend_add_save_callback(
obs_frontend_save_cb callback, void *private_data)=0;
virtual void obs_frontend_remove_save_callback(
obs_frontend_save_cb callback, void *private_data)=0;
virtual void obs_frontend_push_ui_translation(
obs_frontend_translate_ui_cb translate)=0;
virtual void obs_frontend_pop_ui_translation(void)=0;
virtual void on_load(obs_data_t *settings)=0;
virtual void on_save(obs_data_t *settings)=0;
virtual void on_event(enum obs_frontend_event event)=0;
};
EXPORT void obs_frontend_set_callbacks_internal(
obs_frontend_callbacks *callbacks);
......@@ -239,6 +239,11 @@ bool OBSBasic::AddProfile(bool create_new, const char *title, const char *text,
config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
UpdateTitleBar();
if (api) {
api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED);
api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED);
}
return true;
}
......@@ -363,6 +368,11 @@ void OBSBasic::on_actionRenameProfile_triggered()
DeleteProfile(curName.c_str(), curDir.c_str());
RefreshProfiles();
}
if (api) {
api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED);
api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED);
}
}
void OBSBasic::on_actionRemoveProfile_triggered()
......@@ -431,6 +441,11 @@ void OBSBasic::on_actionRemoveProfile_triggered()
blog(LOG_INFO, "------------------------------------------------");
UpdateTitleBar();
if (api) {
api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED);
api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED);
}
}
void OBSBasic::ChangeProfile()
......@@ -481,4 +496,7 @@ void OBSBasic::ChangeProfile()
blog(LOG_INFO, "Switched to profile '%s' (%s)",
newName, newDir);
blog(LOG_INFO, "------------------------------------------------");
if (api)
api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED);
}
......@@ -178,6 +178,11 @@ void OBSBasic::AddSceneCollection(bool create_new)
blog(LOG_INFO, "------------------------------------------------");
UpdateTitleBar();
if (api) {
api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED);
api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);
}
}
void OBSBasic::RefreshSceneCollections()
......@@ -267,6 +272,11 @@ void OBSBasic::on_actionRenameSceneCollection_triggered()
UpdateTitleBar();
RefreshSceneCollections();
if (api) {
api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED);
api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);
}
}
void OBSBasic::on_actionRemoveSceneCollection_triggered()
......@@ -330,6 +340,11 @@ void OBSBasic::on_actionRemoveSceneCollection_triggered()
blog(LOG_INFO, "------------------------------------------------");
UpdateTitleBar();
if (api) {
api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED);
api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);
}
}
void OBSBasic::ChangeSceneCollection()
......@@ -366,4 +381,7 @@ void OBSBasic::ChangeSceneCollection()
blog(LOG_INFO, "------------------------------------------------");
UpdateTitleBar();
if (api)
api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);
}
......@@ -234,6 +234,9 @@ void OBSBasic::TransitionStopped()
SetCurrentScene(scene);
}
if (api)
api->on_event(OBS_FRONTEND_EVENT_TRANSITION_STOPPED);
swapScene = nullptr;
}
......@@ -281,6 +284,9 @@ void OBSBasic::TransitionToScene(OBSSource source, bool force)
obs_scene_release(scene);
obs_source_release(transition);
if (api)
api->on_event(OBS_FRONTEND_EVENT_SCENE_CHANGED);
}
static inline void SetComboTransition(QComboBox *combo, obs_source_t *tr)
......@@ -317,6 +323,9 @@ void OBSBasic::SetTransition(OBSSource transition)
bool configurable = obs_source_configurable(transition);
ui->transitionRemove->setEnabled(configurable);
ui->transitionProps->setEnabled(configurable);
if (api)
api->on_event(OBS_FRONTEND_EVENT_TRANSITION_CHANGED);
}
OBSSource OBSBasic::GetCurrentTransition()
......@@ -378,6 +387,9 @@ void OBSBasic::AddTransition()
ui->transitions->setCurrentIndex(ui->transitions->count() - 1);
CreatePropertiesWindow(source);
obs_source_release(source);
if (api)
api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);
}
}
......@@ -428,6 +440,9 @@ void OBSBasic::on_transitionRemove_clicked()
}
ui->transitions->removeItem(idx);
if (api)
api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);
}
void OBSBasic::RenameTransition()
......
......@@ -373,6 +373,13 @@ void OBSBasic::Save(const char *file)
obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked());
if (api) {
obs_data_t *moduleObj = obs_data_create();
api->on_save(moduleObj);
obs_data_set_obj(saveData, "modules", moduleObj);
obs_data_release(moduleObj);
}
if (!obs_data_save_json_safe(saveData, file, "tmp", "bak"))
blog(LOG_ERROR, "Could not save scene data to %s", file);
......@@ -655,6 +662,12 @@ retryScene:
ui->preview->SetLocked(previewLocked);
ui->actionLockPreview->setChecked(previewLocked);
if (api) {
obs_data_t *modulesObj = obs_data_get_obj(data, "modules");
api->on_load(modulesObj);
obs_data_release(modulesObj);
}
obs_data_release(data);
if (!opt_starting_scene.empty())
......@@ -1019,6 +1032,8 @@ void OBSBasic::ResetOutputs()
#define SHUTDOWN_SEPARATOR \
"==== Shutting down =================================================="
extern obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main);
void OBSBasic::OBSInit()
{
ProfileScope("OBSBasic::OBSInit");
......@@ -1065,6 +1080,8 @@ void OBSBasic::OBSInit()
InitOBSCallbacks();
InitHotkeys();
api = InitializeAPIInterface(this);
AddExtraModulePaths();
blog(LOG_INFO, "---------------------------------");
obs_load_all_modules();
......@@ -2067,6 +2084,9 @@ void OBSBasic::DuplicateSelectedScene()
AddScene(source);
SetCurrentScene(source, true);
obs_scene_release(scene);
if (api)
api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
break;
}
}
......@@ -2076,8 +2096,12 @@ void OBSBasic::RemoveSelectedScene()
OBSScene scene = GetCurrentScene();
if (scene) {
obs_source_t *source = obs_scene_get_source(scene);
if (QueryRemoveSource(source))
if (QueryRemoveSource(source)) {
obs_source_remove(source);
if (api)
api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
}
}
}
......@@ -2639,6 +2663,10 @@ void OBSBasic::closeEvent(QCloseEvent *event)
signalHandlers.clear();
SaveProjectNow();
if (api)
api->on_event(OBS_FRONTEND_EVENT_EXIT);
disableSaving++;
/* Clear all scene data (dialogs, widgets, widget sub-items, scenes,
......@@ -3487,6 +3515,9 @@ void OBSBasic::SceneNameEdited(QWidget *editor,
obs_source_t *source = obs_scene_get_source(scene);
RenameListItem(this, ui->scenes, source, text);
if (api)
api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
UNUSED_PARAMETER(endHint);
}
......@@ -3537,6 +3568,12 @@ void OBSBasic::OpenSceneFilters()
void OBSBasic::StartStreaming()
{
if (outputHandler->StreamingActive())
return;
if (api)
api->on_event(OBS_FRONTEND_EVENT_STREAMING_STARTING);
SaveProject();
ui->streamButton->setEnabled(false);
......@@ -3684,6 +3721,9 @@ void OBSBasic::StreamingStart()
sysTrayStream->setText(ui->streamButton->text());
sysTrayStream->setEnabled(true);
if (api)
api->on_event(OBS_FRONTEND_EVENT_STREAMING_STARTED);
OnActivate();
blog(LOG_INFO, STREAMING_START);
......@@ -3693,6 +3733,9 @@ void OBSBasic::StreamStopping()
{
ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming"));
sysTrayStream->setText(ui->streamButton->text());
if (api)
api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPING);
}
void OBSBasic::StreamingStop(int code)
......@@ -3730,6 +3773,9 @@ void OBSBasic::StreamingStop(int code)
sysTrayStream->setText(ui->streamButton->text());
sysTrayStream->setEnabled(true);
if (api)
api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPED);
OnDeactivate();
blog(LOG_INFO, STREAMING_STOP);
......@@ -3754,6 +3800,9 @@ void OBSBasic::StartRecording()
if (outputHandler->RecordingActive())
return;
if (api)
api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTING);
SaveProject();
outputHandler->StartRecording();
}
......@@ -3762,6 +3811,9 @@ void OBSBasic::RecordStopping()
{
ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording"));
sysTrayRecord->setText(ui->recordButton->text());
if (api)
api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPING);
}
void OBSBasic::StopRecording()
......@@ -3780,6 +3832,9 @@ void OBSBasic::RecordingStart()
ui->recordButton->setText(QTStr("Basic.Main.StopRecording"));
sysTrayRecord->setText(ui->recordButton->text());
if (api)
api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTED);
OnActivate();
blog(LOG_INFO, RECORDING_START);
......@@ -3820,6 +3875,9 @@ void OBSBasic::RecordingStop(int code)
QSystemTrayIcon::Warning);
}
if (api)
api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPED);
OnDeactivate();
}
......
......@@ -30,6 +30,8 @@
#include "window-basic-adv-audio.hpp"
#include "window-basic-filters.hpp"
#include <obs-frontend-internal.hpp>
#include <util/platform.h>
#include <util/threading.h>
#include <util/util.hpp>
......@@ -83,6 +85,7 @@ class OBSBasic : public OBSMainWindow {
friend class OBSBasicPreview;
friend class OBSBasicStatusBar;
friend class OBSBasicSourceSelect;
friend struct OBSStudioAPI;
enum class MoveDir {
Up,
......@@ -92,6 +95,8 @@ class OBSBasic : public OBSMainWindow {
};
private:
obs_frontend_callbacks *api = nullptr;
std::vector<VolControl*> volumes;
std::vector<OBSSignal> signalHandlers;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册