|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <GLFW/glfw3.h> |
| 4 | + |
| 5 | +#include <cstring> |
| 6 | +#include <glm/glm.hpp> |
| 7 | +#include <iomanip> |
| 8 | +#include <iostream> |
| 9 | + |
| 10 | +namespace our { |
| 11 | + |
| 12 | + class Joystick { |
| 13 | + private: |
| 14 | + bool enabled; |
| 15 | + bool currentButtons[GLFW_GAMEPAD_BUTTON_LAST + 1], previousButtons[GLFW_GAMEPAD_BUTTON_LAST + 1]; |
| 16 | + float currentAxes[GLFW_GAMEPAD_AXIS_LAST + 1], previousAxes[GLFW_GAMEPAD_AXIS_LAST + 1]; |
| 17 | + int currentJoystickId = -1; |
| 18 | + bool currentTriggersPressed[2] = {false, false}, previousTriggersPressed[2] = {false, false}; |
| 19 | + |
| 20 | + public: |
| 21 | + Joystick() : enabled(false) { |
| 22 | + disable(); |
| 23 | + } |
| 24 | + |
| 25 | + int getFirstGamepad() { |
| 26 | + for (int i = GLFW_JOYSTICK_1; i <= GLFW_JOYSTICK_LAST; i++) { |
| 27 | + if (glfwJoystickPresent(i) && glfwJoystickIsGamepad(i)) return i; |
| 28 | + } |
| 29 | + return -1; // none connected |
| 30 | + } |
| 31 | + |
| 32 | + void enable() { |
| 33 | + enabled = true; |
| 34 | + std::memset(currentButtons, 0, sizeof(currentButtons)); |
| 35 | + std::memset(previousButtons, 0, sizeof(previousButtons)); |
| 36 | + std::memset(currentAxes, 0, sizeof(currentAxes)); |
| 37 | + std::memset(previousAxes, 0, sizeof(previousAxes)); |
| 38 | + currentJoystickId = getFirstGamepad(); |
| 39 | + GLFWgamepadstate state; |
| 40 | + if (glfwJoystickIsGamepad(currentJoystickId) && glfwGetGamepadState(currentJoystickId, &state)) { |
| 41 | + for (int i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) |
| 42 | + currentButtons[i] = previousButtons[i] = state.buttons[i] == GLFW_PRESS; |
| 43 | + for (int i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) currentAxes[i] = previousAxes[i] = state.axes[i]; |
| 44 | + currentTriggersPressed[0] = previousTriggersPressed[0] = |
| 45 | + state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] > 0.01f - 1; |
| 46 | + currentTriggersPressed[1] = previousTriggersPressed[1] = |
| 47 | + state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.01f - 1; |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + void disable() { |
| 52 | + enabled = false; |
| 53 | + currentJoystickId = -1; |
| 54 | + std::memset(currentButtons, 0, sizeof(currentButtons)); |
| 55 | + std::memset(previousButtons, 0, sizeof(previousButtons)); |
| 56 | + std::memset(currentAxes, 0, sizeof(currentAxes)); |
| 57 | + std::memset(previousAxes, 0, sizeof(previousAxes)); |
| 58 | + } |
| 59 | + |
| 60 | + void update() { |
| 61 | + if (!enabled) return; |
| 62 | + std::memcpy(previousButtons, currentButtons, sizeof(previousButtons)); |
| 63 | + std::memcpy(previousAxes, currentAxes, sizeof(previousAxes)); |
| 64 | + std::memcpy(previousTriggersPressed, currentTriggersPressed, sizeof(previousTriggersPressed)); |
| 65 | + GLFWgamepadstate state; |
| 66 | + if (glfwJoystickIsGamepad(currentJoystickId) && glfwGetGamepadState(currentJoystickId, &state)) { |
| 67 | + for (int i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) currentButtons[i] = state.buttons[i] == GLFW_PRESS; |
| 68 | + for (int i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) currentAxes[i] = state.axes[i]; |
| 69 | + currentTriggersPressed[0] = state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] > 0.01f - 1; |
| 70 | + currentTriggersPressed[1] = state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.01f - 1; |
| 71 | + } else { |
| 72 | + // Joystick disconnected mid-session — clear current but keep previous for justReleased to fire |
| 73 | + std::memset(currentButtons, 0, sizeof(currentButtons)); |
| 74 | + std::memset(currentAxes, 0, sizeof(currentAxes)); |
| 75 | + std::memset(currentTriggersPressed, 0, sizeof(currentTriggersPressed)); |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + void joystickEvent(int jid, int event) { |
| 80 | + if (event == GLFW_DISCONNECTED && jid == currentJoystickId && enabled) { |
| 81 | + enabled = false; // Automatically disable if the joystick is disconnected |
| 82 | + currentJoystickId = -1; |
| 83 | + std::memset(currentButtons, 0, sizeof(currentButtons)); |
| 84 | + std::memset(currentAxes, 0, sizeof(currentAxes)); |
| 85 | + std::memset(currentTriggersPressed, 0, sizeof(currentTriggersPressed)); |
| 86 | + } else if (event == GLFW_CONNECTED && !enabled) { |
| 87 | + enable(); // Automatically enable if the joystick is connected |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + [[nodiscard]] bool isPressed(int button) const { |
| 92 | + return currentButtons[button]; |
| 93 | + } |
| 94 | + [[nodiscard]] bool justPressed(int button) const { |
| 95 | + return currentButtons[button] && !previousButtons[button]; |
| 96 | + } |
| 97 | + [[nodiscard]] bool justReleased(int button) const { |
| 98 | + return !currentButtons[button] && previousButtons[button]; |
| 99 | + } |
| 100 | + [[nodiscard]] float getAxis(int axis) const { |
| 101 | + return currentAxes[axis]; |
| 102 | + } |
| 103 | + |
| 104 | + [[nodiscard]] bool isTriggerPressed(int triggerIndex) const { |
| 105 | + return currentTriggersPressed[triggerIndex]; |
| 106 | + } |
| 107 | + [[nodiscard]] bool justPressedTrigger(int triggerIndex) const { |
| 108 | + return currentTriggersPressed[triggerIndex] && !previousTriggersPressed[triggerIndex]; |
| 109 | + } |
| 110 | + [[nodiscard]] bool justReleasedTrigger(int triggerIndex) const { |
| 111 | + return !currentTriggersPressed[triggerIndex] && previousTriggersPressed[triggerIndex]; |
| 112 | + } |
| 113 | + |
| 114 | + [[nodiscard]] glm::vec2 getLeftStick() const { |
| 115 | + return {getAxis(GLFW_GAMEPAD_AXIS_LEFT_X), getAxis(GLFW_GAMEPAD_AXIS_LEFT_Y)}; |
| 116 | + } |
| 117 | + [[nodiscard]] glm::vec2 getRightStick() const { |
| 118 | + return {getAxis(GLFW_GAMEPAD_AXIS_RIGHT_X), getAxis(GLFW_GAMEPAD_AXIS_RIGHT_Y)}; |
| 119 | + } |
| 120 | + [[nodiscard]] float getLeftTrigger() const { |
| 121 | + return (getAxis(GLFW_GAMEPAD_AXIS_LEFT_TRIGGER) + 1.0f) / 2.0f; |
| 122 | + } |
| 123 | + [[nodiscard]] float getRightTrigger() const { |
| 124 | + return (getAxis(GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER) + 1.0f) / 2.0f; |
| 125 | + } |
| 126 | + |
| 127 | + [[nodiscard]] bool isEnabled() const { |
| 128 | + return enabled; |
| 129 | + } |
| 130 | + void setEnabled(bool enabled) { |
| 131 | + if (this->enabled != enabled) { |
| 132 | + if (enabled) |
| 133 | + enable(); |
| 134 | + else |
| 135 | + disable(); |
| 136 | + } |
| 137 | + } |
| 138 | + }; |
| 139 | + |
| 140 | +} // namespace our |
0 commit comments