Skip to content

Fix Menu cursor and Gamepad #206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 25, 2025
Merged
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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
compile_commands.json
**/.ccls-cache
**/.cache
**/.clangd


**/build*

Expand Down
80 changes: 40 additions & 40 deletions Source/Managers/UInputMan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,9 @@ UInputMan::~UInputMan() {
void UInputMan::Clear() {
m_SkipHandlingSpecialInput = false;
m_TextInput.clear();
;
m_NumJoysticks = 0;
m_OverrideInput = false;
m_AbsoluteMousePos.Reset();
m_RawMouseMovement.Reset();
m_AnalogMouseData.Reset();
m_MouseSensitivity = 0.6F;
m_MouseWheelChange = 0;
m_TrapMousePos = false;
m_PlayerScreenMouseBounds = {0, 0, 0, 0};
m_MouseTrapRadius = 350;
Expand Down Expand Up @@ -89,9 +84,9 @@ int UInputMan::Initialize() {
g_ConsoleMan.PrintString("ERROR: Failed to connect gamepad " + std::to_string(index) + " " + std::string(SDL_GetError()));
continue;
}
SDL_SetGamepadPlayerIndex(controller, index);
s_PrevJoystickStates[controllerIndex] = Gamepad(index, joysticks[index], SDL_GAMEPAD_AXIS_COUNT, SDL_GAMEPAD_BUTTON_COUNT);
s_ChangedJoystickStates[controllerIndex] = Gamepad(index, joysticks[index], SDL_GAMEPAD_AXIS_COUNT, SDL_GAMEPAD_BUTTON_COUNT);
SDL_SetGamepadPlayerIndex(controller, controllerIndex);
s_PrevJoystickStates[controllerIndex] = Gamepad(controllerIndex, joysticks[index], SDL_GAMEPAD_AXIS_COUNT, SDL_GAMEPAD_BUTTON_COUNT);
s_ChangedJoystickStates[controllerIndex] = Gamepad(controllerIndex, joysticks[index], SDL_GAMEPAD_AXIS_COUNT, SDL_GAMEPAD_BUTTON_COUNT);
auto playerScheme = std::find_if(m_ControlScheme.begin(), m_ControlScheme.end(), [controllerIndex](auto& scheme) { return scheme.GetDevice() == controllerIndex + InputDevice::DEVICE_GAMEPAD_1; });
playerScheme->SetDeviceID({.gamepad = joysticks[index]});
controllerIndex++;
Expand All @@ -102,8 +97,8 @@ int UInputMan::Initialize() {
g_ConsoleMan.PrintString("ERROR: Failed to connect joystick.");
continue;
}
s_PrevJoystickStates[controllerIndex] = Gamepad(index, joysticks[index], SDL_GetNumJoystickAxes(joy), SDL_GetNumJoystickButtons(joy));
s_ChangedJoystickStates[controllerIndex] = Gamepad(index, joysticks[index], SDL_GetNumJoystickAxes(joy), SDL_GetNumJoystickButtons(joy));
s_PrevJoystickStates[controllerIndex] = Gamepad(controllerIndex, joysticks[index], SDL_GetNumJoystickAxes(joy), SDL_GetNumJoystickButtons(joy));
s_ChangedJoystickStates[controllerIndex] = Gamepad(controllerIndex, joysticks[index], SDL_GetNumJoystickAxes(joy), SDL_GetNumJoystickButtons(joy));
auto playerScheme = std::find_if(m_ControlScheme.begin(), m_ControlScheme.end(), [controllerIndex](auto& scheme) { return scheme.GetDevice() == controllerIndex + InputDevice::DEVICE_GAMEPAD_1; });
playerScheme->SetDeviceID({.gamepad = joysticks[index]});
controllerIndex++;
Expand Down Expand Up @@ -163,7 +158,7 @@ Vector UInputMan::AnalogAimValues(int whichPlayer) {
Vector aimValues(0, 0);
if (device == InputDevice::DEVICE_MOUSE_KEYB) {
if (!m_EnableMultiMouseKeyboard) {
aimValues = (m_AnalogMouseData / m_MouseTrapRadius);
aimValues = (m_MouseStates.at(0).analogAim / m_MouseTrapRadius);
} else {
if (auto mouse = m_MouseStates.find(m_ControlScheme.at(whichPlayer).GetDeviceID().mouseKeyboard.mouse); mouse != m_MouseStates.end()) {
aimValues = mouse->second.analogAim / m_MouseTrapRadius;
Expand Down Expand Up @@ -367,7 +362,7 @@ Vector UInputMan::GetAbsoluteMousePosition(int whichPlayer) const {

void UInputMan::SetAbsoluteMousePosition(const Vector& pos, int whichPlayer) {
if (whichPlayer == Players::NoPlayer || !m_EnableMultiMouseKeyboard) {
m_AbsoluteMousePos = pos;
m_MouseStates.at(0).position = pos;
} else if (m_ControlScheme.at(whichPlayer).GetDevice() == InputDevice::DEVICE_MOUSE_KEYB) {
if (auto mouse = m_MouseStates.find(m_ControlScheme.at(whichPlayer).GetDeviceID().mouseKeyboard.mouse); mouse != m_MouseStates.end()) {
mouse->second.position = pos;
Expand Down Expand Up @@ -453,6 +448,20 @@ void UInputMan::ClearMouseButtons() {
}
}

int UInputMan::MouseWheelMovedByPlayer(int player) const {
if (player == Players::NoPlayer || player < Players::PlayerOne || player >= Players::MaxPlayerCount || !m_EnableMultiMouseKeyboard) {
return m_MouseStates.at(0).wheelChange;
}
InputDevice playerInput = m_ControlScheme.at(player).GetDevice();
if (playerInput == InputDevice::DEVICE_MOUSE_KEYB) {
DeviceID playerDevice = m_ControlScheme.at(player).GetDeviceID();
if (auto mouse = m_MouseStates.find(playerDevice.mouseKeyboard.mouse); mouse != m_MouseStates.end()) {
return mouse->second.wheelChange;
}
}
return m_MouseStates.at(0).wheelChange;
}

bool UInputMan::AnyMouseButtonPress(SDL_MouseID mouseID) const {
for (int button = MouseButtons::MOUSE_LEFT; button < MouseButtons::MAX_MOUSE_BUTTONS; ++button) {
if (MouseButtonPressed(button, NoPlayer, mouseID)) {
Expand Down Expand Up @@ -490,9 +499,9 @@ void UInputMan::ForceMouseWithinBox(int x, int y, int width, int height, int whi
rightMostPos = std::clamp(leftPos + width, leftPos, rightMostPos - 1);
bottomMostPos = std::clamp(topPos + height, topPos, bottomMostPos - 1);

if (!WithinBox(m_AbsoluteMousePos, static_cast<float>(leftPos), static_cast<float>(topPos), static_cast<float>(rightMostPos), static_cast<float>(bottomMostPos))) {
int limitX = std::clamp(m_AbsoluteMousePos.GetFloorIntX(), leftPos, rightMostPos);
int limitY = std::clamp(m_AbsoluteMousePos.GetFloorIntY(), topPos, bottomMostPos);
if (!WithinBox(m_MouseStates[0].position, static_cast<float>(leftPos), static_cast<float>(topPos), static_cast<float>(rightMostPos), static_cast<float>(bottomMostPos))) {
int limitX = std::clamp(m_MouseStates[0].position.GetFloorIntX(), leftPos, rightMostPos);
int limitY = std::clamp(m_MouseStates[0].position.GetFloorIntY(), topPos, bottomMostPos);
SDL_WarpMouseInWindow(g_WindowMan.GetWindow(), limitX, limitY);
}
} else {
Expand Down Expand Up @@ -852,8 +861,6 @@ int UInputMan::Update() {
}

m_TextInput.clear();
m_MouseWheelChange = 0;
m_RawMouseMovement.Reset();
for (auto& [mouseID, mouse]: m_MouseStates) {
mouse.wheelChange = 0;
mouse.relativeMotion.Reset();
Expand Down Expand Up @@ -931,15 +938,13 @@ int UInputMan::Update() {
m_MouseStates[0].position = {inputEvent.motion.x, inputEvent.motion.y};
m_MouseStates[0].relativeMotion += {inputEvent.motion.xrel, inputEvent.motion.yrel};

m_RawMouseMovement += Vector(static_cast<float>(inputEvent.motion.xrel), static_cast<float>(inputEvent.motion.yrel));
m_AbsoluteMousePos.SetXY(static_cast<float>(inputEvent.motion.x), static_cast<float>(inputEvent.motion.y));

if (g_WindowMan.FullyCoversAllDisplays()) {
int windowPosX = 0;
int windowPosY = 0;
SDL_GetWindowPosition(SDL_GetWindowFromID(inputEvent.motion.windowID), &windowPosX, &windowPosY);
Vector windowCoord(static_cast<float>(g_WindowMan.GetDisplayArrangementAbsOffsetX() + windowPosX), static_cast<float>(g_WindowMan.GetDisplayArrangementAbsOffsetY() + windowPosY));
m_AbsoluteMousePos += windowCoord;
mouse.position += windowCoord;
m_MouseStates[0].position += windowCoord;
}
break;
}
Expand All @@ -959,7 +964,6 @@ int UInputMan::Update() {
break;
}
case SDL_EVENT_MOUSE_WHEEL: {
m_MouseWheelChange += inputEvent.wheel.direction == SDL_MOUSEWHEEL_NORMAL ? inputEvent.wheel.y : -inputEvent.wheel.y;
m_MouseStates[inputEvent.wheel.which].wheelChange += inputEvent.wheel.direction == SDL_MOUSEWHEEL_NORMAL ? inputEvent.wheel.y : -inputEvent.wheel.y;
m_MouseStates[0].wheelChange += inputEvent.wheel.direction == SDL_MOUSEWHEEL_NORMAL ? inputEvent.wheel.y : -inputEvent.wheel.y;
break;
Expand Down Expand Up @@ -988,9 +992,9 @@ int UInputMan::Update() {
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
if (std::vector<Gamepad>::iterator device = std::find(s_PrevJoystickStates.begin(), s_PrevJoystickStates.end(), inputEvent.type == SDL_EVENT_GAMEPAD_AXIS_MOTION ? inputEvent.gaxis.which : inputEvent.jaxis.which); device != s_PrevJoystickStates.end()) {
if (SDL_IsGamepad(device->m_DeviceIndex) && inputEvent.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
if (SDL_IsGamepad(device->m_JoystickID) && inputEvent.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
UpdateJoystickAxis(device, inputEvent.gaxis.axis, inputEvent.gaxis.value);
} else if (!SDL_IsGamepad(device->m_DeviceIndex)) {
} else if (!SDL_IsGamepad(device->m_JoystickID)) {
UpdateJoystickAxis(device, inputEvent.jaxis.axis, inputEvent.jaxis.value);
}
}
Expand All @@ -1003,7 +1007,7 @@ int UInputMan::Update() {
if (std::vector<Gamepad>::iterator device = std::find(s_PrevJoystickStates.begin(), s_PrevJoystickStates.end(), joystickEvent ? inputEvent.jbutton.which : inputEvent.gbutton.which); device != s_PrevJoystickStates.end()) {
int button = -1;
int down = false;
if (SDL_IsGamepad(device->m_DeviceIndex)) {
if (SDL_IsGamepad(device->m_JoystickID)) {
if (inputEvent.type == SDL_EVENT_GAMEPAD_BUTTON_UP || inputEvent.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
button = inputEvent.gbutton.button;
down = inputEvent.gbutton.down;
Expand All @@ -1028,7 +1032,7 @@ int UInputMan::Update() {
case SDL_EVENT_JOYSTICK_REMOVED:
case SDL_EVENT_GAMEPAD_REMOVED:
if (std::vector<Gamepad>::iterator prevDevice = std::find(s_PrevJoystickStates.begin(), s_PrevJoystickStates.end(), inputEvent.jdevice.which); prevDevice != s_PrevJoystickStates.end()) {
g_ConsoleMan.PrintString("INFO: Gamepad " + std::to_string(prevDevice->m_DeviceIndex + 1) + " disconnected!");
g_ConsoleMan.PrintString("INFO: Gamepad " + std::to_string(prevDevice - s_PrevJoystickStates.begin() + 1) + " disconnected!");
SDL_CloseGamepad(SDL_GetGamepadFromID(prevDevice->m_JoystickID));
prevDevice->m_JoystickID = -1;
std::fill(prevDevice->m_Axis.begin(), prevDevice->m_Axis.end(), 0);
Expand All @@ -1046,7 +1050,6 @@ int UInputMan::Update() {
}
}
m_EventQueue.clear();
m_RawMouseMovement *= m_MouseSensitivity;
for (auto& [mouseID, mouse]: m_MouseStates) {
mouse.relativeMotion *= m_MouseSensitivity;
}
Expand Down Expand Up @@ -1170,9 +1173,6 @@ void UInputMan::HandleSpecialInput() {
void UInputMan::UpdateMouseInput() {
// Detect and store mouse movement input, translated to analog stick emulation
// TODO: Figure out a less shit solution to updating the mouse in GUIs when there are no mouse players configured, i.e. no player input scheme is using mouse+keyboard. For not just check if we're out of Activity.
m_AnalogMouseData.m_X += m_RawMouseMovement.m_X * 3;
m_AnalogMouseData.m_Y += m_RawMouseMovement.m_Y * 3;
m_AnalogMouseData.CapMagnitude(m_MouseTrapRadius);
for (auto& [mouseID, mouse]: m_MouseStates) {
mouse.analogAim += mouse.relativeMotion * 3;
mouse.analogAim.CapMagnitude(m_MouseTrapRadius);
Expand All @@ -1190,8 +1190,8 @@ void UInputMan::UpdateMouseInput() {

// Enable the mouse cursor positioning again after having been disabled. Only do this when the mouse is within the drawing area so it
// won't cause the whole window to move if the user clicks the title bar and unintentionally drags it due to programmatic positioning.
int mousePosX = m_AbsoluteMousePos.m_X / g_WindowMan.GetResMultiplier();
int mousePosY = m_AbsoluteMousePos.m_Y / g_WindowMan.GetResMultiplier();
int mousePosX = m_MouseStates.at(0).position.m_X / g_WindowMan.GetResMultiplier();
int mousePosY = m_MouseStates.at(0).position.m_Y / g_WindowMan.GetResMultiplier();
if (m_DisableMouseMoving && m_PrepareToEnableMouseMoving && (mousePosX >= 0 && mousePosX < g_WindowMan.GetResX() && mousePosY >= 0 && mousePosY < g_WindowMan.GetResY())) {
m_DisableMouseMoving = m_PrepareToEnableMouseMoving = false;
}
Expand Down Expand Up @@ -1284,25 +1284,25 @@ void UInputMan::UpdateJoystickDigitalAxis() {
}
}

void UInputMan::HandleGamepadHotPlug(SDL_JoystickID deviceIndex) {
void UInputMan::HandleGamepadHotPlug(SDL_JoystickID joystickID) {
SDL_Joystick* controller = nullptr;
int controllerIndex = 0;

for (controllerIndex = 0; controllerIndex < s_PrevJoystickStates.size(); ++controllerIndex) {
if (s_PrevJoystickStates[controllerIndex].m_JoystickID == deviceIndex) {
if (s_PrevJoystickStates[controllerIndex].m_JoystickID == joystickID) {
return;
}
if (s_PrevJoystickStates[controllerIndex].m_JoystickID == -1) {
if (SDL_IsGamepad(deviceIndex)) {
SDL_Gamepad* gameController = SDL_OpenGamepad(deviceIndex);
if (SDL_IsGamepad(joystickID)) {
SDL_Gamepad* gameController = SDL_OpenGamepad(joystickID);
if (!gameController) {
g_ConsoleMan.PrintString("ERROR: Failed to connect Gamepad!");
break;
}
controller = SDL_GetGamepadJoystick(gameController);
SDL_SetGamepadPlayerIndex(gameController, controllerIndex);
} else {
controller = SDL_OpenJoystick(deviceIndex);
controller = SDL_OpenJoystick(joystickID);
}
if (!controller) {
g_ConsoleMan.PrintString("ERROR: Failed to connect Gamepad!");
Expand All @@ -1320,15 +1320,15 @@ void UInputMan::HandleGamepadHotPlug(SDL_JoystickID deviceIndex) {
if (controller) {
int numAxis = 0;
int numButtons = 0;
if (SDL_IsGamepad(deviceIndex)) {
if (SDL_IsGamepad(joystickID)) {
numAxis = SDL_GAMEPAD_AXIS_COUNT;
numButtons = SDL_GAMEPAD_BUTTON_COUNT;
} else {
numAxis = SDL_GetNumJoystickAxes(controller);
numButtons = SDL_GetNumJoystickButtons(controller);
}
s_PrevJoystickStates[controllerIndex] = Gamepad(controllerIndex, deviceIndex, numAxis, numButtons);
s_ChangedJoystickStates[controllerIndex] = Gamepad(controllerIndex, deviceIndex, numAxis, numButtons);
s_PrevJoystickStates[controllerIndex] = Gamepad(controllerIndex, joystickID, numAxis, numButtons);
s_ChangedJoystickStates[controllerIndex] = Gamepad(controllerIndex, joystickID, numAxis, numButtons);
m_NumJoysticks++;
}
}
19 changes: 6 additions & 13 deletions Source/Managers/UInputMan.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,15 +343,12 @@ namespace RTE {

/// Gets whether the mouse wheel has been moved past the threshold limit in either direction this frame.
/// @return The direction the mouse wheel has been moved which is past that threshold. 0 means not past, negative means moved down, positive means moved up.
int MouseWheelMoved() const { return m_MouseWheelChange; }
int MouseWheelMoved() const { return m_MouseStates.at(0).wheelChange; }

/// Gets the relative mouse wheel position for the specified player.
/// @param player The player to get mouse wheel position for.
/// @return The relative mouse wheel position for the specified player.
int MouseWheelMovedByPlayer(int player) const {
return m_MouseWheelChange;
}

int MouseWheelMovedByPlayer(int player) const;
/// Return true if there are any mouse button presses at all.
/// @return Whether any mouse buttons have been pressed at all since last frame.
bool AnyMouseButtonPress(SDL_MouseID mouseID = 0) const;
Expand Down Expand Up @@ -509,11 +506,7 @@ namespace RTE {
std::array<InputScheme, Players::MaxPlayerCount> m_ControlScheme; //!< Which control scheme is being used by each player.
const Icon* m_DeviceIcons[InputDevice::DEVICE_COUNT]; //!< The Icons representing all different devices.

Vector m_AbsoluteMousePos; //!< The absolute mouse position in screen coordinates.
Vector m_RawMouseMovement; //!< The raw absolute movement of the mouse between the last two Updates.
Vector m_AnalogMouseData; //!< The emulated analog stick position of the mouse.
float m_MouseSensitivity; //!< Mouse sensitivity multiplier while in Activity. HAS NO EFFECT IN MENUS.
int m_MouseWheelChange; //!< The relative mouse wheel position since last reset of it.

bool m_TrapMousePos; //!< Whether the mouse is trapped in the middle of the screen each update or not.
float m_MouseTrapRadius; //!< The radius (in pixels) of the circle trapping the mouse for analog mouse data.
Expand All @@ -532,8 +525,8 @@ namespace RTE {
bool m_PrepareToEnableMouseMoving;

static constexpr double c_GamepadAxisLimit = 32767.0; //!< Maximum axis value as defined by SDL (int16 max).
static constexpr int c_AxisDigitalPressedThreshold = 8192; //!< Digital Axis threshold value as defined by allegro.
static constexpr int c_AxisDigitalReleasedThreshold = c_AxisDigitalPressedThreshold - 100; //!< Digital Axis release threshold, to debounce values.
static constexpr int c_AxisDigitalPressedThreshold = 16384; //!< Digital Axis threshold value as defined by allegro.
static constexpr int c_AxisDigitalReleasedThreshold = 8192; //!< Digital Axis release threshold, to debounce values.

#pragma region Mouse Handling
/// Forces the mouse within a specific player's screen area.
Expand Down Expand Up @@ -606,8 +599,8 @@ namespace RTE {
void UpdateJoystickDigitalAxis();

/// Connect a joystick or gamepad device and add it to the joystick list if a slot is available (up to max player count).
/// @param deviceIndex The device index (generated by the connected event or a value up to SDL_NumJoysticks()).
void HandleGamepadHotPlug(SDL_JoystickID deviceIndex);
/// @param joystickID The SDL_JoystickID of the added Gamepad, usually from the corresponding device event.
void HandleGamepadHotPlug(SDL_JoystickID joystickID);
#pragma endregion

/// Clears all the member variables of this UInputMan, effectively resetting the members of this abstraction level only.
Expand Down
2 changes: 1 addition & 1 deletion Source/System/Gamepad.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace RTE {

/// Structure for storing SDL_Gamepad or SDL_Joystick states.
struct Gamepad {
int m_DeviceIndex = -1; //!< The SDL device index.
int m_DeviceIndex = -1; //!< The internal device index.
SDL_JoystickID m_JoystickID = -1; //!< The joystick ID for event handling.
std::vector<int> m_Axis; //!< Array of analog axis states.
std::vector<int> m_DigitalAxis; //!< Array of digital axis states. Should be updated when analog axis crosses half value 8192.
Expand Down