From a032bcc798272f65dd01acb2235685346dc18628 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 27 Jul 2018 21:35:08 -0700 Subject: [PATCH] UI: Add intro startup page (windows) Allows the ability to show a web page via CEF to the users on startup to present and announce new features. --- UI/CMakeLists.txt | 4 ++ UI/obs-app.cpp | 1 + UI/win-update/win-update.cpp | 117 +++++++++++++++++++++++++++++++++- UI/win-update/win-update.hpp | 12 ++++ UI/window-basic-main.cpp | 119 +++++++++++++++++++++++++++++++++++ UI/window-basic-main.hpp | 3 + plugins/obs-browser | 2 +- 7 files changed, 256 insertions(+), 2 deletions(-) diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 85a130669..1b244abb8 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -55,6 +55,8 @@ include_directories(${FFMPEG_INCLUDE_DIRS}) include_directories(SYSTEM "obs-frontend-api") include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs") include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/deps/libff") +include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/deps/json11") +include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/plugins/obs-browser/panel") find_package(Libcurl REQUIRED) include_directories(${LIBCURL_INCLUDE_DIRS}) @@ -127,6 +129,7 @@ endif() set(obs_SOURCES ${obs_PLATFORM_SOURCES} ${obs_libffutil_SOURCES} + ../deps/json11/json11.cpp obs-app.cpp api-interface.cpp window-basic-main.cpp @@ -178,6 +181,7 @@ set(obs_SOURCES set(obs_HEADERS ${obs_PLATFORM_HEADERS} ${obs_libffutil_HEADERS} + ../deps/json11/json11.hpp obs-app.hpp platform.hpp window-main.hpp diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index ce106f521..5d93d05f9 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -367,6 +367,7 @@ bool OBSApp::InitGlobalConfigDefaults() config_set_default_string(globalConfig, "General", "Language", DEFAULT_LANG); config_set_default_uint(globalConfig, "General", "MaxLogs", 10); + config_set_default_int(globalConfig, "General", "InfoIncrement", -1); config_set_default_string(globalConfig, "General", "ProcessPriority", "Normal"); config_set_default_bool(globalConfig, "General", "EnableAutoUpdates", diff --git a/UI/win-update/win-update.cpp b/UI/win-update/win-update.cpp index 510fddfb7..89467a876 100644 --- a/UI/win-update/win-update.cpp +++ b/UI/win-update/win-update.cpp @@ -27,11 +27,15 @@ using namespace std; #define WIN_MANIFEST_URL "https://obsproject.com/update_studio/manifest.json" #endif +#ifndef WIN_WHATSNEW_URL +#define WIN_WHATSNEW_URL "https://obsproject.com/update_studio/whatsnew.json" +#endif + #ifndef WIN_UPDATER_URL #define WIN_UPDATER_URL "https://obsproject.com/update_studio/updater.exe" #endif -static HCRYPTPROV provider = 0; +static __declspec(thread) HCRYPTPROV provider = 0; #pragma pack(push, r1, 1) @@ -780,3 +784,114 @@ try { } catch (string text) { blog(LOG_WARNING, "%s: %s", __FUNCTION__, text.c_str()); } + +/* ------------------------------------------------------------------------ */ + +void WhatsNewInfoThread::run() +try { + long responseCode; + vector extraHeaders; + string text; + string error; + string signature; + CryptProvider localProvider; + BYTE whatsnewHash[BLAKE2_HASH_LENGTH]; + bool success; + + BPtr whatsnewPath = GetConfigPathPtr( + "obs-studio\\updates\\whatsnew.json"); + + /* ----------------------------------- * + * create signature provider */ + + if (!CryptAcquireContext(&localProvider, + nullptr, + MS_ENH_RSA_AES_PROV, + PROV_RSA_AES, + CRYPT_VERIFYCONTEXT)) + throw strprintf("CryptAcquireContext failed: %lu", + GetLastError()); + + provider = localProvider; + + /* ----------------------------------- * + * avoid downloading json again */ + + if (CalculateFileHash(whatsnewPath, whatsnewHash)) { + char hashString[BLAKE2_HASH_STR_LENGTH]; + HashToString(whatsnewHash, hashString); + + string header = "If-None-Match: "; + header += hashString; + extraHeaders.push_back(move(header)); + } + + /* ----------------------------------- * + * get current install GUID */ + + const char *pguid = config_get_string(GetGlobalConfig(), + "General", "InstallGUID"); + string guid; + if (pguid) + guid = pguid; + + if (guid.empty()) { + GenerateGUID(guid); + + if (!guid.empty()) + config_set_string(GetGlobalConfig(), + "General", "InstallGUID", + guid.c_str()); + } + + if (!guid.empty()) { + string header = "X-OBS2-GUID: "; + header += guid; + extraHeaders.push_back(move(header)); + } + + /* ----------------------------------- * + * get json from server */ + + success = GetRemoteFile(WIN_WHATSNEW_URL, text, error, &responseCode, + nullptr, nullptr, extraHeaders, &signature); + + if (!success || (responseCode != 200 && responseCode != 304)) { + if (responseCode == 404) + return; + + throw strprintf("Failed to fetch whatsnew file: %s", + error.c_str()); + } + + /* ----------------------------------- * + * verify file signature */ + + if (responseCode == 200) { + success = CheckDataSignature(text, "whatsnew", + signature.data(), signature.size()); + if (!success) + throw string("Invalid whatsnew signature"); + } + + /* ----------------------------------- * + * write or load json */ + + if (responseCode == 200) { + if (!QuickWriteFile(whatsnewPath, text.data(), text.size())) + throw strprintf("Could not write file '%s'", + whatsnewPath.Get()); + } else { + if (!QuickReadFile(whatsnewPath, text)) + throw strprintf("Could not read file '%s'", + whatsnewPath.Get()); + } + + /* ----------------------------------- * + * success */ + + emit Result(QString::fromUtf8(text.c_str())); + +} catch (string text) { + blog(LOG_WARNING, "%s: %s", __FUNCTION__, text.c_str()); +} diff --git a/UI/win-update/win-update.hpp b/UI/win-update/win-update.hpp index 47bdd03be..c1bd8fb8b 100644 --- a/UI/win-update/win-update.hpp +++ b/UI/win-update/win-update.hpp @@ -21,3 +21,15 @@ private slots: public: AutoUpdateThread(bool manualUpdate_) : manualUpdate(manualUpdate_) {} }; + +class WhatsNewInfoThread : public QThread { + Q_OBJECT + + virtual void run() override; + +signals: + void Result(const QString &text); + +public: + inline WhatsNewInfoThread() {} +}; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index ad14f7d39..6793a640a 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -68,8 +68,19 @@ #include #include +#ifdef _WIN32 +#include +#endif + +#include + +using namespace json11; using namespace std; +#ifdef _WIN32 +static CREATE_BROWSER_WIDGET_PROC create_browser_widget = nullptr; +#endif + namespace { template @@ -1429,6 +1440,10 @@ void OBSBasic::OBSInit() blog(LOG_INFO, "---------------------------------"); obs_post_load_modules(); +#ifdef _WIN32 + create_browser_widget = obs_browser_init_panel(); +#endif + CheckForSimpleModeX264Fallback(); blog(LOG_INFO, STARTUP_SEPARATOR); @@ -1649,6 +1664,21 @@ void OBSBasic::OnFirstLoad() { if (api) api->on_event(OBS_FRONTEND_EVENT_FINISHED_LOADING); + +#ifdef _WIN32 + /* Attempt to load init screen if available */ + if (create_browser_widget) { + WhatsNewInfoThread *wnit = new WhatsNewInfoThread(); + if (wnit) { + connect(wnit, &WhatsNewInfoThread::Result, + this, &OBSBasic::ReceivedIntroJson); + } + if (wnit) { + introCheckThread = wnit; + introCheckThread->start(); + } + } +#endif } void OBSBasic::DeferredLoad(const QString &file, int requeueCount) @@ -1666,6 +1696,93 @@ void OBSBasic::DeferredLoad(const QString &file, int requeueCount) OnFirstLoad(); } +/* shows a "what's new" page on startup of new versions using CEF */ +void OBSBasic::ReceivedIntroJson(const QString &text) +{ +#ifdef _WIN32 + std::string err; + Json json = Json::parse(QT_TO_UTF8(text), err); + if (!err.empty()) + return; + + std::string info_url; + int info_increment = -1; + + /* check to see if there's an info page for this version */ + const Json::array &items = json.array_items(); + for (const Json &item : items) { + const std::string &version = item["version"].string_value(); + const std::string &url = item["url"].string_value(); + int increment = item["increment"].int_value(); + + int major = 0; + int minor = 0; + + sscanf(version.c_str(), "%d.%d", &major, &minor); + if (major == LIBOBS_API_MAJOR_VER && + minor == LIBOBS_API_MINOR_VER) { + info_url = url; + info_increment = increment; + } + } + + /* this version was not found, or no info for this version */ + if (info_increment == -1) { + return; + } + + uint32_t lastVersion = config_get_int(App()->GlobalConfig(), "General", + "LastVersion"); + + int current_version_increment = -1; + + if (lastVersion < LIBOBS_API_VER) { + config_set_int(App()->GlobalConfig(), "General", + "InfoIncrement", -1); + } else { + current_version_increment = config_get_int( + App()->GlobalConfig(), "General", + "InfoIncrement"); + } + + if (info_increment <= current_version_increment) { + return; + } + + config_set_int(App()->GlobalConfig(), "General", + "InfoIncrement", info_increment); + + QDialog dlg(this); + dlg.setWindowTitle("What's New"); + dlg.resize(600, 600); + + QCefWidget *cefWidget = create_browser_widget(nullptr, info_url); + if (!cefWidget) { + return; + } + + connect(cefWidget, SIGNAL(titleChanged(const QString &)), + &dlg, SLOT(setWindowTitle(const QString &))); + + QPushButton *close = new QPushButton(QTStr("Close")); + connect(close, &QAbstractButton::clicked, + &dlg, &QDialog::accept); + + QHBoxLayout *bottomLayout = new QHBoxLayout(); + bottomLayout->addStretch(); + bottomLayout->addWidget(close); + bottomLayout->addStretch(); + + QVBoxLayout *topLayout = new QVBoxLayout(&dlg); + topLayout->addWidget(cefWidget); + topLayout->addLayout(bottomLayout); + + dlg.exec(); +#else + UNUSED_PARAMETER(text); +#endif +} + void OBSBasic::UpdateMultiviewProjectorMenu() { multiviewProjectorMenu->clear(); @@ -3389,6 +3506,8 @@ void OBSBasic::closeEvent(QCloseEvent *event) blog(LOG_INFO, SHUTDOWN_SEPARATOR); + if (introCheckThread) + introCheckThread->wait(); if (updateCheckThread) updateCheckThread->wait(); if (logUploadThread) diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index c2c9997bd..ecf7a4de0 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -139,6 +139,7 @@ private: bool copyVisible = true; QPointer updateCheckThread; + QPointer introCheckThread; QPointer logUploadThread; QPointer interaction; @@ -369,6 +370,8 @@ private: obs_data_array_t *SaveProjectors(); void LoadSavedProjectors(obs_data_array_t *savedProjectors); + void ReceivedIntroJson(const QString &text); + public slots: void DeferSaveBegin(); void DeferSaveEnd(); diff --git a/plugins/obs-browser b/plugins/obs-browser index f5083a1cc..89aa4ee8b 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit f5083a1cc4294b540e9df94663eae88ab42a86fa +Subproject commit 89aa4ee8b6c7ae12acbd90158feca1fe61dedf7d