diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index 8fb3bea3a3816d..63e3ff5a445e52 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -6,6 +6,10 @@ #include +#ifdef ENABLE_WAYLAND +#include +#endif + using namespace std; Q_DECLARE_METATYPE(OBSScene); @@ -445,6 +449,76 @@ struct OBSStudioAPI : obs_frontend_callbacks { return true; } + bool obs_frontend_is_browser_available(void) override + { +#ifdef BROWSER_AVAILABLE +#ifdef ENABLE_WAYLAND + return (obs_get_nix_platform() != OBS_NIX_PLATFORM_WAYLAND); +#else + return true; +#endif +#else + return false; +#endif + } + + bool obs_frontend_add_browser_dock( + const char *id, const char *title, + struct obs_frontend_browser_params *params) override + { +#ifdef BROWSER_AVAILABLE + if (!obs_frontend_is_browser_available()) + return false; + + if (main->IsDockObjectNameUsed(QT_UTF8(id))) { + blog(LOG_WARNING, + "Dock id '%s' already used! " + "Duplicate library?", + id); + return false; + } + + PluginBrowserParams dock; + dock.id = QT_UTF8(id); + dock.title = QT_UTF8(title); + + dock.url = QT_UTF8(params->url); + + for (size_t i = 0; i < params->force_popup_urls.num; i++) + dock.forcePopupUrls.append( + params->force_popup_urls.array[i]); + + dock.startupScript = QT_UTF8(params->startup_script.array); + + if (main->IsBrowserInitialised()) + main->AddPluginBrowserDock(dock); + else + main->StorePluginBrowserDock(dock); + + return true; +#else + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(title); + UNUSED_PARAMETER(params); + + return false; +#endif + } + + void obs_frontend_change_browser_dock_url(const char *id, + const char *url) override + { +#ifdef BROWSER_AVAILABLE + if (!id && !url) + return; + + main->ChangePluginBrowserDockUrl(id, url); +#else + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(url); +#endif + } + void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) override { diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index 34747fc07b5ab1..89b48f1678574e 100644 --- a/UI/obs-frontend-api/obs-frontend-api.cpp +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -350,6 +350,26 @@ bool obs_frontend_add_custom_qdock(const char *id, void *dock) : false; } +bool obs_frontend_is_browser_available(void) +{ + return !!callbacks_valid() ? c->obs_frontend_is_browser_available() + : false; +} + +bool obs_frontend_add_browser_dock(const char *id, const char *title, + struct obs_frontend_browser_params *params) +{ + return !!callbacks_valid() + ? c->obs_frontend_add_browser_dock(id, title, params) + : false; +} + +void obs_frontend_change_browser_dock_url(const char *id, const char *url) +{ + if (callbacks_valid()) + c->obs_frontend_change_browser_dock_url(id, url); +} + void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) { diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h index a0913e683359f1..391faa9b83163e 100644 --- a/UI/obs-frontend-api/obs-frontend-api.h +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -81,6 +82,22 @@ obs_frontend_source_list_free(struct obs_frontend_source_list *source_list) da_free(source_list->sources); } +struct obs_frontend_browser_params { + const char *url; + struct dstr startup_script; + DARRAY(char *) force_popup_urls; +}; + +static inline void +obs_frontend_browser_params_free(struct obs_frontend_browser_params *params) +{ + if (params->startup_script.len > 0) + dstr_free(¶ms->startup_script); + + if (params->force_popup_urls.num > 0) + da_free(params->force_popup_urls); +} + #endif //!SWIG /* ------------------------------------------------------------------------- */ @@ -150,6 +167,14 @@ EXPORT void obs_frontend_remove_dock(const char *id); /* takes QDockWidget for dock */ EXPORT bool obs_frontend_add_custom_qdock(const char *id, void *dock); +EXPORT bool obs_frontend_is_browser_available(void); + +EXPORT bool +obs_frontend_add_browser_dock(const char *id, const char *title, + struct obs_frontend_browser_params *params); +EXPORT void obs_frontend_change_browser_dock_url(const char *id, + const char *url); + typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event, void *private_data); diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp index 90a0e06ea9dd5b..bf6a856b5b6bd1 100644 --- a/UI/obs-frontend-api/obs-frontend-internal.hpp +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -73,6 +73,13 @@ struct obs_frontend_callbacks { virtual bool obs_frontend_add_custom_qdock(const char *id, void *dock) = 0; + virtual bool obs_frontend_is_browser_available(void) = 0; + virtual bool obs_frontend_add_browser_dock( + const char *id, const char *title, + struct obs_frontend_browser_params *params) = 0; + virtual void obs_frontend_change_browser_dock_url(const char *id, + const char *url) = 0; + virtual void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) = 0; diff --git a/UI/window-basic-main-browser.cpp b/UI/window-basic-main-browser.cpp index ec99f141e4987b..86ab71817745da 100644 --- a/UI/window-basic-main-browser.cpp +++ b/UI/window-basic-main-browser.cpp @@ -25,6 +25,7 @@ #ifdef BROWSER_AVAILABLE #include +#include "window-dock-browser.hpp" #endif struct QCef; @@ -162,3 +163,70 @@ void OBSBasic::InitBrowserPanelSafeBlock() InitPanelCookieManager(); #endif } + +#ifdef BROWSER_AVAILABLE +bool OBSBasic::IsBrowserInitialised() +{ + return !!cef; +} + +void OBSBasic::StorePluginBrowserDock(const PluginBrowserParams ¶ms) +{ + pluginBrowserDockNames.push_back(params.id); + preInitPluginBrowserDocks.push_back(params); +} + +void OBSBasic::LoadStoredPluginBrowserDock() +{ + for (int i = 0; preInitPluginBrowserDocks.size() > i; i++) + AddPluginBrowserDock(preInitPluginBrowserDocks[i]); + + preInitPluginBrowserDocks.clear(); +} + +void OBSBasic::AddPluginBrowserDock(const PluginBrowserParams ¶ms) +{ + static int panel_version = -1; + if (panel_version == -1) { + panel_version = obs_browser_qcef_version(); + } + + BrowserDock *dock = new BrowserDock(); + dock->setObjectName(params.id); + dock->resize(460, 600); + dock->setMinimumSize(80, 80); + dock->setWindowTitle(params.title); + + QCefWidget *browser = + cef->create_widget(dock, QT_TO_UTF8(params.url), nullptr); + if (browser && panel_version >= 1) + browser->allowAllPopups(true); + + dock->SetWidget(browser); + + if (!params.startupScript.isEmpty()) + browser->setStartupScript(params.startupScript.toStdString()); + + for (int i = 0; params.forcePopupUrls.size() > i; i++) + cef->add_force_popup_url(params.forcePopupUrls[i].toStdString(), + dock); + + if (!pluginBrowserDockNames.contains(dock->objectName())) + pluginBrowserDockNames.push_back(dock->objectName()); + AddDockWidget(dock, Qt::RightDockWidgetArea); + + dock->setFloating(true); + dock->setVisible(false); +} + +void OBSBasic::ChangePluginBrowserDockUrl(const char *id_, const char *url) +{ + QString id = QT_UTF8(id_); + if (pluginBrowserDockNames.contains(id) && + extraDockNames.contains(id)) { + int idx = extraDockNames.indexOf(id); + reinterpret_cast(extraDocks[idx].data()) + ->cefWidget->setURL(url); + } +} +#endif diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 4b57ec2400c18c..96fe2789bb52c7 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -2152,6 +2152,7 @@ void OBSBasic::OBSInit() ui->scenesDock->toggleViewAction()); LoadExtraBrowserDocks(); + LoadStoredPluginBrowserDock(); } #endif @@ -10332,6 +10333,10 @@ void OBSBasic::RemoveDockWidget(const QString &name) extraDockNames.removeAt(idx); extraDocks[idx].clear(); extraDocks.removeAt(idx); +#ifdef BROWSER_AVAILABLE + if (pluginBrowserDockNames.contains(name)) + pluginBrowserDockNames.removeAll(name); +#endif } else if (extraCustomDockNames.contains(name)) { int idx = extraCustomDockNames.indexOf(name); extraCustomDockNames.removeAt(idx); @@ -10352,6 +10357,9 @@ bool OBSBasic::IsDockObjectNameUsed(const QString &name) list << oldExtraDockNames; list << extraDockNames; list << extraCustomDockNames; +#ifdef BROWSER_AVAILABLE + list << pluginBrowserDockNames; +#endif return list.contains(name); } diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 5079b057a3c0b5..fe92249ac587fe 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -139,6 +139,16 @@ class ColorSelect : public QWidget { std::unique_ptr ui; }; +#ifdef BROWSER_AVAILABLE +struct PluginBrowserParams { + QString id; + QString title; + QString url; + QString startupScript; + QStringList forcePopupUrls; +}; +#endif + class OBSBasic : public OBSMainWindow { Q_OBJECT Q_PROPERTY(QIcon imageIcon READ GetImageIcon WRITE SetImageIcon @@ -572,6 +582,15 @@ class OBSBasic : public OBSMainWindow { void ManageExtraBrowserDocks(); void AddExtraBrowserDock(const QString &title, const QString &url, const QString &uuid, bool firstCreate); + + QStringList pluginBrowserDockNames; + QList preInitPluginBrowserDocks; + + bool IsBrowserInitialised(); + void StorePluginBrowserDock(const PluginBrowserParams ¶ms); + void LoadStoredPluginBrowserDock(); + void AddPluginBrowserDock(const PluginBrowserParams ¶ms); + void ChangePluginBrowserDockUrl(const char *id, const char *url); #endif QIcon imageIcon; diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst index a68db69ce4f527..efa9432cd43161 100644 --- a/docs/sphinx/reference-frontend-api.rst +++ b/docs/sphinx/reference-frontend-api.rst @@ -210,6 +210,22 @@ Structures/Enumerations obs_frontend_source_list_free(&scenes); +.. type:: struct obs_frontend_browser_params + + - const char* **url** + - struct dstr **startup_script** + - DARRAY(char*) **force_popup_urls** + +.. code:: cpp + + struct obs_frontend_browser_params params = {0}; + + params.url = "https://obsproject.com/"; + + obs_frontend_add_browser_dock("example", "Example", ¶ms); + + obs_frontend_browser_params_free(¶ms); + .. type:: void (*obs_frontend_cb)(void *private_data) Frontend tool menu callback @@ -242,6 +258,14 @@ Functions --------------------------------------- +.. function:: void obs_frontend_browser_params_free(struct obs_frontend_browser_params *params) + + Frees the startup script and force popup URLs if not empty. + + :param params: Browser parameters with dynamic types to free + +--------------------------------------- + .. function:: void *obs_frontend_get_main_window(void) :return: The QMainWindow pointer to the OBS Studio window @@ -491,6 +515,39 @@ Functions --------------------------------------- +.. function:: bool obs_frontend_is_browser_available(void) + + :return: If browser feature is available (built with obs-browser or + not runnning under Wayland) + +--------------------------------------- + +.. function:: bool obs_frontend_add_browser_dock(const char *id, const char *title, struct obs_frontend_browser_params* params) + + Adds a browser dock with the widget to the UI with a toggle in the Docks + menu. + + Note: Use :c:func:`obs_frontend_remove_dock` to remove the dock + and the id from the UI. + + :param id: Unique identifier of the dock + :param title: Window title of the dock + :param params: Parameters of the browser widget + :return: *true* if the dock was added, *false* if the id was already + used + +--------------------------------------- + +.. function:: void obs_frontend_change_browser_dock_url(const char *id, const char *url) + + Change the URL of browser dock created with + :c:func:`obs_frontend_add_browser_dock`. + + :param id: Unique identifier of the targeted browser dock + :param url: New URL + +--------------------------------------- + .. function:: void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) Adds a callback that will be called when a frontend event occurs.