From 2c018ca174a5ab640c5ca48dcc0e5fdd8ea059e7 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 6 Jun 2021 05:06:17 -0700 Subject: [PATCH] RequestHandler: Add some requests from the General category --- src/requesthandler/RequestHandler.cpp | 6 + src/requesthandler/RequestHandler.h | 8 ++ src/requesthandler/RequestHandler_General.cpp | 132 +++++++++++++++++- src/requesthandler/rpc/RequestStatus.h | 4 + src/utils/Obs.cpp | 52 +++++++ src/utils/Utils.h | 11 +- 6 files changed, 208 insertions(+), 5 deletions(-) diff --git a/src/requesthandler/RequestHandler.cpp b/src/requesthandler/RequestHandler.cpp index ac28cce0..529bd3ff 100644 --- a/src/requesthandler/RequestHandler.cpp +++ b/src/requesthandler/RequestHandler.cpp @@ -7,6 +7,12 @@ const std::map RequestHandler::_handlerMap // General {"GetVersion", &RequestHandler::GetVersion}, {"BroadcastCustomEvent", &RequestHandler::BroadcastCustomEvent}, + {"GetHotkeyList", &RequestHandler::GetHotkeyList}, + {"TriggerHotkeyByName", &RequestHandler::TriggerHotkeyByName}, + {"TriggerHotkeyByKeySequence", &RequestHandler::TriggerHotkeyByKeySequence}, + {"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled}, + {"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled}, + {"Sleep", &RequestHandler::Sleep}, }; RequestResult RequestHandler::ProcessRequest(const Request& request) diff --git a/src/requesthandler/RequestHandler.h b/src/requesthandler/RequestHandler.h index c0e5c521..7458fcdc 100644 --- a/src/requesthandler/RequestHandler.h +++ b/src/requesthandler/RequestHandler.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include "rpc/Request.h" #include "rpc/RequestResult.h" @@ -17,6 +19,12 @@ class RequestHandler { private: RequestResult GetVersion(const Request&); RequestResult BroadcastCustomEvent(const Request&); + RequestResult GetHotkeyList(const Request&); + RequestResult TriggerHotkeyByName(const Request&); + RequestResult TriggerHotkeyByKeySequence(const Request&); + RequestResult GetStudioModeEnabled(const Request&); + RequestResult SetStudioModeEnabled(const Request&); + RequestResult Sleep(const Request&); static const std::map _handlerMap; }; diff --git a/src/requesthandler/RequestHandler_General.cpp b/src/requesthandler/RequestHandler_General.cpp index 66f7387b..a4f8c423 100644 --- a/src/requesthandler/RequestHandler_General.cpp +++ b/src/requesthandler/RequestHandler_General.cpp @@ -1,3 +1,5 @@ +#include + #include "../obs-websocket.h" #include "../WebSocketServer.h" @@ -7,7 +9,21 @@ RequestResult RequestHandler::GetVersion(const Request& request) { - return RequestResult::Success(); + json responseData; + + responseData["obsVersion"] = Utils::Obs::StringHelper::GetObsVersionString(); + responseData["obsWebSocketVersion"] = OBS_WEBSOCKET_VERSION; + responseData["rpcVersion"] = OBS_WEBSOCKET_RPC_VERSION; + responseData["availableRequests"] = GetRequestList(); + + QList imageWriterFormats = QImageWriter::supportedImageFormats(); + std::vector supportedImageFormats; + for (const QByteArray& format : imageWriterFormats) { + supportedImageFormats.push_back(format.toStdString()); + } + responseData["supportedImageFormats"] = supportedImageFormats; + + return RequestResult::Success(responseData); } RequestResult RequestHandler::BroadcastCustomEvent(const Request& request) @@ -26,3 +42,117 @@ RequestResult RequestHandler::BroadcastCustomEvent(const Request& request) return RequestResult::Success(); } + +RequestResult RequestHandler::GetHotkeyList(const Request& request) +{ + json responseData; + + responseData["hotkeys"] = Utils::Obs::ListHelper::GetHotkeyNameList(); + + return RequestResult::Success(responseData); +} + +RequestResult RequestHandler::TriggerHotkeyByName(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + if (!request.ValidateString("hotkeyName", statusCode, comment)) { + return RequestResult::Error(statusCode, comment); + } + + obs_hotkey_t *hotkey = Utils::Obs::SearchHelper::GetHotkeyByName(request.RequestData["hotkeyName"]); + if (!hotkey) + return RequestResult::Error(RequestStatus::HotkeyNotFound, "Unable to find a hotkey by that name."); + + obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hotkey), true); + + return RequestResult::Success(); +} + +RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request& request) +{ + obs_key_combination_t combo = {0}; + + RequestStatus::RequestStatus statusCode = RequestStatus::NoError; + std::string comment; + if (!request.ValidateString("keyId", statusCode, comment) && statusCode != RequestStatus::MissingRequestParameter) { + if (!request.IgnoreNonFatalRequestChecks) + return RequestResult::Error(statusCode, comment); + } else if (statusCode != RequestStatus::MissingRequestParameter) { + std::string keyId = request.RequestData["keyId"]; + combo.key = obs_key_from_name(keyId.c_str()); + } + + statusCode = RequestStatus::NoError; + if (!request.ValidateObject("keyModifiers", statusCode, comment)) { + if (statusCode != RequestStatus::MissingRequestParameter && statusCode != RequestStatus::RequestParameterEmpty) + return RequestResult::Error(statusCode, comment); + } else { + uint32_t keyModifiers = 0; + if (request.RequestData["keyModifiers"].contains("shift") && request.RequestData["keyModifiers"]["shift"].is_boolean() && request.RequestData["keyModifiers"]["shift"].get()) + keyModifiers |= INTERACT_SHIFT_KEY; + if (request.RequestData["keyModifiers"].contains("control") && request.RequestData["keyModifiers"]["control"].is_boolean() && request.RequestData["keyModifiers"]["control"].get()) + keyModifiers |= INTERACT_CONTROL_KEY; + if (request.RequestData["keyModifiers"].contains("alt") && request.RequestData["keyModifiers"]["alt"].is_boolean() && request.RequestData["keyModifiers"]["alt"].get()) + keyModifiers |= INTERACT_ALT_KEY; + if (request.RequestData["keyModifiers"].contains("command") && request.RequestData["keyModifiers"]["command"].is_boolean() && request.RequestData["keyModifiers"]["command"].get()) + keyModifiers |= INTERACT_COMMAND_KEY; + combo.modifiers = keyModifiers; + } + + if (!combo.modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) + return RequestResult::Error(RequestStatus::CannotAct, "Your provided request parameters cannot be used to trigger a hotkey."); + + // Apparently things break when you don't start by setting the combo to false + obs_hotkey_inject_event(combo, false); + obs_hotkey_inject_event(combo, true); + obs_hotkey_inject_event(combo, false); + + return RequestResult::Success(); +} + +RequestResult RequestHandler::GetStudioModeEnabled(const Request& request) +{ + json responseData; + + responseData["studioModeEnabled"] = obs_frontend_preview_program_mode_active(); + + return RequestResult::Success(responseData); +} + +RequestResult RequestHandler::SetStudioModeEnabled(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + if (!request.ValidateBoolean("studioModeEnabled", statusCode, comment)) { + return RequestResult::Error(statusCode, comment); + } + + // Avoid queueing tasks if nothing will change + if (obs_frontend_preview_program_mode_active() != request.RequestData["studioModeEnabled"]) { + // (Bad) Create a boolean on the stack, then free it after the task is completed. Requires `wait` in obs_queue_task() to be true + bool *studioModeEnabled = new bool(request.RequestData["studioModeEnabled"]); + // Queue the task inside of the UI thread to prevent race conditions + obs_queue_task(OBS_TASK_UI, [](void* param) { + bool studioModeEnabled = (bool*)param; + obs_frontend_set_preview_program_mode(&studioModeEnabled); + }, studioModeEnabled, true); + delete studioModeEnabled; + } + + return RequestResult::Success(); +} + +RequestResult RequestHandler::Sleep(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + if (!request.ValidateNumber("sleepMillis", statusCode, comment, 0, 50000)) { + return RequestResult::Error(statusCode, comment); + } + + int64_t sleepMillis = request.RequestData["sleepMillis"]; + std::this_thread::sleep_for(std::chrono::milliseconds(sleepMillis)); + + return RequestResult::Success(); +} diff --git a/src/requesthandler/rpc/RequestStatus.h b/src/requesthandler/rpc/RequestStatus.h index cb22239e..a9d68f33 100644 --- a/src/requesthandler/rpc/RequestStatus.h +++ b/src/requesthandler/rpc/RequestStatus.h @@ -97,6 +97,8 @@ namespace RequestStatus { ConfigParameterNotFound = 617, // The specified property (obs_properties_t) was not found PropertyNotFound = 618, + // The specififed key (OBS_KEY_*) was not found + KeyNotFound = 619, // Processing the request failed unexpectedly RequestProcessingFailed = 700, @@ -112,5 +114,7 @@ namespace RequestStatus { ScreenshotSaveFailed = 705, // Creating the directory failed DirectoryCreationFailed = 706, + // The combination of request parameters cannot be used to perform an action + CannotAct = 707, }; }; diff --git a/src/utils/Obs.cpp b/src/utils/Obs.cpp index 82645911..db9198d2 100644 --- a/src/utils/Obs.cpp +++ b/src/utils/Obs.cpp @@ -25,6 +25,19 @@ std::vector ConvertStringArray(char **array) return ret; } +std::string Utils::Obs::StringHelper::GetObsVersionString() +{ + uint32_t version = obs_get_version(); + + uint8_t major, minor, patch; + major = (version >> 24) & 0xFF; + minor = (version >> 16) & 0xFF; + patch = version & 0xFF; + + QString combined = QString("%1.%2.%3").arg(major).arg(minor).arg(patch); + return combined.toStdString(); +} + std::string Utils::Obs::StringHelper::GetSourceTypeString(obs_source_t *source) { obs_source_type sourceType = obs_source_get_type(source); @@ -83,6 +96,33 @@ std::vector Utils::Obs::ListHelper::GetProfileList() return ret; } +std::vector Utils::Obs::ListHelper::GetHotkeyList() +{ + std::vector ret; + + obs_enum_hotkeys([](void* data, obs_hotkey_id id, obs_hotkey_t* hotkey) { + auto ret = reinterpret_cast *>(data); + + ret->push_back(hotkey); + + return true; + }, &ret); + + return ret; +} + +std::vector Utils::Obs::ListHelper::GetHotkeyNameList() +{ + auto hotkeys = GetHotkeyList(); + + std::vector ret; + for (auto hotkey : hotkeys) { + ret.push_back(obs_hotkey_get_name(hotkey)); + } + + return ret; +} + std::vector Utils::Obs::ListHelper::GetSceneList() { obs_frontend_source_list sceneList = {}; @@ -121,3 +161,15 @@ std::vector Utils::Obs::ListHelper::GetTransitionList() return ret; } + +obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name) +{ + auto hotkeys = ListHelper::GetHotkeyList(); + + for (auto hotkey : hotkeys) { + if (obs_hotkey_get_name(hotkey) == name) + return hotkey; + } + + return nullptr; +} diff --git a/src/utils/Utils.h b/src/utils/Utils.h index 3bd7fcf5..10beb24d 100644 --- a/src/utils/Utils.h +++ b/src/utils/Utils.h @@ -29,20 +29,23 @@ namespace Utils { namespace Obs { namespace StringHelper { + std::string GetObsVersionString(); std::string GetSourceTypeString(obs_source_t *source); std::string GetInputMonitorTypeString(obs_source_t *input); std::string GetMediaInputStateString(obs_source_t *input); } - namespace DataHelper { - ; - } - namespace ListHelper { std::vector GetSceneCollectionList(); std::vector GetProfileList(); + std::vector GetHotkeyList(); + std::vector GetHotkeyNameList(); std::vector GetSceneList(); std::vector GetTransitionList(); } + + namespace SearchHelper { + obs_hotkey_t *GetHotkeyByName(std::string name); + } } }