diff --git a/buildspec.json b/buildspec.json index 2f306cd..5c37151 100644 --- a/buildspec.json +++ b/buildspec.json @@ -40,7 +40,7 @@ "displayName": "Elgato Marketplace Connect", "versionMajor": 1, "versionMinor": 0, - "versionPatch": 1, + "versionPatch": 2, "buildNumber": 0, "releaseType": "release", "author": "Elgato", diff --git a/cmake/windows/helpers.cmake b/cmake/windows/helpers.cmake index 6887eb4..d224660 100644 --- a/cmake/windows/helpers.cmake +++ b/cmake/windows/helpers.cmake @@ -60,6 +60,9 @@ function(set_target_properties_plugin target) configure_file(cmake/windows/resources/resource.rc.in "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.rc") target_sources(${CMAKE_PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.rc") + + configure_file(cmake/windows/resources/elgato-marketplace-connect-loader.rc.in "${CMAKE_CURRENT_BINARY_DIR}/elgato-marketplace-connect-loader.rc") + target_sources(${CMAKE_PROJECT_NAME}-loader PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/elgato-marketplace-connect-loader.rc") endfunction() # Helper function to add resources into bundle diff --git a/cmake/windows/resources/elgato-marketplace-connect-loader.rc.in b/cmake/windows/resources/elgato-marketplace-connect-loader.rc.in new file mode 100644 index 0000000..f414444 --- /dev/null +++ b/cmake/windows/resources/elgato-marketplace-connect-loader.rc.in @@ -0,0 +1,110 @@ +// Microsoft Visual C++ generated resource script. +// +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by elgato-marketplace-connect-loader.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 + PRODUCTVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "${PLUGIN_AUTHOR}" + VALUE "FileDescription", "Marketplace Connect for OBS" + VALUE "FileVersion", "${PROJECT_VERSION}" + VALUE "InternalName", "elgato-m.exe" + VALUE "LegalCopyright", "(C) ${CURRENT_YEAR} ${PLUGIN_AUTHOR}" + VALUE "OriginalFilename", "elgato-m.exe" + VALUE "ProductName", "Marketplace Connect for OBS" + VALUE "ProductVersion", "${PROJECT_VERSION}" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/loader/main.cpp b/loader/main.cpp index 5028fd1..0669a11 100644 --- a/loader/main.cpp +++ b/loader/main.cpp @@ -47,7 +47,7 @@ DWORD GetProcessIdFromExe(const std::wstring& exeName) { // Create a snapshot of all running processes HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { - printf("Error taking snapshot of processes."); + std::cerr << "[ERROR]: Error taking snapshot of processes." << std::endl; return 0; } @@ -100,17 +100,17 @@ int send_auth_to_obs(std::string payload) { sa.bInheritHandle = FALSE; int connect_attempts_remaining = 6; - + std::cout << "Attempting to connect to Marketplace Connect Plugin" << std::flush; while (connect_attempts_remaining-- > 0 && pipe == INVALID_HANDLE_VALUE) { pipe_number = 0; while (pipe_number < 10) { attempt_name = base_name + std::to_string(pipe_number); - printf("Attempting %s\n", attempt_name.c_str()); + std::cout << "." << std::flush; pipe = CreateFileA(attempt_name.c_str(), GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, NULL); if (pipe != INVALID_HANDLE_VALUE) { - printf("Success\n"); + std::cout << "\nSuccess" << std::endl; break; } pipe_number++; @@ -120,14 +120,14 @@ int send_auth_to_obs(std::string payload) { } } if (pipe == INVALID_HANDLE_VALUE) { - printf("Could not open named pipe!"); + std::cerr << "[ERROR] Could not find connection Marketplace Connect Plugin." << std::endl; return 1; } DWORD mode = PIPE_READMODE_MESSAGE; auto success = SetNamedPipeHandleState(pipe, &mode, NULL, NULL); if (!success) { CloseHandle(pipe); - printf("Could not configure named pipe!"); + std::cerr << "[ERROR] Could not negotiate connection with Marketplace Connect Plugin." << std::endl; return 1; } @@ -135,7 +135,67 @@ int send_auth_to_obs(std::string payload) { success = WriteFile(pipe, payload.c_str(), static_cast(payload.size()), &written, NULL); if (!success || written < payload.size()) { - printf("Failed to write to named pipe!"); + std::cerr << "[ERROR] Could not send data to Marketplace Connect Plugin." << std::endl; + CloseHandle(pipe); + return 1; + } + + CloseHandle(pipe); + return 0; +} + +int open_obs_mp_window() +{ + int pipe_number = 0; + std::string pipe_name = "elgato_cloud"; + std::string base_name = "\\\\.\\pipe\\" + pipe_name; + std::string attempt_name; + HANDLE pipe = INVALID_HANDLE_VALUE; + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + int connect_attempts_remaining = 60; + + std::cout << "Waiting for OBS to launch" << std::flush; + + while (connect_attempts_remaining-- > 0 && + pipe == INVALID_HANDLE_VALUE) { + pipe_number = 0; + attempt_name = base_name + std::to_string(pipe_number); + std::cout << "." << std::flush; + pipe = CreateFileA(attempt_name.c_str(), GENERIC_WRITE, + 0, &sa, OPEN_EXISTING, 0, NULL); + if (pipe != INVALID_HANDLE_VALUE) { + std::cout << "\nConnected To OBS" << std::endl; + break; + } + if (pipe == INVALID_HANDLE_VALUE) { + Sleep(1000); + } + } + if (pipe == INVALID_HANDLE_VALUE) { + std::cerr << "[ERROR] Could not find connection Marketplace Connect Plugin." << std::endl; + return 1; + } + DWORD mode = PIPE_READMODE_MESSAGE; + auto success = SetNamedPipeHandleState(pipe, &mode, NULL, NULL); + if (!success) { + CloseHandle(pipe); + std::cerr << "[ERROR] Could not negotiate connection with Marketplace Connect Plugin." << std::endl; + return 1; + } + + DWORD written = 0; + std::string payload = "elgatolink://open"; + success = WriteFile(pipe, payload.c_str(), static_cast(payload.size()), &written, + NULL); + if (!success || written < payload.size()) { + std::cerr << "[ERROR] Could not send data to Marketplace Connect Plugin." << std::endl; CloseHandle(pipe); return 1; } @@ -169,12 +229,12 @@ int launch_obs() STARTUPINFOW si = { sizeof(STARTUPINFO) }; PROCESS_INFORMATION pi; - printf("Try create %ls with %ls\n", launch_path.c_str(), - wd.c_str()); + //printf("Try create %ls with %ls\n", launch_path.c_str(), + // wd.c_str()); //WCHAR lpCommandLine[] = L""; if (CreateProcess(launch_path.c_str(), NULL, NULL, NULL, FALSE, 0, NULL, wd.c_str(), &si, &pi) == 0) { - printf("Failed to launch OBS."); + std::cerr << "[ERROR] Failed to launch OBS." << std::endl; return 1; } @@ -185,7 +245,7 @@ int launch_obs() } else if (running) { DWORD processId = GetProcessIdFromExe(L"obs64.exe"); if (processId == 0) { - printf("Process not found!"); + std::cerr << "[ERROR] OBS appears to have quit." << std::endl; return 1; } EnumWindows(WindowToForeground, processId); @@ -198,26 +258,31 @@ bool obs_is_running(std::wstring name) { if (!iswalnum(name[i])) name[i] = L'_'; } - - printf("CHECKING %ls\n", name.c_str()); HANDLE h = OpenMutexW(SYNCHRONIZE, false, name.c_str()); return !!h; } int main(int argc, char *argv[]) { if (argc < 2) { - printf("Please provide an argument"); + std::cerr << "[ERROR] No argument provided." << std::endl; + system("pause"); return 1; } + int resp; std::string payload = argv[1]; if (payload.find("elgatolink://auth") == 0) { // Send auth to OBS which requested it. - return send_auth_to_obs(payload); + resp = send_auth_to_obs(payload); } else { - return launch_obs(); + resp = launch_obs(); + if(resp == 0) resp = open_obs_mp_window(); + } + + if (resp == 1) { + system("pause"); } - printf("No obs found to launch."); - return 1; + + return resp; } diff --git a/src/elgato-cloud-data.cpp b/src/elgato-cloud-data.cpp index 873b9e1..ef54c35 100644 --- a/src/elgato-cloud-data.cpp +++ b/src/elgato-cloud-data.cpp @@ -59,18 +59,41 @@ std::unique_lock *GetElgatoCloudLoopLock() ElgatoCloud::ElgatoCloud(obs_module_t *m) { + _openOnLaunch = false; + _obsReady = false; _modulePtr = m; //_translate = t; _securerand = QRandomGenerator::securelySeeded(); + obs_frontend_add_event_callback(ElgatoCloud::FrontEndEventHandler, this); _Initialize(); _Listen(); } ElgatoCloud::~ElgatoCloud() { + obs_frontend_remove_event_callback(ElgatoCloud::FrontEndEventHandler, this); obs_data_release(_config); } +void ElgatoCloud::FrontEndEventHandler(enum obs_frontend_event event, void* data) +{ + auto ec = static_cast(data); + switch (event) { + case OBS_FRONTEND_EVENT_FINISHED_LOADING: + ec->_obsReady = true; + if (ec->_openOnLaunch) { + ec->_openOnLaunch = false; + QMetaObject::invokeMethod( + QCoreApplication::instance() + ->thread(), + [ec]() { + OpenElgatoCloudWindow(); + }); + } + break; + } +} + obs_data_t *ElgatoCloud::GetConfig() { obs_data_addref(_config); @@ -169,6 +192,7 @@ void ElgatoCloud::_Listen() { _listenThread = std::thread([this]() { listen_on_pipe("elgato_cloud", [this](std::string d) { + obs_log(LOG_INFO, "Pipe received: %s", d.c_str()); if (d.find("elgatolink://auth") == 0) { if (mainWindowOpen && window) { QMetaObject::invokeMethod( @@ -242,6 +266,33 @@ void ElgatoCloud::_Listen() authorizing = false; return; } + else if (d.find("elgatolink://open") == 0) + { + obs_log(LOG_INFO, "OPEN COMMAND RECEIEVED!"); + if (!_obsReady) { + _openOnLaunch = true; + return; + } + if (mainWindowOpen && window) { + QMetaObject::invokeMethod( + QCoreApplication::instance() + ->thread(), + [this]() { + //window->setLoading(); + window->show(); + window->raise(); + window->activateWindow(); + LoadPurchasedProducts(); + }); + } else { + QMetaObject::invokeMethod( + QCoreApplication::instance() + ->thread(), + [this]() { + OpenElgatoCloudWindow(); + }); + } + } }); }); diff --git a/src/elgato-cloud-data.hpp b/src/elgato-cloud-data.hpp index ad33836..c3064fb 100644 --- a/src/elgato-cloud-data.hpp +++ b/src/elgato-cloud-data.hpp @@ -19,6 +19,7 @@ with this program. If not, see #pragma once #include +#include #include #include @@ -72,6 +73,7 @@ class ElgatoCloud { bool mainWindowOpen = false; ElgatoCloudWindow *window = nullptr; inline bool MakerToolsOnStart() const { return _makerToolsOnStart; } + static void FrontEndEventHandler(enum obs_frontend_event event, void* data); private: void _Initialize(); @@ -95,6 +97,8 @@ class ElgatoCloud { std::string _skipUpdate; obs_data_t *_config; bool _makerToolsOnStart; + bool _openOnLaunch; + bool _obsReady; }; class ElgatoCloudThread : public QThread {