318 lines
7.6 KiB
C++
318 lines
7.6 KiB
C++
/**
|
|
* Author: Jason White
|
|
*
|
|
* Description:
|
|
* Reads joystick/gamepad events and displays them.
|
|
*
|
|
* Compile:
|
|
* gcc joystick.c -o joystick
|
|
*
|
|
* Run:
|
|
* ./joystick [/dev/input/jsX]
|
|
*
|
|
* See also:
|
|
* https://www.kernel.org/doc/Documentation/input/joystick-api.txt
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <cstdio>
|
|
#include <unistd.h>
|
|
#include <linux/joystick.h>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include <format>
|
|
#include <jstick.hpp>
|
|
|
|
enum class XBoxButton : uint8_t
|
|
{
|
|
ButtonA = 0,
|
|
ButtonB = 1,
|
|
ButtonX = 2,
|
|
ButtonY = 3,
|
|
BumperL = 4,
|
|
BumperR = 5,
|
|
Back = 6,
|
|
Start = 7,
|
|
TheBigOne = 8,
|
|
ThumbL = 9,
|
|
ThumbR = 10,
|
|
};
|
|
|
|
/// 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)
|
|
{
|
|
ssize_t bytes;
|
|
|
|
bytes = read(fd, event, sizeof(*event));
|
|
|
|
if (bytes == sizeof(*event))
|
|
return 0;
|
|
|
|
/// Error, could not read full event.
|
|
return -1;
|
|
}
|
|
|
|
/// @returns the number of axes on the controller or 0 if an error occurs.
|
|
size_t get_axis_count(int fd)
|
|
{
|
|
__u8 axes;
|
|
|
|
if (ioctl(fd, JSIOCGAXES, &axes) == -1)
|
|
return 0;
|
|
|
|
return axes;
|
|
}
|
|
|
|
/// @returns the number of buttons on the controller or 0 if an error occurs.
|
|
size_t get_button_count(int fd)
|
|
{
|
|
__u8 buttons;
|
|
if (ioctl(fd, JSIOCGBUTTONS, &buttons) == -1)
|
|
return 0;
|
|
|
|
return buttons;
|
|
}
|
|
|
|
/// Current state of an axis.
|
|
|
|
|
|
struct Axes {
|
|
short LeftThumbX = 0;
|
|
short LeftThumbY = 0;
|
|
short RightThumbX = 0;
|
|
short RightThumbY = 0;
|
|
short DPadX = 0;
|
|
short DPadY = 0;
|
|
// TODO Left & Right triggers should start at -32767 because 0 would be pressed half way.
|
|
short LeftTrigger = 0;
|
|
short RightTrigger = 0;
|
|
};
|
|
|
|
|
|
/// 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 Axes& axes)
|
|
{
|
|
/// Mappings on my device, meaning these assumptions are incorrect.
|
|
// LStick X = 0
|
|
// LStick Y = 1
|
|
// LTrigger = 2
|
|
// RStick X = 3
|
|
// RStick Y = 4
|
|
// RTrigger = 5
|
|
// DirPad X = 6
|
|
// DirPad Y = 7
|
|
|
|
size_t axis = event->number;
|
|
short value = event->value;
|
|
|
|
// X-Box 360 & X-Box One are the same.
|
|
if (axis == 0) axes.LeftThumbX = value;
|
|
if (axis == 1) axes.LeftThumbY = value;
|
|
if (axis == 2) axes.LeftTrigger = value;
|
|
if (axis == 3) axes.RightThumbX = value;
|
|
if (axis == 4) axes.RightThumbY = value;
|
|
if (axis == 5) axes.RightTrigger = value;
|
|
if (axis == 6) axes.DPadX = value;
|
|
if (axis == 7) axes.DPadY = value;
|
|
|
|
// "Sony PLAYSTATION(R)3 Controller", Has 6 axes.
|
|
/*
|
|
// D-PAD UP -> Button 12
|
|
// D-Pad DOWN -> Button 13
|
|
// D-Pad LEFT -> Button 14
|
|
// D-Pad RIGHT -> Button 15
|
|
if (axis == 0) axes.LeftThumbX = value;
|
|
if (axis == 1) axes.LeftThumbY = value;
|
|
if (axis == 3) axes.RightThumbX = value;
|
|
if (axis == 4) axes.RightThumbY = value;
|
|
if (axis == 2) axes.LeftTrigger = value;
|
|
if (axis == 5) axes.RightTrigger = value;
|
|
*/
|
|
|
|
|
|
// Playstation 4
|
|
/*
|
|
if (axis == 0) axes.LeftThumbX = value;
|
|
if (axis == 1) axes.LeftThumbY = value;
|
|
if (axis == 3) axes.RightThumbX = value;
|
|
if (axis == 4) axes.RightThumbY = value;
|
|
if (axis == 2) axes.LeftTrigger = value;
|
|
if (axis == 5) axes.RightTrigger = value;
|
|
if (axis == 6) axes.DPadX = value;
|
|
if (axis == 7) axes.DPadY = value;
|
|
*/
|
|
|
|
// "8BitDo Pro 2" in Direct Input mode. This controller also supports the Xbox mode.
|
|
/*
|
|
if (axis == 0) axes.LeftThumbX = value;
|
|
if (axis == 1) axes.LeftThumbY = value;
|
|
if (axis == 2) axes.RightThumbX = value;
|
|
if (axis == 3) axes.RightThumbY = value;
|
|
if (axis == 4) axes.RightTrigger = value;
|
|
if (axis == 5) axes.LeftTrigger = value;
|
|
if (axis == 6) axes.DPadX = value;
|
|
if (axis == 7) axes.DPadY = value;
|
|
*/
|
|
|
|
|
|
std::cout << std::format("axis_index: {}, val: {}", axis, value) << std::endl;
|
|
std::cout << std::format("lthumb: {},{} ltrigger: {}, rthumb: {},{}, rtrigger: {}, dp: {},{}", axes.LeftThumbX, axes.LeftThumbY, axes.LeftTrigger, axes.RightThumbX, axes.RightThumbY, axes.RightTrigger, axes.DPadX, axes.DPadY) << std::endl;
|
|
|
|
/*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;
|
|
}
|
|
|
|
return axis;*/
|
|
return axis;
|
|
}
|
|
|
|
uint8_t get_num_axes(int joystick_handle) {
|
|
char num_axes;
|
|
ioctl(joystick_handle, JSIOCGAXES, &num_axes);
|
|
return num_axes;
|
|
}
|
|
|
|
std::string get_joystick_name(int joystick_handle)
|
|
{
|
|
char name_buf[128];
|
|
if (ioctl(joystick_handle, JSIOCGNAME(sizeof(name_buf)), name_buf) < 0)
|
|
strncpy(name_buf, "Unknown", sizeof(name_buf));
|
|
return std::string(name_buf);
|
|
}
|
|
|
|
int get_joystick_driver_ver(int joystick_handle)
|
|
{
|
|
int version;
|
|
ioctl(joystick_handle, JSIOCGVERSION, &version);
|
|
return version;
|
|
}
|
|
|
|
bool check_for_joystick_device()
|
|
{
|
|
return std::filesystem::exists("/dev/input/js0");
|
|
}
|
|
|
|
int js;
|
|
bool device_open;
|
|
|
|
bool connect_device() {
|
|
js = open("/dev/input/js0", O_NONBLOCK);
|
|
|
|
if (js == -1)
|
|
perror("Could not open joystick");
|
|
|
|
|
|
std::cout << get_joystick_name(js) << std::endl;
|
|
std::cout << get_joystick_driver_ver(js) << std::endl;
|
|
std::cout << get_axis_count(js) << std::endl;
|
|
}
|
|
|
|
void disconnect_device() {
|
|
close(js);
|
|
}
|
|
|
|
#define ENUM_TO_STRING(var) (#var)
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
std::cout << jstick::NumJoysticksDetected() << std::endl;
|
|
return 0;
|
|
|
|
/*
|
|
const char *device;
|
|
|
|
struct js_event event;
|
|
struct Axes axes = {};
|
|
size_t axis;
|
|
|
|
if (argc > 1)
|
|
device = argv[1];
|
|
else
|
|
device = "/dev/input/js0";
|
|
|
|
js = open(device, O_NONBLOCK);
|
|
|
|
if (js == -1)
|
|
perror("Could not open joystick");
|
|
else
|
|
{
|
|
std::cout << get_joystick_name(js) << std::endl;
|
|
std::cout << get_joystick_driver_ver(js) << std::endl;
|
|
std::cout << get_axis_count(js) << std::endl;
|
|
}
|
|
|
|
|
|
|
|
/// This loop will exit if the controller is unplugged.
|
|
|
|
bool had = false;
|
|
|
|
while (1)
|
|
{
|
|
bool has_file = check_for_joystick_device();
|
|
|
|
if (has_file && !had) {
|
|
if (js == -1)
|
|
{
|
|
std::cout << "Controller plugged in!" << std::endl;
|
|
js = open(device, O_NONBLOCK);
|
|
|
|
if (js == -1)
|
|
perror("Could not open joystick");
|
|
}
|
|
|
|
} else if (!has_file && had) {
|
|
std::cout << "Controller unplugged!" << std::endl;
|
|
if (js != -1) {
|
|
close(js);
|
|
js = -1;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
had = has_file;
|
|
|
|
|
|
while (read_event(js, &event) == 0)
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case JS_EVENT_BUTTON:
|
|
printf("Button %u %s\n", event.number, event.value ? "pressed" : "released");
|
|
break;
|
|
case JS_EVENT_AXIS:
|
|
axis = get_axis_state(&event, axes);
|
|
//if (axis < 3)
|
|
//printf("Axis %zu at (%6d, %6d)\n", axis, axes[axis].x, axes[axis].y);
|
|
break;
|
|
case JS_EVENT_INIT:
|
|
default:
|
|
/// Ignore init events.
|
|
break;
|
|
}
|
|
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
|
|
close(js);
|
|
return 0;
|
|
*/
|
|
}
|