diff --git a/CMakeLists.txt b/CMakeLists.txt index b3d95fd4..4f0bf765 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1494,6 +1494,8 @@ if(T_CHECK) "source/ui/ui-about.cpp" "source/ui/ui-about-entry.hpp" "source/ui/ui-about-entry.cpp" + "source/ui/ui-obs-browser-widget.hpp" + "source/ui/ui-obs-browser-widget.cpp" ) list(APPEND PROJECT_INCLUDE_DIRS "source/ui" diff --git a/source/ui/ui-obs-browser-widget.cpp b/source/ui/ui-obs-browser-widget.cpp new file mode 100644 index 00000000..fd1b9dca --- /dev/null +++ b/source/ui/ui-obs-browser-widget.cpp @@ -0,0 +1,93 @@ +// AUTOGENERATED COPYRIGHT HEADER START +// Copyright (C) 2023 Michael Fabian 'Xaymar' Dirks +// AUTOGENERATED COPYRIGHT HEADER END + +#include "ui-obs-browser-widget.hpp" +#include "plugin.hpp" + +#include "warning-disable.hpp" +#include + +#include <../plugins/obs-browser/panel/browser-panel.hpp> +#include "warning-enable.hpp" + +streamfx::ui::obs_browser_cef::obs_browser_cef() +{ + // Load the "obs-browser" module. + _module = util::library::load(obs_get_module("obs-browser")); + auto fn = reinterpret_cast(_module->load_symbol("obs_browser_create_qcef")); + if (!fn) { + throw std::runtime_error("Unable to create Browser Panel."); + } + + // Create a QCef instance and initialize it. + _cef = fn(); + if (!_cef) { + throw std::runtime_error("Unable to initialize for CEF-based Browser Panel."); + } + reinterpret_cast(_cef)->init_browser(); + reinterpret_cast(_cef)->wait_for_browser_init(); + + // Create a generic Cookie manager for widgets. + _cookie = + reinterpret_cast(_cef)->create_cookie_manager(streamfx::config_file_path("cookies").u8string(), false); +} + +streamfx::ui::obs_browser_cef::~obs_browser_cef() +{ + delete reinterpret_cast(_cookie); + delete reinterpret_cast(_cef); +} + +void* streamfx::ui::obs_browser_cef::cef() +{ + return _cef; +} + +void* streamfx::ui::obs_browser_cef::cookie_manager() +{ + return _cookie; +} + +std::shared_ptr streamfx::ui::obs_browser_cef::instance() +{ + static std::weak_ptr ptr; + static std::mutex lock; + + std::lock_guard lg(lock); + if (!ptr.expired()) { + return ptr.lock(); + } + + std::shared_ptr sintance{new obs_browser_cef()}; + ptr = sintance; + return sintance; +} + +streamfx::ui::obs_browser_widget::obs_browser_widget(QUrl url, QWidget* parent) : QWidget(parent) +{ + _cef = obs_browser_cef::instance(); + _widget = reinterpret_cast(_cef->cef()) + ->create_widget(this, url.toString().toStdString(), + reinterpret_cast(_cef->cookie_manager())); + if (!_widget) { + throw std::runtime_error("Failed to create QCefWidget."); + } + + // Add a proper layout. + _layout = new QHBoxLayout(); + _layout->setContentsMargins(0, 0, 0, 0); + _layout->setSpacing(0); + this->setLayout(_layout); + _layout->addWidget(_widget); + + // Disable all popups. + dynamic_cast(_widget)->allowAllPopups(false); +} + +streamfx::ui::obs_browser_widget::~obs_browser_widget() {} + +void streamfx::ui::obs_browser_widget::set_url(QUrl url) +{ + dynamic_cast(_widget)->setURL(url.toString().toStdString()); +} diff --git a/source/ui/ui-obs-browser-widget.hpp b/source/ui/ui-obs-browser-widget.hpp new file mode 100644 index 00000000..c148ded9 --- /dev/null +++ b/source/ui/ui-obs-browser-widget.hpp @@ -0,0 +1,49 @@ +// AUTOGENERATED COPYRIGHT HEADER START +// Copyright (C) 2023 Michael Fabian 'Xaymar' Dirks +// AUTOGENERATED COPYRIGHT HEADER END + +#pragma once +#include "util/util-library.hpp" + +#include "warning-disable.hpp" +#include +#include +#include +#include +#include "warning-enable.hpp" + +namespace streamfx::ui { + class obs_browser_cef { + std::shared_ptr<::streamfx::util::library> _module; + + void* _cef; + void* _cookie; + + private: + obs_browser_cef(); + + public: + ~obs_browser_cef(); + + void* cef(); + + void* cookie_manager(); + + public: // Singleton + static std::shared_ptr instance(); + }; + + class obs_browser_widget : public QWidget { + Q_OBJECT + + std::shared_ptr _cef; + QWidget* _widget; + QHBoxLayout* _layout; + + public: + obs_browser_widget(QUrl url, QWidget* parent = nullptr); + virtual ~obs_browser_widget(); + + void set_url(QUrl url); + }; +} // namespace streamfx::ui diff --git a/source/ui/ui.cpp b/source/ui/ui.cpp index ffa985d8..0a2ab673 100644 --- a/source/ui/ui.cpp +++ b/source/ui/ui.cpp @@ -9,6 +9,7 @@ #include "configuration.hpp" #include "obs/obs-tools.hpp" #include "plugin.hpp" +#include "ui/ui-obs-browser-widget.hpp" #include "warning-disable.hpp" #include @@ -37,6 +38,8 @@ constexpr std::string_view _url_discord = "https://s.xaymar.com/streamfx-dc"; constexpr std::string_view _url_twitter = "https://s.xaymar.com/streamfx-tw"; constexpr std::string_view _url_youtube = "https://s.xaymar.com/streamfx-yt"; +static std::shared_ptr _obs_browser_cef; + inline void qt_init_resource() { Q_INIT_RESOURCE(streamfx); @@ -107,6 +110,9 @@ void streamfx::ui::handler::on_obs_loaded() _translator = new streamfx::ui::translator(this); QCoreApplication::installTranslator(_translator); + // Pre-load CEF. + _obs_browser_cef = streamfx::ui::obs_browser_cef::instance(); + // Create the 'About StreamFX' dialog. _about_dialog = new streamfx::ui::about(); @@ -201,6 +207,9 @@ void streamfx::ui::handler::on_obs_loaded() void streamfx::ui::handler::on_obs_exit() { + // Release CEF + _obs_browser_cef.reset(); + // Remove translator. QCoreApplication::removeTranslator(_translator);