Skip to content

Instantly share code, notes, and snippets.

@jasonwhite
Last active April 11, 2024 15:45
Show Gist options
  • Star 85 You must be signed in to star a gist
  • Fork 14 You must be signed in to fork a gist
  • Save jasonwhite/c5b2048c15993d285130 to your computer and use it in GitHub Desktop.
Save jasonwhite/c5b2048c15993d285130 to your computer and use it in GitHub Desktop.
Reads joystick/gamepad events on Linux and displays them.
/**
* 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 <stdio.h>
#include <unistd.h>
#include <linux/joystick.h>
/**
* 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 axis_state {
short x, y;
};
/**
* 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;
}
return axis;
}
int main(int argc, char *argv[])
{
const char *device;
int js;
struct js_event event;
struct axis_state axes[3] = {0};
size_t axis;
if (argc > 1)
device = argv[1];
else
device = "/dev/input/js0";
js = open(device, O_RDONLY);
if (js == -1)
perror("Could not open joystick");
/* This loop will exit if the controller is unplugged. */
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;
default:
/* Ignore init events. */
break;
}
fflush(stdout);
}
close(js);
return 0;
}
@jasonwhite
Copy link
Author

@MDAR Very cool. In case you are wondering how to send rumble effects to the gamepad, it's done with https://www.kernel.org/doc/html/latest/input/ff.html

@MDAR
Copy link

MDAR commented May 20, 2022

@jasonwhite

In case you are wondering how to send rumble effects to the gamepad

I wasn't.... but I think I know a chap that might

@MDAR
Copy link

MDAR commented Aug 10, 2022

@jasonwhite

Hi Jason

I found an old set of PlayStation Buzz controllers in a box of bits, your code works with them too !!!

I can't think of a use for it right now, but I'm sure I will.

@MDAR
Copy link

MDAR commented May 23, 2023

@jasonwhite

This is a big ask and I will totally respect a "no, not interested" reply.

Would you be interested in exploring options to get your code working with a Contour MultiMedia Express mouse?

https://contour-design.co.uk/products/multimedia-controller-xpress

image

I was hoping it might resolve as a Joystick in Linux, but alas it loads as a mouse. ( /dev/input/mouse0 in Debian Bullseye on an Odroid C4 to be exact )

@jasonwhite
Copy link
Author

jasonwhite commented May 23, 2023

@MDAR If the Linux kernel doesn't see it as a joystick device, then code to support it probably doesn't belong in this gist. The main purpose of this gist was just to show how to read joystick events. There are plenty of existing examples that show how to read events from /dev/input/mouseX.

Also note that /dev/input/mouseX usually requires user to be root or in the input group to be able to read events from it. I believe things like games that need mouse input get it from X11 or Wayland rather than directly from the device.

@MDAR
Copy link

MDAR commented May 23, 2023

@jasonwhite

Excellent advice.

Thanks for that.

I'll go hunting 🔎

@e2dk4r
Copy link

e2dk4r commented Mar 28, 2024

For more modern version and not requiring you to be root for reading gamepad input in linux using evdev generic input event interface with liburing and libevdev libraries, go to my repo https://github.com/e2dk4r/gamepad

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment