Controller navigation.

This commit is contained in:
Dario 2024-12-04 19:41:07 -03:00
parent 0ef5c8bbf6
commit cccc428755
2 changed files with 204 additions and 9 deletions

View file

@ -40,6 +40,8 @@ void HostStartup()
g_codeCache.Init();
g_memory.Alloc(XMAIOBegin, 0xFFFF, MEM_COMMIT);
hid::Init();
}
// Name inspired from nt's entry point
@ -86,7 +88,6 @@ void KiSystemStartup()
}
XAudioInitializeSystem();
hid::Init();
}
uint32_t LdrLoadModule(const char* path)

View file

@ -4,9 +4,11 @@
#include <install/installer.h>
#include <gpu/video.h>
#include <hid/hid.h>
#include <locale/locale.h>
#include <ui/imgui_utils.h>
#include <ui/message_window.h>
#include <ui/sdl_listener.h>
#include <ui/window.h>
#include <res/images/installer/install_001.dds.h>
@ -128,6 +130,162 @@ static std::string g_currentMessagePrompt = "";
static bool g_currentMessagePromptConfirmation = false;
static int g_currentMessageResult = -1;
static bool g_currentMessageUpdateRemaining = false;
static ImVec2 g_joypadAxis = {};
static int g_currentCursorIndex = -1;
static int g_currentCursorDefault = 0;
static bool g_currentCursorAccepted = false;
static std::vector<std::pair<ImVec2, ImVec2>> g_currentCursorRects;
class SDLEventListenerForInstaller : public SDLEventListener
{
public:
void OnSDLEvent(SDL_Event *event) override
{
constexpr float AxisValueRange = 32767.0f;
constexpr float AxisTapRange = 0.5f;
if (!InstallerWizard::s_isVisible || !g_currentMessagePrompt.empty())
{
return;
}
ImVec2 tapDirection = {};
switch (event->type)
{
case SDL_KEYDOWN:
switch (event->key.keysym.scancode)
{
case SDL_SCANCODE_LEFT:
case SDL_SCANCODE_RIGHT:
tapDirection.x = (event->key.keysym.scancode == SDL_SCANCODE_RIGHT) ? 1.0f : -1.0f;
break;
case SDL_SCANCODE_UP:
case SDL_SCANCODE_DOWN:
tapDirection.y = (event->key.keysym.scancode == SDL_SCANCODE_DOWN) ? 1.0f : -1.0f;
break;
case SDL_SCANCODE_RETURN:
case SDL_SCANCODE_KP_ENTER:
g_currentCursorAccepted = true;
break;
}
break;
case SDL_CONTROLLERBUTTONDOWN:
switch (event->cbutton.button)
{
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
tapDirection = { -1.0f, 0.0f };
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
tapDirection = { 1.0f, 0.0f };
break;
case SDL_CONTROLLER_BUTTON_DPAD_UP:
tapDirection = { 0.0f, -1.0f };
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
tapDirection = { 0.0f, 1.0f };
break;
case SDL_CONTROLLER_BUTTON_A:
g_currentCursorAccepted = true;
break;
}
break;
case SDL_CONTROLLERAXISMOTION:
{
if (event->caxis.axis < 2)
{
float newAxisValue = event->caxis.value / AxisValueRange;
bool sameDirection = (newAxisValue * g_joypadAxis[event->caxis.axis]) > 0.0f;
bool wasInRange = abs(g_joypadAxis[event->caxis.axis]) > AxisTapRange;
bool isInRange = abs(newAxisValue) > AxisTapRange;
if (sameDirection && !wasInRange && isInRange)
{
tapDirection[event->caxis.axis] = newAxisValue;
}
g_joypadAxis[event->caxis.axis] = newAxisValue;
}
break;
}
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEMOTION:
{
g_currentCursorIndex = -1;
for (size_t i = 0; i < g_currentCursorRects.size(); i++)
{
auto &currentRect = g_currentCursorRects[i];
if (ImGui::IsMouseHoveringRect(currentRect.first, currentRect.second, false))
{
g_currentCursorIndex = int(i);
if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_LEFT)
{
g_currentCursorAccepted = true;
}
break;
}
}
break;
}
}
if (tapDirection.x != 0.0f || tapDirection.y != 0.0f)
{
int newCursorIndex = -1;
if (g_currentCursorIndex >= g_currentCursorRects.size() || g_currentCursorIndex < 0)
{
newCursorIndex = g_currentCursorDefault;
}
else
{
auto &currentRect = g_currentCursorRects[g_currentCursorIndex];
ImVec2 currentPoint = ImVec2
(
(currentRect.first.x + currentRect.second.x) / 2.0f + tapDirection.x * (currentRect.second.x - currentRect.first.x) / 2.0f,
(currentRect.first.y + currentRect.second.y) / 2.0f + tapDirection.y * (currentRect.second.y - currentRect.first.y) / 2.0f
);
float closestDistance = FLT_MAX;
for (size_t i = 0; i < g_currentCursorRects.size(); i++)
{
if (g_currentCursorIndex == i)
{
continue;
}
auto &targetRect = g_currentCursorRects[i];
ImVec2 targetPoint = ImVec2
(
(targetRect.first.x + targetRect.second.x) / 2.0f + tapDirection.x * (targetRect.first.x - targetRect.second.x) / 2.0f,
(targetRect.first.y + targetRect.second.y) / 2.0f + tapDirection.y * (targetRect.first.y - targetRect.second.y) / 2.0f
);
ImVec2 delta = ImVec2(targetPoint.x - currentPoint.x, targetPoint.y - currentPoint.y);
float projectedDistance = delta.x * tapDirection.x + delta.y * tapDirection.y;
float manhattanDistance = abs(delta.x) + abs(delta.y);
if (projectedDistance > 0.0f && manhattanDistance < closestDistance)
{
newCursorIndex = int(i);
closestDistance = manhattanDistance;
}
}
}
if (newCursorIndex >= 0)
{
// TODO: Play sound.
g_currentCursorIndex = newCursorIndex;
}
}
}
};
static SDLEventListenerForInstaller g_eventListener;
const char CREDITS_TEXT[] = "- Sajid (RIP)\n- imgui sega balls!";
@ -212,6 +370,38 @@ static double ComputeHermiteMotionInstallerLoop(double timeAppear, double speed,
return (cosf(M_PI * ComputeMotionInstallerLoop(timeAppear, speed, offset) + M_PI) + 1) / 2;
}
static bool PushCursorRect(ImVec2 min, ImVec2 max, bool &cursorPressed, bool makeDefault = false)
{
int currentIndex = int(g_currentCursorRects.size());
g_currentCursorRects.emplace_back(min, max);
if (makeDefault)
{
g_currentCursorDefault = currentIndex;
}
if (g_currentCursorIndex == currentIndex)
{
if (g_currentCursorAccepted)
{
cursorPressed = true;
g_currentCursorAccepted = false;
}
return true;
}
else
{
return false;
}
}
static void ResetCursorRects()
{
g_currentCursorDefault = 0;
g_currentCursorRects.clear();
}
static void DrawBackground()
{
auto &res = ImGui::GetIO().DisplaySize;
@ -512,7 +702,7 @@ static ImVec2 ComputeTextSize(ImFont *font, const char *text, float size, float
return textSize;
}
static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sourceButton, bool buttonEnabled, bool &buttonPressed, float maxTextWidth = FLT_MAX)
static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sourceButton, bool buttonEnabled, bool &buttonPressed, float maxTextWidth = FLT_MAX, bool makeDefault = false)
{
buttonPressed = false;
@ -526,14 +716,13 @@ static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sour
int baser = 0;
int baseg = 0;
if (g_currentMessagePrompt.empty() && !sourceButton && buttonEnabled && (alpha >= 1.0f) && ImGui::IsMouseHoveringRect(min, max, false))
if (g_currentMessagePrompt.empty() && !sourceButton && buttonEnabled && (alpha >= 1.0f))
{
baser = 48;
baseg = 32;
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
bool cursorOnButton = PushCursorRect(min, max, buttonPressed, makeDefault);
if (cursorOnButton)
{
buttonPressed = true;
baser = 48;
baseg = 32;
}
}
@ -794,7 +983,7 @@ static void DrawLanguagePicker()
// TODO: The active button should change its style to show an enabled toggle if it matches the current language.
DrawButton(min, max, LANGUAGE_TEXT[i], false, true, buttonPressed);
DrawButton(min, max, LANGUAGE_TEXT[i], false, true, buttonPressed, FLT_MAX, LANGUAGE_ENUM[i] == ELanguage::English);
if (buttonPressed)
{
Config::Language = LANGUAGE_ENUM[i];
@ -1155,6 +1344,7 @@ void InstallerWizard::Draw()
return;
}
ResetCursorRects();
DrawBackground();
DrawLeftImage();
DrawScanlineBars();
@ -1209,6 +1399,10 @@ bool InstallerWizard::Run(bool skipGame)
{
NFD_Init();
// Guarantee one controller is initialized. We'll rely on SDL's event loop to get the controller events.
XAMINPUT_STATE inputState;
hid::GetState(0, &inputState);
if (skipGame)
{
for (int i = 0; i < int(DLC::Count); i++)