diff --git a/source/ButtonComboManager.cpp b/source/ButtonComboManager.cpp index f9368da..27eb0c8 100644 --- a/source/ButtonComboManager.cpp +++ b/source/ButtonComboManager.cpp @@ -1,6 +1,7 @@ #include "ButtonComboManager.h" #include "ButtonComboInfoDown.h" #include "ButtonComboInfoHold.h" +#include "TVOverlayManager.h" #include "logger.h" #include @@ -335,7 +336,7 @@ std::optional> ButtonComboManager::CreateComb bool ButtonComboManager::hasActiveComboWithTVButton() { std::lock_guard lock(mMutex); - return std::ranges::any_of(mCombos, [](const auto &combo) { return combo->getStatus() == BUTTON_COMBO_MODULE_COMBO_STATUS_VALID && combo->getCombo() & BCMPAD_BUTTON_TV; }); + return std::ranges::any_of(mCombos, [](const auto &combo) { return combo->getStatus() == BUTTON_COMBO_MODULE_COMBO_STATUS_VALID && combo->getCombo() & BCMPAD_BUTTON_TV && !combo->isObserver(); }); } ButtonComboModule_ComboStatus ButtonComboManager::CheckComboStatus(const ButtonComboInfoIF &other) { @@ -363,7 +364,7 @@ void ButtonComboManager::AddCombo(std::shared_ptr newComboInf outHandle = newComboInfo->getHandle(); mCombos.emplace_front(std::move(newComboInfo)); - UpdateTVMenuBlocking(); + TVOverlayManager::UpdateBlocking(); } ButtonComboModule_Error ButtonComboManager::RemoveCombo(ButtonComboModule_ComboHandle handle) { @@ -377,7 +378,7 @@ ButtonComboModule_Error ButtonComboManager::RemoveCombo(ButtonComboModule_ComboH if (!remove_first_if(mCombos, [handle](const auto &combo) { return combo->getHandle() == handle; })) { DEBUG_FUNCTION_LINE_WARN("Failed to remove combo by handle %p", handle.handle); } else { - UpdateTVMenuBlocking(); + TVOverlayManager::UpdateBlocking(); } return BUTTON_COMBO_MODULE_ERROR_SUCCESS; @@ -432,12 +433,6 @@ void ButtonComboManager::UpdateInputVPAD(const VPADChan chan, const VPADStatus * } } -void ButtonComboManager::UpdateTVMenuBlocking() { - const auto block = hasActiveComboWithTVButton(); - VPADSetTVMenuInvalid(VPAD_CHAN_0, block); - VPADSetTVMenuInvalid(VPAD_CHAN_1, block); -} - void ButtonComboManager::UpdateInputsLocked(const ButtonComboModule_ControllerTypes controller, const std::span pressedButtons) { std::lock_guard lock(mMutex); mIsIterating++; @@ -457,7 +452,7 @@ void ButtonComboManager::UpdateInputsLocked(const ButtonComboModule_ControllerTy mCombosToRemove.clear(); // Update TV Menu blocking status once after all removals - UpdateTVMenuBlocking(); + TVOverlayManager::UpdateBlocking(); } } @@ -569,7 +564,7 @@ ButtonComboModule_Error ButtonComboManager::UpdateButtonCombo(const ButtonComboM comboInfo->setStatus(CheckComboStatus(*comboInfo)); outComboStatus = comboInfo->getStatus(); - UpdateTVMenuBlocking(); + TVOverlayManager::UpdateBlocking(); return BUTTON_COMBO_MODULE_ERROR_SUCCESS; } diff --git a/source/ButtonComboManager.h b/source/ButtonComboManager.h index 65b3454..b22f249 100644 --- a/source/ButtonComboManager.h +++ b/source/ButtonComboManager.h @@ -20,7 +20,6 @@ class ButtonComboManager { static std::optional> CreateComboInfo(const ButtonComboModule_ComboOptions &options, ButtonComboModule_Error &err); void UpdateInputVPAD(VPADChan chan, const VPADStatus *buffer, uint32_t bufferSize, const VPADReadError *error); - void UpdateTVMenuBlocking(); void UpdateInputWPAD(WPADChan chan, WPADStatus *data); @@ -66,4 +65,4 @@ class ButtonComboManager { mutable std::recursive_mutex mMutex; std::recursive_mutex mDetectButtonsMutex; bool mInButtonComboDetection = false; -}; \ No newline at end of file +}; diff --git a/source/TVOverlayManager.cpp b/source/TVOverlayManager.cpp new file mode 100644 index 0000000..d936975 --- /dev/null +++ b/source/TVOverlayManager.cpp @@ -0,0 +1,115 @@ +#include "TVOverlayManager.h" +#include "ButtonComboManager.h" +#include "export.h" +#include "globals.h" +#include "logger.h" + +#include +#include +#include + +namespace TVOverlayManager { + + namespace { + + ButtonComboModule_ComboHandle sTVButtonHandle; + // Track when the TV button was last pressed to implement timeout, or zero if + // timeout expired. + OSTime sTVPressed[2]; + bool sTVMenuBlocked[2]; + + void TVComboCallback(ButtonComboModule_ControllerTypes triggeredBy, + ButtonComboModule_ComboHandle, + void *) { + VPADChan chan; + switch (triggeredBy) { + case BUTTON_COMBO_MODULE_CONTROLLER_VPAD_0: + chan = VPAD_CHAN_0; + break; + case BUTTON_COMBO_MODULE_CONTROLLER_VPAD_1: + chan = VPAD_CHAN_1; + break; + default: + return; + } + DEBUG_FUNCTION_LINE_INFO("TV pressed"); + sTVPressed[chan] = OSGetSystemTime(); + OSMemoryBarrier(); + } + + void SetBlocked(VPADChan channel, bool blocked) { + VPADSetTVMenuInvalid(channel, blocked); + sTVMenuBlocked[channel] = blocked; + } + + } // namespace + + void RegisterCombo() { + ButtonComboModule_ComboOptions opt = {}; + opt.version = BUTTON_COMBO_MODULE_COMBO_OPTIONS_VERSION; + opt.metaOptions.label = "TV remote overlay combo"; + opt.callbackOptions.callback = TVComboCallback; + opt.callbackOptions.context = {}; + opt.buttonComboOptions.type = BUTTON_COMBO_MODULE_COMBO_TYPE_PRESS_DOWN_OBSERVER; + opt.buttonComboOptions.basicCombo.combo = BCMPAD_BUTTON_TV; + opt.buttonComboOptions.basicCombo.controllerMask = BUTTON_COMBO_MODULE_CONTROLLER_VPAD; + opt.buttonComboOptions.optionalHoldForXMs = 0; + if (ButtonComboModule_AddButtonCombo(&opt, &sTVButtonHandle, nullptr) != BUTTON_COMBO_MODULE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("FAILED TO SET UP TV COMBO!"); + } + } + + void UnregisterCombo() { + ButtonComboModule_RemoveButtonCombo(sTVButtonHandle); + } + + void InitVPAD(VPADChan channel) { + bool blocked = false; + if (auto mgr = gButtonComboManager) { + blocked = mgr->hasActiveComboWithTVButton(); + } + SetBlocked(channel, blocked); + ResetVPAD(channel); + } + + void ResetVPAD(VPADChan channel) { + sTVPressed[channel] = 0; + OSMemoryBarrier(); + } + + void UpdateVPAD(VPADChan channel) { + if (sTVPressed[channel]) { + OSTime elapsed = OSGetSystemTime() - sTVPressed[channel]; + // TODO: this should be configurable by the public API. + const OSTime double_press_time = OSMillisecondsToTicks(100); + if (elapsed > double_press_time && sTVMenuBlocked[channel]) { + DEBUG_FUNCTION_LINE_INFO("Unblocking TV menu"); + SetBlocked(channel, false); + } + // TODO: this should be configurable by the public API. + const OSTime tv_enable_timeout = OSMillisecondsToTicks(1000); + if (elapsed > tv_enable_timeout && !VPADGetTVMenuStatus(channel)) { + bool blocked = false; + if (auto mgr = gButtonComboManager) { + blocked = mgr->hasActiveComboWithTVButton(); + } + DEBUG_FUNCTION_LINE_INFO("TV timeout reached, setting TV Menu block to %s", + blocked ? "blocked" : "unblocked"); + SetBlocked(channel, blocked); + ResetVPAD(channel); + } + } + } + + void UpdateBlocking() { + bool blocked = false; + if (auto mgr = gButtonComboManager) { + blocked = mgr->hasActiveComboWithTVButton(); + } + for (VPADChan channel : {VPAD_CHAN_0, VPAD_CHAN_1}) { + SetBlocked(channel, blocked); + ResetVPAD(channel); + } + } + +} // namespace TVOverlayManager diff --git a/source/TVOverlayManager.h b/source/TVOverlayManager.h new file mode 100644 index 0000000..21cf153 --- /dev/null +++ b/source/TVOverlayManager.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace TVOverlayManager +{ + + void RegisterCombo(); + + void UnregisterCombo(); + + void InitVPAD(VPADChan channel); + + void ResetVPAD(VPADChan channel); + + void UpdateVPAD(VPADChan channel); + + void UpdateBlocking(); + +} // namespace TVOverlayManager diff --git a/source/export.cpp b/source/export.cpp index 9529859..abec75f 100644 --- a/source/export.cpp +++ b/source/export.cpp @@ -1,3 +1,4 @@ +#include "export.h" #include "ButtonComboManager.h" #include "globals.h" diff --git a/source/export.h b/source/export.h new file mode 100644 index 0000000..23117c4 --- /dev/null +++ b/source/export.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +extern ButtonComboModule_Error ButtonComboModule_AddButtonCombo(const ButtonComboModule_ComboOptions *options, + ButtonComboModule_ComboHandle *outHandle, + ButtonComboModule_ComboStatus *outStatus); + +extern ButtonComboModule_Error ButtonComboModule_RemoveButtonCombo(const ButtonComboModule_ComboHandle handle); diff --git a/source/function_patches.cpp b/source/function_patches.cpp index 89ed590..cdaecf2 100644 --- a/source/function_patches.cpp +++ b/source/function_patches.cpp @@ -1,5 +1,6 @@ #include "ButtonComboInfo.h" #include "ButtonComboManager.h" +#include "TVOverlayManager.h" #include "globals.h" #include @@ -13,23 +14,27 @@ DECL_FUNCTION(int32_t, VPADRead, VPADChan chan, VPADStatus *buffer, uint32_t buf const int32_t result = real_VPADRead(chan, buffer, buffer_size, &real_error); if (result > 0 && real_error == VPAD_READ_SUCCESS) { - if (const auto comboManager = gButtonComboManager; comboManager) { + if (const auto comboManager = gButtonComboManager) { comboManager->UpdateInputVPAD(chan, buffer, result > static_cast(buffer_size) ? buffer_size : result, error); } } if (error) { *error = real_error; } + + TVOverlayManager::UpdateVPAD(chan); + return result; } DECL_FUNCTION(void, WPADRead, WPADChan chan, WPADStatus *data) { real_WPADRead(chan, data); - if (const auto comboManager = gButtonComboManager; comboManager) { + if (const auto comboManager = gButtonComboManager) { comboManager->UpdateInputWPAD(chan, data); } } + struct WUT_PACKED CCRCDCCallbackData { uint32_t attached; VPADChan chan; @@ -39,10 +44,11 @@ struct WUT_PACKED CCRCDCCallbackData { DECL_FUNCTION(void, __VPADBASEAttachCallback, CCRCDCCallbackData *data, void *context) { real___VPADBASEAttachCallback(data, context); - if (data && data->attached) { - if (const auto comboManager = gButtonComboManager; comboManager) { - const bool block = comboManager->hasActiveComboWithTVButton(); - VPADSetTVMenuInvalid(data->chan, block); + if (data) { + if (data->attached) { + TVOverlayManager::InitVPAD(data->chan); + } else { + TVOverlayManager::ResetVPAD(data->chan); } } } diff --git a/source/main.cpp b/source/main.cpp index dca8138..7658709 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,4 +1,5 @@ #include "ButtonComboManager.h" +#include "TVOverlayManager.h" #include "function_patches.h" #include "globals.h" #include "logger.h" @@ -34,10 +35,13 @@ WUMS_INITIALIZE() { gButtonComboManager = std::make_unique(); + TVOverlayManager::RegisterCombo(); + deinitLogging(); } WUMS_DEINITIALIZE() { + TVOverlayManager::UnregisterCombo(); gButtonComboManager.reset(); }