diff --git a/CMakeLists.txt b/CMakeLists.txt index 36917a05..0245248c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-condition-process.hpp src/headers/macro-condition-recording.hpp src/headers/macro-condition-region.hpp + src/headers/macro-condition-replay-buffer.hpp src/headers/macro-condition-scene-order.hpp src/headers/macro-condition-scene.hpp src/headers/macro-condition-source.hpp @@ -184,6 +185,7 @@ set(advanced-scene-switcher_SOURCES src/macro-condition-process.cpp src/macro-condition-recording.cpp src/macro-condition-region.cpp + src/macro-condition-replay-buffer.cpp src/macro-condition-scene-order.cpp src/macro-condition-scene.cpp src/macro-condition-source.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 128f4da3..60e272ef 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -172,6 +172,11 @@ AdvSceneSwitcher.condition.hotkey="Hotkey" AdvSceneSwitcher.condition.hotkey.name="Macro trigger hotkey" AdvSceneSwitcher.condition.hotkey.tip="Note: You can configure the keybindings for this hotkey in the OBS settings window" AdvSceneSwitcher.condition.hotkey.entry="Name: {{name}}" +AdvSceneSwitcher.condition.replay="Replay buffer" +AdvSceneSwitcher.condition.replay.state.stopped="Replay buffer stopped" +AdvSceneSwitcher.condition.replay.state.started="Replay buffer started" +AdvSceneSwitcher.condition.replay.state.saved="Replay buffer saved" +AdvSceneSwitcher.condition.replay.entry="{{state}}" ; Macro Actions AdvSceneSwitcher.action.switchScene="Switch scene" diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index 87dd391e..dcf43a51 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -499,6 +499,11 @@ void handlePeviewSceneChange() } } +void setReplayBufferSaved() +{ + switcher->replayBufferSaved = true; +} + // Note to future self: // be careful using switcher->m here as there is potential for deadlocks when using // frontend functions such as obs_frontend_set_current_scene() @@ -530,6 +535,9 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher) case OBS_FRONTEND_EVENT_STREAMING_STOPPED: resetLiveTime(); break; + case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED: + setReplayBufferSaved(); + break; default: break; } diff --git a/src/headers/macro-condition-replay-buffer.hpp b/src/headers/macro-condition-replay-buffer.hpp new file mode 100644 index 00000000..88702aae --- /dev/null +++ b/src/headers/macro-condition-replay-buffer.hpp @@ -0,0 +1,56 @@ +#pragma once +#include "macro.hpp" +#include +#include + +enum class ReplayBufferState { + STOP, + START, + SAVE, +}; + +class MacroConditionReplayBuffer : public MacroCondition { +public: + bool CheckCondition(); + bool Save(obs_data_t *obj); + bool Load(obs_data_t *obj); + std::string GetId() { return id; }; + static std::shared_ptr Create() + { + return std::make_shared(); + } + + ReplayBufferState _state; + +private: + static bool _registered; + static const std::string id; +}; + +class MacroConditionReplayBufferEdit : public QWidget { + Q_OBJECT + +public: + MacroConditionReplayBufferEdit( + QWidget *parent, + std::shared_ptr cond = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionReplayBufferEdit( + parent, + std::dynamic_pointer_cast( + cond)); + } + +private slots: + void StateChanged(int value); + +protected: + QComboBox *_state; + std::shared_ptr _entryData; + +private: + bool _loading = true; +}; diff --git a/src/headers/switcher-data-structs.hpp b/src/headers/switcher-data-structs.hpp index 91ea854d..f5b2b0fb 100644 --- a/src/headers/switcher-data-structs.hpp +++ b/src/headers/switcher-data-structs.hpp @@ -93,6 +93,7 @@ struct SwitcherData { std::deque macros; bool macroSceneSwitched = false; + bool replayBufferSaved = false; std::deque windowSwitches; std::vector ignoreIdleWindows; diff --git a/src/macro-condition-replay-buffer.cpp b/src/macro-condition-replay-buffer.cpp new file mode 100644 index 00000000..e5d39806 --- /dev/null +++ b/src/macro-condition-replay-buffer.cpp @@ -0,0 +1,108 @@ +#include "headers/macro-condition-edit.hpp" +#include "headers/macro-condition-replay-buffer.hpp" +#include "headers/utility.hpp" +#include "headers/advanced-scene-switcher.hpp" + +const std::string MacroConditionReplayBuffer::id = "replay_buffer"; + +bool MacroConditionReplayBuffer::_registered = MacroConditionFactory::Register( + MacroConditionReplayBuffer::id, {MacroConditionReplayBuffer::Create, + MacroConditionReplayBufferEdit::Create, + "AdvSceneSwitcher.condition.replay"}); + +static std::map ReplayBufferStates = { + {ReplayBufferState::STOP, + "AdvSceneSwitcher.condition.replay.state.stopped"}, + {ReplayBufferState::START, + "AdvSceneSwitcher.condition.replay.state.started"}, + {ReplayBufferState::SAVE, + "AdvSceneSwitcher.condition.replay.state.saved"}, +}; + +bool MacroConditionReplayBuffer::CheckCondition() +{ + bool stateMatch = false; + switch (_state) { + case ReplayBufferState::STOP: + stateMatch = !obs_frontend_replay_buffer_active(); + break; + case ReplayBufferState::START: + stateMatch = obs_frontend_replay_buffer_active(); + break; + case ReplayBufferState::SAVE: + if (switcher->replayBufferSaved) { + stateMatch = true; + switcher->replayBufferSaved = false; + } + break; + default: + break; + } + return stateMatch; +} + +bool MacroConditionReplayBuffer::Save(obs_data_t *obj) +{ + MacroCondition::Save(obj); + obs_data_set_int(obj, "state", static_cast(_state)); + return true; +} + +bool MacroConditionReplayBuffer::Load(obs_data_t *obj) +{ + MacroCondition::Load(obj); + _state = static_cast(obs_data_get_int(obj, "state")); + return true; +} + +static inline void populateStateSelection(QComboBox *list) +{ + for (auto entry : ReplayBufferStates) { + list->addItem(obs_module_text(entry.second.c_str())); + } +} + +MacroConditionReplayBufferEdit::MacroConditionReplayBufferEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent) +{ + _state = new QComboBox(); + + QWidget::connect(_state, SIGNAL(currentIndexChanged(int)), this, + SLOT(StateChanged(int))); + + populateStateSelection(_state); + + QHBoxLayout *mainLayout = new QHBoxLayout; + + std::unordered_map widgetPlaceholders = { + {"{{state}}", _state}, + }; + + placeWidgets(obs_module_text("AdvSceneSwitcher.condition.replay.entry"), + mainLayout, widgetPlaceholders); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroConditionReplayBufferEdit::StateChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_state = static_cast(value); +} + +void MacroConditionReplayBufferEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _state->setCurrentIndex(static_cast(_entryData->_state)); +}