Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ CFLAGS := -Wall -Wextra -O2 -ffunction-sections\

CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__

CXXFLAGS := $(CFLAGS) -std=c++20
CXXFLAGS := $(CFLAGS) -std=c++23

ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUMSSPECS)
Expand Down
3 changes: 2 additions & 1 deletion source/ButtonComboInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class ButtonComboInfoIF {
void *context,
bool observer);
virtual ~ButtonComboInfoIF();
virtual void UpdateInput(ButtonComboModule_ControllerTypes controller, std::span<uint32_t> pressedButtons) = 0;
// Note: return the index of the sample that activated the combo, or -1.
virtual int UpdateInput(ButtonComboModule_ControllerTypes controller, std::span<uint32_t> pressedButtons) = 0;

[[nodiscard]] bool isObserver() const;

Expand Down
12 changes: 8 additions & 4 deletions source/ButtonComboInfoDown.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "ButtonComboInfoDown.h"

#include "logger.h"
#include <ranges>

ButtonComboInfoDown::ButtonComboInfoDown(
std::string label,
Expand All @@ -16,23 +17,24 @@ ButtonComboInfoDown::~ButtonComboInfoDown() {
DEBUG_FUNCTION_LINE_INFO("Deleted ButtonComboInfoDown: \"%s\", combo: %08X, controllerMask: %08X. Observer %d", mLabel.c_str(), mCombo, mControllerMask, mIsObserver);
}

void ButtonComboInfoDown::UpdateInput(
int ButtonComboInfoDown::UpdateInput(
const ButtonComboModule_ControllerTypes controller,
const std::span<uint32_t> pressedButtons) {
if ((mControllerMask & controller) == 0) {
return;
return -1;
}
const auto chanIndex = ControllerTypeToChanIndex(controller);
if (chanIndex < 0 || static_cast<uint32_t>(chanIndex) >= std::size(mHoldInformation)) {
DEBUG_FUNCTION_LINE_WARN("ChanIndex is out of bounds %d", chanIndex);
return;
return -1;
}

auto &[prevButtonCombo] = mHoldInformation[chanIndex];

DEBUG_FUNCTION_LINE_VERBOSE("[PRESS DOWN] Check button combo %08X on controller %08X (lastItem im pressedButtons (size %d) is %08X) for %s [%08X]", mCombo, controller, pressedButtons.size(), pressedButtons.back(), mLabel.c_str(), getHandle().handle);

for (const auto &pressedButton : pressedButtons) {
int activatedIndex = -1;
for (auto [index, pressedButton] : std::views::enumerate(pressedButtons)) {
const bool prevButtonsIncludedCombo = (prevButtonCombo & mCombo) == mCombo; // Make sure the combo can't be triggered on releasing
const bool buttonsPressedChanged = prevButtonCombo != pressedButton; // Avoid "holding" the combo
const bool buttonsPressedMatchCombo = pressedButton == mCombo; // detect the actual combo
Expand All @@ -41,12 +43,14 @@ void ButtonComboInfoDown::UpdateInput(
if (mCallback != nullptr) {
DEBUG_FUNCTION_LINE("Calling callback [%08X](controller: %08X, context: %08X) for \"%s\" [handle: %08X], pressed down %08X", mCallback, controller, mContext, mLabel.c_str(), getHandle().handle, mCombo);
mCallback(controller, getHandle(), mContext);
activatedIndex = index;
} else {
DEBUG_FUNCTION_LINE_WARN("Callback was null for combo %08X", getHandle());
}
}
prevButtonCombo = pressedButton;
}
return activatedIndex;
}

ButtonComboModule_Error ButtonComboInfoDown::setHoldDuration(uint32_t) {
Expand Down
4 changes: 2 additions & 2 deletions source/ButtonComboInfoDown.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ButtonComboInfoDown final : public ButtonComboInfoIF {
uint32_t prevButtonCombo;
} HoldInformation;

void UpdateInput(ButtonComboModule_ControllerTypes controller, std::span<uint32_t> pressedButtons) override;
int UpdateInput(ButtonComboModule_ControllerTypes controller, std::span<uint32_t> pressedButtons) override;

ButtonComboModule_Error setHoldDuration(uint32_t uint32) override;

Expand All @@ -33,4 +33,4 @@ class ButtonComboInfoDown final : public ButtonComboInfoIF {

private:
HoldInformation mHoldInformation[9] = {}; // one for each controller
};
};
11 changes: 7 additions & 4 deletions source/ButtonComboInfoHold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@ ButtonComboInfoHold::~ButtonComboInfoHold() {
DEBUG_FUNCTION_LINE_INFO("Deleted ButtonComboInfoHold: \"%s\", combo: %08X, targetDurationInMs: %d ms, controllerMask: %08X", mLabel.c_str(), mCombo, mTargetDurationInMs, mControllerMask);
}

void ButtonComboInfoHold::UpdateInput(const ButtonComboModule_ControllerTypes controller, const std::span<uint32_t> pressedButtons) {
int ButtonComboInfoHold::UpdateInput(const ButtonComboModule_ControllerTypes controller, const std::span<uint32_t> pressedButtons) {
if ((mControllerMask & controller) == 0) {
return;
return -1;
}
const auto chanIndex = ControllerTypeToChanIndex(controller);
if (chanIndex < 0 || static_cast<uint32_t>(chanIndex) >= std::size(mHoldInformation)) {
DEBUG_FUNCTION_LINE_WARN("ChanIndex is out of bounds %d", chanIndex);
return;
return -1;
}

int activatedIndex = -1;

auto &holdInformation = mHoldInformation[chanIndex];
const auto latestButtonPress = pressedButtons.back();

Expand All @@ -53,7 +55,7 @@ void ButtonComboInfoHold::UpdateInput(const ButtonComboModule_ControllerTypes co
if (mCallback != nullptr) {
DEBUG_FUNCTION_LINE("Calling callback [%08X](controller: %08X context: %08X) for \"%s\" [handle: %08X], hold %08X for %d ms", mCallback, controller, mContext, mLabel.c_str(), getHandle().handle, mCombo, intervalInMs);
mCallback(controller, getHandle(), mContext);

activatedIndex = pressedButtons.size() - 1;
} else {
DEBUG_FUNCTION_LINE_WARN("Callback was null for combo %08X", getHandle());
}
Expand All @@ -63,6 +65,7 @@ void ButtonComboInfoHold::UpdateInput(const ButtonComboModule_ControllerTypes co
holdInformation.callbackTriggered = false;
holdInformation.holdStartedAt = 0;
}
return activatedIndex;
}

ButtonComboModule_Error ButtonComboInfoHold::setHoldDuration(const uint32_t holdDurationInMs) {
Expand Down
2 changes: 1 addition & 1 deletion source/ButtonComboInfoHold.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ButtonComboInfoHold final : public ButtonComboInfoIF {
~ButtonComboInfoHold() override;

private:
void UpdateInput(ButtonComboModule_ControllerTypes controller, std::span<uint32_t> pressedButtons) override;
int UpdateInput(ButtonComboModule_ControllerTypes controller, std::span<uint32_t> pressedButtons) override;

ButtonComboModule_Error setHoldDuration(uint32_t holdDurationInMs) override;

Expand Down
115 changes: 93 additions & 22 deletions source/ButtonComboManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ ButtonComboModule_Error ButtonComboManager::GetButtonComboStatus(const ButtonCom
return BUTTON_COMBO_MODULE_ERROR_SUCCESS;
}

void ButtonComboManager::UpdateInputVPAD(const VPADChan chan, const VPADStatus *buffer, const uint32_t bufferSize, const VPADReadError *error) {
void ButtonComboManager::UpdateInputVPAD(const VPADChan chan, VPADStatus *buffer, const uint32_t bufferSize, const VPADReadError *error) {
if (chan < VPAD_CHAN_0 || chan > VPAD_CHAN_1) {
DEBUG_FUNCTION_LINE_ERR("Invalid VPADChan");
return;
Expand Down Expand Up @@ -435,9 +435,23 @@ void ButtonComboManager::UpdateInputVPAD(const VPADChan chan, const VPADStatus *
if (combo->getStatus() != BUTTON_COMBO_MODULE_COMBO_STATUS_VALID) {
continue;
}
combo->UpdateInput(controller, std::span(mVPADButtonBuffer.data(), usedBufferSize));
int activated = combo->UpdateInput(controller, std::span(mVPADButtonBuffer.data(), usedBufferSize));
if (activated >= 0 && !combo->isObserver()) {
// suppress all buttons triggered
uint32_t triggered = buffer[usedBufferSize - activated - 1].trigger;
mVPADSuppressed[chan] |= triggered;
}
}
}
// hide all suppressed buttons from the game, iterate from oldest sample to newest
for (uint32_t i = bufferSize - 1; i + 1 > 0; --i) {
// released buttons stop being suppressed
mVPADSuppressed[chan] &= ~buffer[i].release;
// hide the suppressed buttons
buffer[i].trigger &= ~mVPADSuppressed[chan];
buffer[i].hold &= ~mVPADSuppressed[chan];
buffer[i].release &= ~mVPADSuppressed[chan];
}
}

void ButtonComboManager::UpdateInputWPAD(const WPADChan chan, WPADStatus *data) {
Expand All @@ -450,42 +464,99 @@ void ButtonComboManager::UpdateInputWPAD(const WPADChan chan, WPADStatus *data)
return;
}

// Do not check for combos while the combo detection is active
if (mInButtonComboDetection) {
return;
}
auto &coreBtns = mWPADCoreBtns[chan];
auto &extBtns = mWPADExtBtns[chan];

const auto controller = convert(chan);
uint32_t pressedButtons = {};
if (mWPADExtension[chan] != data->extensionType) {
mWPADExtension[chan] = data->extensionType;
extBtns.reset();
}
switch (data->extensionType) {
case WPAD_EXT_CORE:
case WPAD_EXT_NUNCHUK:
case WPAD_EXT_MPLUS:
case WPAD_EXT_MPLUS_NUNCHUK: {
pressedButtons = remapWiiMoteButtons(data->buttons);
case WPAD_EXT_MPLUS_NUNCHUK:
coreBtns.update(data->buttons);
extBtns.reset();
break;
}
case WPAD_EXT_CLASSIC:
case WPAD_EXT_MPLUS_CLASSIC: {
const auto classic = reinterpret_cast<WPADStatusClassic *>(data);
pressedButtons = remapClassicButtons(classic->buttons);
auto cdata = reinterpret_cast<WPADStatusClassic *>(data);
coreBtns.update(cdata->core.buttons);
extBtns.update(cdata->buttons);
break;
}
case WPAD_EXT_PRO_CONTROLLER: {
const auto proController = reinterpret_cast<WPADStatusProController *>(data);
pressedButtons = remapProButtons(proController->buttons);
auto pdata = reinterpret_cast<WPADStatusProController *>(data);
coreBtns.reset();
extBtns.update(pdata->buttons);
break;
}
default:
default: // early out when we don't know how to handle extension
return;
}
{
std::lock_guard lock(mMutex);
for (const auto &combo : mCombos) {
if (combo->getStatus() != BUTTON_COMBO_MODULE_COMBO_STATUS_VALID) {
continue;

// Do not check for combos while the combo detection is active
if (!mInButtonComboDetection) {
const auto controller = convert(chan);
uint32_t pressedButtons = {};
switch (data->extensionType) {
case WPAD_EXT_CORE:
case WPAD_EXT_NUNCHUK:
case WPAD_EXT_MPLUS:
case WPAD_EXT_MPLUS_NUNCHUK: {
pressedButtons = remapWiiMoteButtons(data->buttons);
break;
}
case WPAD_EXT_CLASSIC:
case WPAD_EXT_MPLUS_CLASSIC: {
const auto classic = reinterpret_cast<WPADStatusClassic *>(data);
pressedButtons = remapClassicButtons(classic->buttons);
break;
}
combo->UpdateInput(controller, std::span(&pressedButtons, 1));
case WPAD_EXT_PRO_CONTROLLER: {
const auto proController = reinterpret_cast<WPADStatusProController *>(data);
pressedButtons = remapProButtons(proController->buttons);
break;
}
}
{
std::lock_guard lock(mMutex);
for (const auto &combo : mCombos) {
if (combo->getStatus() != BUTTON_COMBO_MODULE_COMBO_STATUS_VALID) {
continue;
}
int activated = combo->UpdateInput(controller, std::span(&pressedButtons, 1));
if (activated >= 0 && !combo->isObserver()) {
coreBtns.blockTriggered();
extBtns.blockTriggered();
}
}
}
}

coreBtns.unblockReleased();
extBtns.unblockReleased();

// modify data, to hide all suppressed buttons from the game
switch (data->extensionType) {
case WPAD_EXT_CORE:
case WPAD_EXT_NUNCHUK:
case WPAD_EXT_MPLUS:
case WPAD_EXT_MPLUS_NUNCHUK:
coreBtns.suppressButtons(data->buttons);
break;
case WPAD_EXT_CLASSIC:
case WPAD_EXT_MPLUS_CLASSIC: {
auto cdata = reinterpret_cast<WPADStatusClassic *>(data);
coreBtns.suppressButtons(cdata->core.buttons);
extBtns.suppressButtons(cdata->buttons);
break;
}
case WPAD_EXT_PRO_CONTROLLER: {
auto pdata = reinterpret_cast<WPADStatusProController *>(data);
extBtns.suppressButtons(pdata->buttons);
break;
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions source/ButtonComboManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <padscore/wpad.h>
#include <vpad/input.h>

#include <array>
#include <memory>
#include <mutex>
#include <optional>
Expand All @@ -12,6 +13,7 @@
#include <cstdint>
#include <forward_list>

#include "ButtonTracker.h"

class ButtonComboManager {
public:
Expand All @@ -21,7 +23,7 @@ class ButtonComboManager {

[[nodiscard]] ButtonComboInfoIF *GetComboInfoForHandle(ButtonComboModule_ComboHandle handle) const;

void UpdateInputVPAD(VPADChan chan, const VPADStatus *buffer, uint32_t bufferSize, const VPADReadError *error);
void UpdateInputVPAD(VPADChan chan, VPADStatus *buffer, uint32_t bufferSize, const VPADReadError *error);

void UpdateInputWPAD(WPADChan chan, WPADStatus *data);

Expand Down Expand Up @@ -61,4 +63,9 @@ class ButtonComboManager {
std::mutex mMutex;
std::mutex mDetectButtonsMutex;
bool mInButtonComboDetection = false;
};

std::array<uint32_t, 2> mVPADSuppressed{};
std::array<ButtonTracker<uint16_t>, 7> mWPADCoreBtns;
std::array<ButtonTracker<uint32_t>, 7> mWPADExtBtns;
std::array<uint8_t, 7> mWPADExtension{};
};
39 changes: 39 additions & 0 deletions source/ButtonTracker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

// Simple class to track buttons triggered and released; also perform suppression logic.

template<typename T>
struct ButtonTracker {
void reset() noexcept {
hold = 0;
trigger = 0;
release = 0;
suppress = 0;
}

void update(T buttons) noexcept {
T changed = buttons ^ hold;
hold = buttons;
trigger = changed & buttons;
release = changed & ~buttons;
}

void blockTriggered() noexcept {
suppress |= trigger;
}

void unblockReleased() noexcept {
suppress &= ~release;
}

template<typename U>
void suppressButtons(U &buttons) {
buttons &= ~suppress;
}

private:
T hold = 0;
T trigger = 0;
T release = 0;
T suppress = 0;
};