Add remaining controller config options, remove ImGui config

This commit is contained in:
Irastris
2026-05-07 12:00:35 -04:00
parent b11f3add06
commit 73eb401c93
5 changed files with 181 additions and 563 deletions
+3 -2
View File
@@ -261,7 +261,9 @@ namespace dusk {
}
if (ImGui::IsKeyPressed(ImGuiKey_F11)) {
ImGuiMenuGame::ToggleFullscreen();
getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen);
VISetWindowFullscreen(getSettings().video.enableFullscreen);
config::Save();
}
if (ImGui::GetIO().KeyShift && ImGui::IsKeyPressed(ImGuiKey_F1)) {
@@ -360,7 +362,6 @@ namespace dusk {
ImGui::End();
}
m_menuGame.windowControllerConfig();
m_menuGame.windowInputViewer();
m_menuGame.drawSpeedrunTimerOverlay();
-552
View File
@@ -1,574 +1,22 @@
#include "fmt/format.h"
#include "imgui.h"
#include "ImGuiEngine.hpp"
#include "ImGuiConsole.hpp"
#include "ImGuiConfig.hpp"
#include "dusk/main.h"
#include "dusk/hotkeys.h"
#include "m_Do/m_Do_main.h"
#include <SDL3/SDL_gamepad.h>
namespace dusk {
void ImGuiMenuGame::ToggleFullscreen() {
getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen);
VISetWindowFullscreen(getSettings().video.enableFullscreen);
config::Save();
}
ImGuiMenuGame::ImGuiMenuGame() {}
void ImGuiMenuGame::draw() {
if (ImGui::BeginMenu("Settings")) {
// TODO: Remove this once Controller Config exists in RmlUi
if (ImGui::Button("Configure Controller")){
m_showControllerConfig = !m_showControllerConfig;
}
ImGui::Checkbox("Show Input Viewer", &m_showInputViewer);
ImGui::EndMenu();
}
}
static void drawVirtualStick(const char* id, const ImVec2& stick) {
float scale = ImGuiScale();
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 45 * scale, ImGui::GetCursorPos().y + 10));
ImGui::BeginChild(id, ImVec2(80 * scale, 80 * scale), 0, ImGuiWindowFlags_NoBackground);
ImDrawList* dl = ImGui::GetWindowDrawList();
ImVec2 p = ImGui::GetCursorScreenPos();
float radius = 30.0f * scale;
ImVec2 pos = ImVec2(p.x + radius, p.y + radius);
constexpr ImU32 stickGray = IM_COL32(150, 150, 150, 255);
constexpr ImU32 white = IM_COL32(255, 255, 255, 255);
constexpr ImU32 red = IM_COL32(230, 0, 0, 255);
dl->AddCircleFilled(pos, radius, stickGray, 8);
dl->AddCircleFilled(ImVec2(pos.x + stick.x * (radius), pos.y + -stick.y * (radius)), 3 * scale, red);
ImGui::EndChild();
}
struct SpecificButtonName {
SDL_GamepadType Type;
const char* Name;
};
struct ButtonNames {
SDL_GamepadButton Button;
std::vector<SpecificButtonName> Names;
};
// clang-format off
static const std::vector<ButtonNames> GamepadButtonNames = {
{ SDL_GAMEPAD_BUTTON_LEFT_STICK, {
{SDL_GAMEPAD_TYPE_PS3, "L3"},
{SDL_GAMEPAD_TYPE_PS4, "L3"},
{SDL_GAMEPAD_TYPE_PS5, "L3"},
{SDL_GAMEPAD_TYPE_XBOX360, "Left Stick"},
{SDL_GAMEPAD_TYPE_XBOXONE, "Left Stick"},
{SDL_GAMEPAD_TYPE_GAMECUBE, "Control Stick"},
}},
{ SDL_GAMEPAD_BUTTON_RIGHT_STICK, {
{SDL_GAMEPAD_TYPE_PS3, "R3"},
{SDL_GAMEPAD_TYPE_PS4, "R3"},
{SDL_GAMEPAD_TYPE_PS5, "R3"},
{SDL_GAMEPAD_TYPE_XBOX360, "Right Stick"},
{SDL_GAMEPAD_TYPE_XBOXONE, "Right Stick"},
{SDL_GAMEPAD_TYPE_GAMECUBE, "C Stick"},
}},
{ SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, {
{SDL_GAMEPAD_TYPE_PS3, "L1"},
{SDL_GAMEPAD_TYPE_PS4, "L1"},
{SDL_GAMEPAD_TYPE_PS5, "L1"},
{SDL_GAMEPAD_TYPE_XBOX360, "LB"},
{SDL_GAMEPAD_TYPE_XBOXONE, "LB"},
}},
{ SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, {
{SDL_GAMEPAD_TYPE_PS3, "R1"},
{SDL_GAMEPAD_TYPE_PS4, "R1"},
{SDL_GAMEPAD_TYPE_PS5, "R1"},
{SDL_GAMEPAD_TYPE_XBOX360, "RB"},
{SDL_GAMEPAD_TYPE_XBOXONE, "RB"},
{SDL_GAMEPAD_TYPE_GAMECUBE, "Z"},
}},
{ SDL_GAMEPAD_BUTTON_BACK, {
{SDL_GAMEPAD_TYPE_PS3, "Select"},
{SDL_GAMEPAD_TYPE_PS4, "Share"},
{SDL_GAMEPAD_TYPE_PS5, "Create"},
{SDL_GAMEPAD_TYPE_XBOX360, "Back"},
{SDL_GAMEPAD_TYPE_XBOXONE, "View"},
}},
{ SDL_GAMEPAD_BUTTON_START, {
{SDL_GAMEPAD_TYPE_PS3, "Start"},
{SDL_GAMEPAD_TYPE_PS4, "Options"},
{SDL_GAMEPAD_TYPE_PS5, "Options"},
{SDL_GAMEPAD_TYPE_XBOX360, "Start"},
{SDL_GAMEPAD_TYPE_XBOXONE, "Menu"},
{SDL_GAMEPAD_TYPE_GAMECUBE, "Start/Pause"},
}},
};
// clang-format on
static const char* GetNameForGamepadButton(SDL_Gamepad* gamepad, u32 buttonUntyped) {
if (buttonUntyped == PAD_NATIVE_BUTTON_INVALID) {
return "Not bound";
}
auto button = static_cast<SDL_GamepadButton>(buttonUntyped);
auto label = SDL_GetGamepadButtonLabel(gamepad, button);
switch (label) {
case SDL_GAMEPAD_BUTTON_LABEL_A:
return "A";
case SDL_GAMEPAD_BUTTON_LABEL_B:
return "B";
case SDL_GAMEPAD_BUTTON_LABEL_X:
return "X";
case SDL_GAMEPAD_BUTTON_LABEL_Y:
return "Y";
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
return "Cross";
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
return "Circle";
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
return "Triangle";
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
return "Square";
default:; // Fall through
}
auto padType = SDL_GetGamepadType(gamepad);
for (const auto& buttonNames : GamepadButtonNames) {
if (buttonNames.Button != button) {
continue;
}
for (const auto& name : buttonNames.Names) {
if (name.Type == padType) {
return name.Name;
}
}
}
switch (button) {
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
return "D-pad left";
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
return "D-pad right";
case SDL_GAMEPAD_BUTTON_DPAD_UP:
return "D-pad up";
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
return "D-pad down";
default:
return PADGetNativeButtonName(buttonUntyped);
}
}
void ImGuiMenuGame::windowControllerConfig() {
if (!m_showControllerConfig) {
return;
}
// if pending for a button mapping, check to set new input
if (m_controllerConfig.m_pendingButtonMapping != nullptr) {
s32 nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort);
if (nativeButton != -1) {
m_controllerConfig.m_pendingButtonMapping->nativeButton = nativeButton;
m_controllerConfig.m_pendingButtonMapping = nullptr;
m_controllerConfig.m_pendingPort = -1;
PADBlockInput(false);
PADSerializeMappings();
}
}
// if pending for an axis mapping, check to set new input
if (m_controllerConfig.m_pendingAxisMapping != nullptr) {
auto nativeAxis = PADGetNativeAxisPulled(m_controllerConfig.m_pendingPort);
if (nativeAxis.nativeAxis != -1) {
m_controllerConfig.m_pendingAxisMapping->nativeAxis = nativeAxis;
m_controllerConfig.m_pendingAxisMapping->nativeButton = -1;
m_controllerConfig.m_pendingAxisMapping = nullptr;
m_controllerConfig.m_pendingPort = -1;
PADBlockInput(false);
PADSerializeMappings();
} else {
auto nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort);
if (nativeButton != -1) {
m_controllerConfig.m_pendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE};
m_controllerConfig.m_pendingAxisMapping->nativeButton = nativeButton;
m_controllerConfig.m_pendingAxisMapping = nullptr;
m_controllerConfig.m_pendingPort = -1;
PADBlockInput(false);
PADSerializeMappings();
}
}
}
float scale = ImGuiScale();
ImGuiWindowFlags windowFlags =
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_AlwaysAutoResize;
// ImGui::SetNextWindowBgAlpha(0.65f);
if (!ImGui::Begin("Controller Config", &m_showControllerConfig, windowFlags)) {
ImGui::End();
return;
}
// port tabs
ImGui::BeginTabBar("##ControllerTabs");
for (int i = PAD_1; i <= PAD_4; i++) {
if (ImGui::BeginTabItem(fmt::format("Port {}", i + 1).c_str())) {
m_controllerConfig.m_selectedPort = i;
ImGui::EndTabItem();
}
}
ImGui::EndTabBar();
// if tab is changed while waiting for input, cancel pending
if ((m_controllerConfig.m_pendingButtonMapping != nullptr ||
m_controllerConfig.m_pendingAxisMapping != nullptr) &&
m_controllerConfig.m_pendingPort != m_controllerConfig.m_selectedPort)
{
m_controllerConfig.m_pendingButtonMapping = nullptr;
m_controllerConfig.m_pendingAxisMapping = nullptr;
m_controllerConfig.m_pendingPort = -1;
PADBlockInput(false);
}
// get a list of all available controller's names
std::vector<std::string> controllerList;
controllerList.push_back("None");
for (int i = 0; i < PADCount(); i++) {
// attach index to name for unique name
controllerList.push_back(fmt::format("{}-{}", PADGetNameForControllerIndex(i), i));
}
// get current controller name
const char* tmpName = PADGetName(m_controllerConfig.m_selectedPort);
std::string currentName = "None";
if (tmpName != nullptr) {
currentName = fmt::format("{}-{}", tmpName, PADGetIndexForPort(m_controllerConfig.m_selectedPort));
}
// controller selection combo box
bool changedController = false;
int changedControllerIndex = 0;
ImGui::SetNextItemWidth(400.0f * scale);
if (ImGui::BeginCombo("##ControllerDeviceList", currentName.c_str())) {
for (int i = 0; const auto& name : controllerList) {
if (ImGui::Selectable(name.c_str(), currentName == name)) {
changedControllerIndex = i;
changedController = true;
}
i++;
}
ImGui::EndCombo();
}
// handle controller change
if (changedController) {
if (changedControllerIndex > 0) {
PADSetPortForIndex(changedControllerIndex - 1, m_controllerConfig.m_selectedPort);
}
else if (changedControllerIndex == 0) {
// if "None" selected
PADClearPort(m_controllerConfig.m_selectedPort);
}
PADSerializeMappings();
}
// restore defaults button
ImGui::SameLine();
if (ImGui::Button("Default")) {
PADRestoreDefaultMapping(m_controllerConfig.m_selectedPort);
PADSerializeMappings();
}
// buttons panel
const float uiButtonSize = 40 * scale;
ImVec2 btnSize(110.0f * scale, 30.0f * scale);
ImGuiBeginGroupPanel("Buttons", ImVec2(150 * scale, 20 * scale));
SDL_Gamepad* gamepad = PADGetSDLGamepadForIndex(PADGetIndexForPort(m_controllerConfig.m_selectedPort));
u32 buttonCount;
PADButtonMapping* btnMappingList = PADGetButtonMappings(m_controllerConfig.m_selectedPort, &buttonCount);
if (btnMappingList != nullptr) {
for (int i = 0; i < buttonCount; i++) {
const char* btnName = PADGetButtonName(btnMappingList[i].padButton);
ImVec2 len = ImGui::CalcTextSize(btnName);
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosY(pos.y + len.y / 4);
ImGui::SetCursorPosX(pos.x + abs(len.x - uiButtonSize));
ImGui::Text("%s", btnName);
ImGui::SameLine();
ImGui::SetCursorPosY(pos.y);
std::string dispName;
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingButtonMapping == &btnMappingList[i]) {
dispName = fmt::format("Press a Key...##{}", btnName);
} else {
const char* nativeName = GetNameForGamepadButton(gamepad, btnMappingList[i].nativeButton);
if (nativeName == nullptr) {
nativeName = "[unbound]";
}
dispName = fmt::format("{0}##-{1}", nativeName, i);
}
bool pressed = ImGui::Button(dispName.c_str(),
btnSize);
if (pressed) {
m_controllerConfig.m_isReading = true;
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
m_controllerConfig.m_pendingButtonMapping = &btnMappingList[i];
PADBlockInput(true);
}
}
}
ImGuiEndGroupPanel();
ImGui::SameLine();
uint32_t axisCount;
PADAxisMapping* axisMappingList = PADGetAxisMappings(m_controllerConfig.m_selectedPort, &axisCount);
ImGuiBeginGroupPanel("Analog Triggers", ImVec2(150 * scale, 20 * scale));
PADAxis triggers[] = {PAD_AXIS_TRIGGER_L, PAD_AXIS_TRIGGER_R};
if (axisMappingList != nullptr) {
for (PADAxis trigger : triggers) {
const char* axisName = PADGetAxisName(axisMappingList[trigger].padAxis);
ImVec2 len = ImGui::CalcTextSize(axisName);
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosY(pos.y + len.y / 4);
ImGui::SetCursorPosX(pos.x + abs(len.x - uiButtonSize));
ImGui::Text("%s", axisName);
ImGui::SameLine();
ImGui::SetCursorPosY(pos.y);
std::string dispName;
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[trigger]) {
dispName = fmt::format("Press a Key...##{}", axisName);
} else {
dispName = fmt::format("{0}##-{1}", PADGetNativeAxisName(axisMappingList[trigger].nativeAxis), trigger);
}
bool pressed = ImGui::Button(dispName.c_str(),
btnSize);
if (pressed) {
m_controllerConfig.m_isReading = true;
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[trigger];
PADBlockInput(true);
}
}
}
int port = m_controllerConfig.m_selectedPort;
PADDeadZones* deadZones = PADGetDeadZones(port);
if (deadZones != nullptr) {
ImGui::Text("L Threshold");
ImGui::SameLine();
{
float tmp = static_cast<float>(deadZones->leftTriggerActivationZone * 100.f) / 32767.f;
if (ImGui::DragFloat("##LThreshold", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->leftTriggerActivationZone = static_cast<u16>((tmp / 100.f) * 32767);
PADSerializeMappings();
}
}
}
if (deadZones != nullptr) {
ImGui::Text("R Threshold");
ImGui::SameLine();
{
float tmp = static_cast<float>(deadZones->rightTriggerActivationZone * 100.f) / 32767.f;
if (ImGui::DragFloat("##RThreshold", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->rightTriggerActivationZone = static_cast<u16>((tmp / 100.f) * 32767);
PADSerializeMappings();
}
}
}
ImGuiEndGroupPanel();
ImGui::SameLine();
// main stick panel
ImGuiBeginGroupPanel("Control Stick", ImVec2(150 * scale, 20 * scale));
drawVirtualStick("##mainStick", ImVec2{ mDoCPd_c::getStickX(port), mDoCPd_c::getStickY(port) });
if (axisMappingList != nullptr) {
const PADAxis lStickAxes[] = {PAD_AXIS_LEFT_Y_POS, PAD_AXIS_LEFT_Y_NEG, PAD_AXIS_LEFT_X_NEG, PAD_AXIS_LEFT_X_POS};
for (auto axis : lStickAxes) {
const char* label = PADGetAxisDirectionLabel(axis);
ImVec2 len = ImGui::CalcTextSize(label);
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosY(pos.y + len.y / 4);
ImGui::SetCursorPosX(pos.x + abs(len.x - uiButtonSize));
ImGui::Text("%s", label);
ImGui::SameLine();
ImGui::SetCursorPosY(pos.y);
std::string dispName;
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) {
dispName = fmt::format("Press a Key...##{}", label);
} else {
if (axisMappingList[axis].nativeAxis.nativeAxis != -1) {
const char* signStr;
if (axis == PAD_AXIS_TRIGGER_L || axis == PAD_AXIS_TRIGGER_R) {
signStr = "";
} else if (axisMappingList[axis].nativeAxis.sign == AXIS_SIGN_POSITIVE) {
signStr = "+";
} else {
signStr = "-";
}
dispName = fmt::format("{0}{1}##-{2}", PADGetNativeAxisName(axisMappingList[axis].nativeAxis), signStr, axis);
} else {
assert(axisMappingList[axis].nativeButton != -1);
dispName = fmt::format("{0}##-{1}", PADGetNativeButtonName(axisMappingList[axis].nativeButton), axis);
}
}
bool pressed = ImGui::Button(dispName.c_str(), btnSize);
if (pressed) {
m_controllerConfig.m_isReading = true;
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis];
PADBlockInput(true);
}
}
}
if (deadZones != nullptr) {
ImGui::Text("Dead Zone");
{
float tmp = static_cast<float>(deadZones->stickDeadZone * 100.f) / 32767.f;
if (ImGui::DragFloat("##mainDeadZone", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->stickDeadZone = static_cast<u16>((tmp / 100.f) * 32767);
PADSerializeMappings();
}
}
}
ImGuiEndGroupPanel();
ImGui::SameLine();
// sub stick panel
ImGuiBeginGroupPanel("C Stick", ImVec2(150 * scale, 20 * scale));
drawVirtualStick("##subStick", ImVec2{ mDoCPd_c::getSubStickX(port), mDoCPd_c::getSubStickY(port) });
if (axisMappingList != nullptr) {
const PADAxis rStickAxes[] = {PAD_AXIS_RIGHT_Y_POS, PAD_AXIS_RIGHT_Y_NEG, PAD_AXIS_RIGHT_X_NEG, PAD_AXIS_RIGHT_X_POS};
for (auto axis : rStickAxes) {
const char* label = PADGetAxisDirectionLabel(axisMappingList[axis].padAxis);
ImVec2 len = ImGui::CalcTextSize(label);
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosY(pos.y + len.y / 4);
ImGui::SetCursorPosX(pos.x + abs(len.x - uiButtonSize));
ImGui::Text("%s", label);
ImGui::SameLine();
ImGui::SetCursorPosY(pos.y);
std::string dispName;
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) {
dispName = fmt::format("Press a Key...##sub{}", label);
} else {
if (axisMappingList[axis].nativeAxis.nativeAxis != -1) {
const char* signStr;
if (axis == PAD_AXIS_TRIGGER_L || axis == PAD_AXIS_TRIGGER_R) {
signStr = "";
} else if (axisMappingList[axis].nativeAxis.sign == AXIS_SIGN_POSITIVE) {
signStr = "+";
} else {
signStr = "-";
}
dispName = fmt::format("{0}{1}##-{2}", PADGetNativeAxisName(axisMappingList[axis].nativeAxis), signStr, axis);
} else {
assert(axisMappingList[axis].nativeButton != -1);
dispName = fmt::format("{0}##-{1}", PADGetNativeButtonName(axisMappingList[axis].nativeButton), axis);
}
}
bool pressed = ImGui::Button(fmt::format("{0}##sub{1}", dispName, label).c_str(), btnSize);
if (pressed) {
m_controllerConfig.m_isReading = true;
m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort;
m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis];
PADBlockInput(true);
}
}
}
if (deadZones != nullptr) {
ImGui::Text("Dead Zone");
{
float tmp = static_cast<float>(deadZones->substickDeadZone * 100.f) / 32767.f;
if (ImGui::DragFloat("##subDeadZone", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->substickDeadZone = static_cast<u16>((tmp / 100.f) * 32767);
PADSerializeMappings();
}
}
}
ImGuiEndGroupPanel();
ImGui::SameLine();
// Options panel
ImGuiBeginGroupPanel("Options", ImVec2(150 * scale, -1));
if (deadZones != nullptr) {
if (ImGui::Checkbox("Enable Dead Zones", &deadZones->useDeadzones)) {
PADSerializeMappings();
}
if (ImGui::Checkbox("Emulate Triggers", &deadZones->emulateTriggers)) {
PADSerializeMappings();
}
}
if (PADSupportsRumbleIntensity(m_controllerConfig.m_selectedPort)) {
ImGuiBeginGroupPanel("Rumble Intensity", ImVec2(150 * scale, -1));
u16 low;
u16 high;
(void)PADGetRumbleIntensity(m_controllerConfig.m_selectedPort, &low, &high);
float fLow = (static_cast<float>(low) / 32767.f) * 100.f;
bool changed = ImGui::SliderFloat("Low", &fLow, 0.f, 100.f, "%.0f%%");
float fHigh = (static_cast<float>(high) / 32767.f) * 100.f;
changed |= ImGui::SliderFloat("High", &fHigh, 0.f, 100.f, "%.0f%%");
if (changed) {
PADSetRumbleIntensity(m_controllerConfig.m_selectedPort,
static_cast<u16>((fLow / 100) * 32767),
static_cast<u16>((fHigh / 100) * 32767));
PADSerializeMappings();
}
if (ImGui::Button(fmt::format("{0}...##rumbleTest", m_controllerConfig.m_isRumbling ? "Stop": "Test").c_str(), {-1, 0})) {
PADControlMotor(m_controllerConfig.m_selectedPort, !m_controllerConfig.m_isRumbling ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP_HARD);
m_controllerConfig.m_isRumbling ^= 1;
}
ImGuiEndGroupPanel();
}
ImGuiEndGroupPanel();
ImGui::End();
}
static std::string GetFormattedTime(OSTime ticks) {
OSCalendarTime time;
OSTicksToCalendarTime(ticks, &time);
-3
View File
@@ -47,11 +47,8 @@ namespace dusk {
void draw();
void windowInputViewer();
void windowControllerConfig();
void drawSpeedrunTimerOverlay();
static void ToggleFullscreen();
static void resetForSpeedrunMode();
private:
+174 -6
View File
@@ -3,7 +3,7 @@
#include "bool_button.hpp"
#include "button.hpp"
#include "pane.hpp"
#include "select_button.hpp"
#include "number_button.hpp"
#include <SDL3/SDL_gamepad.h>
#include <SDL3/SDL_keyboard.h>
@@ -279,6 +279,18 @@ s32 keyboard_key_pressed() {
return PAD_KEY_INVALID;
}
u16 percent_to_raw(int percent) {
return static_cast<u16>((static_cast<float>(percent) / 100.f) * 32767.f);
}
int deadzone_raw_to_percent(u16 raw) {
return static_cast<int>((static_cast<float>(raw) * 100.f) / 32767.f + 0.5f);
}
int rumble_raw_to_percent(u16 raw) {
return static_cast<int>((static_cast<float>(raw) / 32767.f) * 100.f + 0.5f);
}
} // namespace
ControllerConfigWindow::ControllerConfigWindow() {
@@ -308,6 +320,7 @@ ControllerConfigWindow::ControllerConfigWindow() {
}
void ControllerConfigWindow::hide(bool close) {
stop_rumble_test();
cancel_pending_binding();
Window::hide(close);
}
@@ -318,16 +331,18 @@ void ControllerConfigWindow::update() {
}
void ControllerConfigWindow::build_port_tab(Rml::Element* content, int port) {
stop_rumble_test();
auto& leftPane = add_child<Pane>(content, Pane::Type::Controlled);
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
mRightPane = &rightPane;
mActivePort = port;
auto addPageButton = [this, &leftPane, &rightPane, port](
Page page, Rml::String key, auto getValue) {
Page page, Rml::String key, auto getValue, auto isDisabled) {
leftPane.register_control(leftPane.add_select_button({
.key = std::move(key),
.getValue = std::move(getValue),
.isDisabled = std::move(isDisabled),
}),
rightPane, [this, port, page](Pane& pane) {
mPage = page;
@@ -335,10 +350,11 @@ void ControllerConfigWindow::build_port_tab(Rml::Element* content, int port) {
});
};
addPageButton(Page::Controller, "Controller", [port] { return current_controller_name(port); });
addPageButton(Page::Buttons, "Buttons", [] { return Rml::String(">"); });
addPageButton(Page::Triggers, "Triggers", [] { return Rml::String(">"); });
addPageButton(Page::Sticks, "Sticks", [] { return Rml::String(">"); });
addPageButton(Page::Controller, "Controller", [port] { return current_controller_name(port); }, [] { return false; });
addPageButton(Page::Buttons, "Buttons", [] { return Rml::String(">"); }, [] { return false; });
addPageButton(Page::Triggers, "Triggers", [] { return Rml::String(">"); }, [] { return false; });
addPageButton(Page::Sticks, "Sticks", [] { return Rml::String(">"); }, [] { return false; });
addPageButton(Page::Rumble, "Rumble", [] { return Rml::String(">"); }, [port] { return !PADSupportsRumbleIntensity(static_cast<u32>(port)); });
leftPane.add_section("Options");
leftPane.register_control(leftPane.add_child<BoolButton>(BoolButton::Props{
@@ -684,6 +700,38 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
});
}
}
if (PADDeadZones* deadZones = PADGetDeadZones(port)) {
pane.add_section("Emulated Trigger Thresholds");
pane.add_child<NumberButton>(NumberButton::Props{
.key = "L Threshold",
.getValue = [deadZones] { return deadzone_raw_to_percent(deadZones->leftTriggerActivationZone); },
.setValue =
[deadZones](int value) {
deadZones->leftTriggerActivationZone = percent_to_raw(value);
PADSerializeMappings();
},
.isDisabled = [deadZones] { return !deadZones->emulateTriggers; },
.min = 0,
.max = 100,
.step = 1,
.suffix = "%",
});
pane.add_child<NumberButton>(NumberButton::Props{
.key = "R Threshold",
.getValue = [deadZones] { return deadzone_raw_to_percent(deadZones->rightTriggerActivationZone); },
.setValue =
[deadZones](int value) {
deadZones->rightTriggerActivationZone = percent_to_raw(value);
PADSerializeMappings();
},
.isDisabled = [deadZones] { return !deadZones->emulateTriggers; },
.min = 0,
.max = 100,
.step = 1,
.suffix = "%",
});
}
break;
}
case Page::Sticks: {
@@ -769,12 +817,121 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
addAxis(PAD_AXIS_LEFT_Y_NEG);
addAxis(PAD_AXIS_LEFT_X_NEG);
addAxis(PAD_AXIS_LEFT_X_POS);
if (PADDeadZones* deadZones = PADGetDeadZones(port)) {
pane.add_child<NumberButton>(NumberButton::Props{
.key = "Deadzone",
.getValue = [deadZones] { return deadzone_raw_to_percent(deadZones->stickDeadZone); },
.setValue =
[deadZones](int value) {
deadZones->stickDeadZone = percent_to_raw(value);
PADSerializeMappings();
},
.isDisabled = [deadZones] { return !deadZones->useDeadzones; },
.min = 0,
.max = 100,
.step = 1,
.suffix = "%",
});
}
pane.add_section("C Stick");
addAxis(PAD_AXIS_RIGHT_Y_POS);
addAxis(PAD_AXIS_RIGHT_Y_NEG);
addAxis(PAD_AXIS_RIGHT_X_NEG);
addAxis(PAD_AXIS_RIGHT_X_POS);
if (PADDeadZones* deadZones = PADGetDeadZones(port)) {
pane.add_child<NumberButton>(NumberButton::Props{
.key = "Deadzone",
.getValue = [deadZones] { return deadzone_raw_to_percent(deadZones->substickDeadZone); },
.setValue =
[deadZones](int value) {
deadZones->substickDeadZone = percent_to_raw(value);
PADSerializeMappings();
},
.isDisabled = [deadZones] { return !deadZones->useDeadzones; },
.min = 0,
.max = 100,
.step = 1,
.suffix = "%",
});
}
break;
}
case Page::Rumble: {
auto& rumbleTest = pane.add_select_button({
.key = "Test Rumble",
.getValue =
[this, port] {
return (mRumbleTestActive && mRumbleTestPort == port) ? Rml::String("Stop")
: Rml::String("Start");
},
});
rumbleTest.on_pressed([this, port] {
if (!PADSupportsRumbleIntensity(static_cast<u32>(port))) {
return;
}
mDoAud_seStartMenu(kSoundItemChange);
if (mRumbleTestActive && mRumbleTestPort == port) {
PADControlMotor(port, PAD_MOTOR_STOP_HARD);
mRumbleTestActive = false;
mRumbleTestPort = -1;
} else {
if (mRumbleTestActive) {
PADControlMotor(mRumbleTestPort, PAD_MOTOR_STOP_HARD);
}
PADControlMotor(port, PAD_MOTOR_RUMBLE);
mRumbleTestActive = true;
mRumbleTestPort = port;
}
});
pane.add_child<NumberButton>(NumberButton::Props{
.key = "Low Rumble Frequency",
.getValue =
[port] {
u16 low = 0;
u16 high = 0;
PADGetRumbleIntensity(static_cast<u32>(port), &low, &high);
return rumble_raw_to_percent(low);
},
.setValue =
[port](int value) {
u16 low = 0;
u16 high = 0;
PADGetRumbleIntensity(static_cast<u32>(port), &low, &high);
PADSetRumbleIntensity(static_cast<u32>(port), percent_to_raw(value), high);
PADSerializeMappings();
},
.isDisabled = [this] { return mRumbleTestActive; },
.min = 0,
.max = 100,
.step = 1,
.suffix = "%",
});
pane.add_child<NumberButton>(NumberButton::Props{
.key = "High Rumble Frequency",
.getValue =
[port] {
u16 low = 0;
u16 high = 0;
PADGetRumbleIntensity(static_cast<u32>(port), &low, &high);
return rumble_raw_to_percent(high);
},
.setValue =
[port](int value) {
u16 low = 0;
u16 high = 0;
PADGetRumbleIntensity(static_cast<u32>(port), &low, &high);
PADSetRumbleIntensity(static_cast<u32>(port), low, percent_to_raw(value));
PADSerializeMappings();
},
.isDisabled = [this] { return mRumbleTestActive; },
.min = 0,
.max = 100,
.step = 1,
.suffix = "%",
});
pane.add_text("Configure your desired rumble intensities, then run a test to check how they feel.");
break;
}
}
@@ -938,4 +1095,15 @@ Rml::String ControllerConfigWindow::pending_key_label() const {
return mPendingBindingArmed ? "Press a key or mouse button..." : "Waiting...";
}
void ControllerConfigWindow::stop_rumble_test() {
if (!mRumbleTestActive) {
return;
}
if (mRumbleTestPort >= PAD_CHAN0 && mRumbleTestPort < PAD_CHANMAX) {
PADControlMotor(mRumbleTestPort, PAD_MOTOR_STOP_HARD);
}
mRumbleTestActive = false;
mRumbleTestPort = -1;
}
} // namespace dusk::ui
+4
View File
@@ -19,6 +19,7 @@ private:
Buttons,
Triggers,
Sticks,
Rumble,
};
void build_port_tab(Rml::Element* content, int port);
@@ -34,6 +35,7 @@ private:
void cancel_pending_binding();
void finish_pending_key_binding();
Rml::String pending_key_label() const;
void stop_rumble_test();
Page mPage = Page::Controller;
Pane* mRightPane = nullptr;
@@ -46,6 +48,8 @@ private:
PADAxisMapping* mPendingAxisMapping = nullptr;
int mPendingKeyButton = -1;
int mPendingKeyAxis = -1;
bool mRumbleTestActive = false;
int mRumbleTestPort = -1;
};
} // namespace dusk::ui