This commit is contained in:
2025-03-17 19:02:46 -05:00
parent 88e75135bb
commit c4c7b99f4c
5 changed files with 267 additions and 47 deletions

View File

@@ -3,18 +3,33 @@
#include <linux/joystick.h>
#include <cstddef>
#include <cstdio>
#include <fcntl.h>
#include <format>
#include <filesystem>
#include <iostream>
#include <unistd.h>
#include <bits/fs_fwd.h>
int js_handle;
bool js_connected;
#include <J3ML/LinearAlgebra/Vector2i.hpp>
struct axis_state {
short x, y;
};
int js_handle;
bool js_connected;
struct js_event event;
struct axis_state axes[3] = {0};
bool btn_state[10] = {false};
size_t axis;
Vector2 l_thumb;
Vector2 r_thumb;
short l_trigger;
short r_trigger;
Vector2 dpad;
/// Reads a joystick event from the joystick device.
/// @returns 0 on success. Otherwise -1 is returned.
int read_event(int fd, struct js_event *event) {
@@ -29,9 +44,12 @@ int read_event(int fd, struct js_event *event) {
return -1;
}
std::string js_device_file(int hwid) {
return std::format("/dev/input/js{}", hwid);
}
bool jstick::JoystickDetected(int jsHandle) {
return std::filesystem::exists(std::format("/dev/input/js{}", jsHandle));
return std::filesystem::exists(js_device_file(jsHandle));
}
int jstick::NumJoysticksDetected() {
@@ -43,57 +61,104 @@ int jstick::NumJoysticksDetected() {
return max;
}
bool jstick::Connected(int hwid) {
return js_connected && js_handle == hwid;
}
bool jstick::Connect(int hwid) {
if (js_connected) {
std::cerr << "Joystick already connected! Support for multiple joysticks is in-progress!" << std::endl;
return false;
}
js_handle = open(js_device_file(hwid).c_str(), O_NONBLOCK);
if (js_handle == -1) {
perror("Could not open joystick");
return false;
}
js_connected = true;
return true;
}
bool jstick::Disconnect(int hwid) {
if (!js_connected || js_handle == -1) {
std::cerr << "Joystick is already disconnected! Support for multiple joysticks is in-progrss!" << std::endl;
return false;
}
close(js_handle);
js_connected = false;
return true;
}
void jstick::JoystickServiceUpdate() {
int device_count = NumJoysticksDetected();
bool has = JoystickDetected();
if (has && !js_connected)
Connect();
}
void ProcessButtonEvent(uint8_t button_index, bool value) {
}
jstick::XBoxButton btn = (jstick::XBoxButton)button_index;
void ProcessAxisEvent(uint8_t axis, short x, short y) {
btn_state[button_index] = value;
}
if (value) {
jstick::ButtonPressed.Invoke(btn);
/// Keeps track of the current axis state.
/// @note This function assumes that axes are numbered starting from 0, and that
/// the X axis is an even number, and the Y axis is an odd number. However, this
/// is usually a safe assumption.
/// @returns the axis that the event indicated.
size_t get_axis_state(struct js_event *event, struct axis_state axes[3])
{
size_t axis = event->number / 2;
if (axis < 3)
{
if (event->number % 2 == 0)
axes[axis].x = event->value;
else
axes[axis].y = event->value;
} else {
jstick::ButtonReleased.Invoke(btn);
}
return axis;
}
struct js_event event;
struct axis_state axes[3] = {0};
size_t axis;
void ProcessAxisEvent(uint8_t axis, short value) {
// TODO: Handle all single-axis changes, and then invoke the axis event with the new vectors.
Vector2 prev_lthumb = l_thumb;
Vector2 prev_rthumb = r_thumb;
Vector2 prev_dp = dpad;
short prev_ltrigger = l_trigger;
short prev_rtrigger = r_trigger;
if (axis == 0) l_thumb.x = value;
if (axis == 1) l_thumb.y = value;
if (axis == 2) l_trigger = value;
if (axis == 3) r_thumb.x = value;
if (axis == 4) r_thumb.y = value;
if (axis == 5) r_trigger = value;
if (axis == 6) dpad.x = value;
if (axis == 7) dpad.y = value;
if (l_thumb.DistanceSq(prev_lthumb) > 1.f)
jstick::LeftThumbstickMoved.Invoke(l_thumb);
if (r_thumb.DistanceSq(prev_rthumb) > 1.f)
jstick::RightThumbstickMoved.Invoke(r_thumb);
if (dpad.DistanceSq(prev_dp) > 1.f)
jstick::DPadMoved.Invoke(dpad);
if (Math::Abs(l_trigger) - Math::Abs(prev_ltrigger) > 1.f)
jstick::LeftTriggerMoved.Invoke(l_trigger);
if (Math::Abs(r_trigger) - Math::Abs(prev_rtrigger) > 1.f)
jstick::RightTriggerMoved.Invoke(r_trigger);
}
void jstick::ReadEventLoop() {
while (read_event(js_handle, &event) == 0) {
switch (event.type) {
case JS_EVENT_BUTTON:
ProcessButtonEvent(event.number, event.value ? true : false);
break;
case JS_EVENT_AXIS:
axis = get_axis_state(&event, axes);
if (axis < 3)
ProcessAxisEvent(axis, axes[axis].x, axes[axis].y);
ProcessAxisEvent(event.number, event.value);
break;
case JS_EVENT_INIT:
default:
@@ -104,3 +169,36 @@ void jstick::ReadEventLoop() {
}
}
bool jstick::IsButtonDown(const XBoxButton &btn) {
return btn_state[(uint8_t)btn];
}
Vector2 jstick::GetLeftThumbstickAxis(float deadzone) { return l_thumb;}
Vector2 jstick::GetRightThumbstickAxis(float deadzone) { return r_thumb; }
Vector2 jstick::GetDPadAxis(float deadzone) { return dpad; }
short jstick::GetRightTrigger() { return r_trigger;}
short jstick::GetLeftTrigger() { return l_trigger; }
constexpr float short_range = 32768.f;
Vector2 jstick::GetLeftThumbstickAxisNormalized(float deadzone) {
return l_thumb / short_range;
}
Vector2 jstick::GetRightThumbstickAxisNormalized(float deadzone) {
return r_thumb / short_range;
}
Vector2 jstick::GetDPadAxisNormalized(float deadzone) {
return dpad / short_range;
}
float jstick::GetLeftTriggerNormalized() {
return l_trigger / short_range;
}
float jstick::GetRightTriggerNormalized() {
return r_trigger / short_range;
}