Kernel/LibInput: Rework Joystick handling
Joystick axis and buttons are now named to standard values, this allows interfacing multiple different controllers (only DS3 is supported) Add ioctl calls for userspace to set joystick player leds and rumble Only use DS3 code paths when we detect that the attached device is actually an DS3 controller update test-joystick program to the new interface and add support to control rumble and player leds
This commit is contained in:
@@ -54,9 +54,14 @@ struct winsize
|
||||
#define SND_GET_SAMPLE_RATE 61 /* stores sample rate to uint32_t argument */
|
||||
#define SND_RESET_BUFFER 62 /* stores the size of internal buffer to uint32_t argument and clears the buffer */
|
||||
#define SND_GET_BUFFERSZ 63 /* stores the size of internal buffer to uint32_t argument */
|
||||
#define SND_GET_TOTAL_PINS 64 /* gets the number of pins on the current device */
|
||||
#define SND_GET_PIN 65 /* gets the currently active pin */
|
||||
#define SND_SET_PIN 66 /* sets the currently active pin */
|
||||
#define SND_GET_TOTAL_PINS 64 /* gets the number of pins on the current device as uint32_t */
|
||||
#define SND_GET_PIN 65 /* gets the currently active pin as uint32_t */
|
||||
#define SND_SET_PIN 66 /* sets the currently active pin to uint32_t */
|
||||
|
||||
#define JOYSTICK_GET_LEDS 80 /* get controller led bitmap to uint8_t argument */
|
||||
#define JOYSTICK_SET_LEDS 81 /* set controller leds to uint8_t bitmap */
|
||||
#define JOYSTICK_GET_RUMBLE 82 /* get controller rumble strength to uint8_t argument */
|
||||
#define JOYSTICK_SET_RUMBLE 83 /* set controller rumble strength to uint8_t argument */
|
||||
|
||||
int ioctl(int, int, ...);
|
||||
|
||||
|
||||
@@ -5,7 +5,45 @@
|
||||
namespace LibInput
|
||||
{
|
||||
|
||||
// TODO: not used but here if we ever make controller
|
||||
enum JoystickButton
|
||||
{
|
||||
JSB_DPAD_UP,
|
||||
JSB_DPAD_DOWN,
|
||||
JSB_DPAD_LEFT,
|
||||
JSB_DPAD_RIGHT,
|
||||
|
||||
JSB_FACE_UP,
|
||||
JSB_FACE_DOWN,
|
||||
JSB_FACE_LEFT,
|
||||
JSB_FACE_RIGHT,
|
||||
|
||||
JSB_STICK_LEFT,
|
||||
JSB_STICK_RIGHT,
|
||||
|
||||
JSB_SHOULDER_LEFT,
|
||||
JSB_SHOULDER_RIGHT,
|
||||
|
||||
JSB_MENU,
|
||||
JSB_START,
|
||||
JSB_SELECT,
|
||||
|
||||
JSB_COUNT,
|
||||
};
|
||||
|
||||
enum JoystickAxis
|
||||
{
|
||||
JSA_STICK_LEFT_X,
|
||||
JSA_STICK_LEFT_Y,
|
||||
JSA_STICK_RIGHT_X,
|
||||
JSA_STICK_RIGHT_Y,
|
||||
|
||||
JSA_TRIGGER_LEFT,
|
||||
JSA_TRIGGER_RIGHT,
|
||||
|
||||
JSA_COUNT,
|
||||
};
|
||||
|
||||
// TODO: not used but exists if we ever make controller
|
||||
// support generating events instead of being polled
|
||||
struct JoystickEvent
|
||||
{
|
||||
@@ -14,15 +52,9 @@ namespace LibInput
|
||||
|
||||
struct JoystickState
|
||||
{
|
||||
struct Axis
|
||||
{
|
||||
int64_t value;
|
||||
int64_t min;
|
||||
int64_t max;
|
||||
};
|
||||
|
||||
Axis axis[4];
|
||||
bool buttons[32];
|
||||
// axis are mapped to range [-32767, +32767]
|
||||
int16_t axis[JSA_COUNT];
|
||||
bool buttons[JSB_COUNT];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/framebuffer.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <termios.h>
|
||||
|
||||
@@ -47,13 +48,6 @@ void cleanup()
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &original_termios);
|
||||
}
|
||||
|
||||
int map_joystick(const LibInput::JoystickState::Axis& axis, float min, float max)
|
||||
{
|
||||
if (axis.min == axis.max)
|
||||
return (min + max) / 2;
|
||||
return (axis.value - axis.min) * (max - min) / (axis.max - axis.min) + min;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* fb_path = "/dev/fb0";
|
||||
@@ -132,6 +126,12 @@ int main(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t led_bitmap { 0b0001 };
|
||||
ioctl(joystick_fd, JOYSTICK_SET_LEDS, &led_bitmap);
|
||||
bool old_lshoulder { false }, old_rshoulder { false };
|
||||
|
||||
uint8_t rumble_strength { 0x00 };
|
||||
|
||||
uint32_t color = 0xFF0000;
|
||||
int circle_x = fb_info.width / 2;
|
||||
int circle_y = fb_info.height / 2;
|
||||
@@ -142,9 +142,20 @@ int main(int argc, char** argv)
|
||||
draw_circle(circle_x, circle_y, radius, color);
|
||||
msync(fb_mmap, fb_bytes, MS_SYNC);
|
||||
|
||||
timespec last_led_update;
|
||||
clock_gettime(CLOCK_MONOTONIC, &last_led_update);
|
||||
|
||||
timespec prev_frame_ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &prev_frame_ts);
|
||||
|
||||
while (true)
|
||||
{
|
||||
LibInput::JoystickState state {};
|
||||
using namespace LibInput;
|
||||
|
||||
timespec current_ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, ¤t_ts);
|
||||
|
||||
JoystickState state {};
|
||||
if (read(joystick_fd, &state, sizeof(state)) == -1)
|
||||
{
|
||||
fprintf(stderr, "read: ");
|
||||
@@ -152,9 +163,9 @@ int main(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
const int dx = map_joystick(state.axis[0], -50, 50);
|
||||
const int dy = map_joystick(state.axis[1], -50, 50);
|
||||
const int dr = map_joystick(state.axis[3], 5, -5);
|
||||
const int dx = state.axis[JSA_STICK_LEFT_X] / 655;
|
||||
const int dy = state.axis[JSA_STICK_LEFT_Y] / 655;
|
||||
const int dr = state.axis[JSA_STICK_RIGHT_Y] / 6553;
|
||||
|
||||
draw_circle(circle_x, circle_y, radius, 0x000000);
|
||||
|
||||
@@ -164,21 +175,59 @@ int main(int argc, char** argv)
|
||||
circle_y = BAN::Math::clamp<int>(circle_y + dy, 0, fb_info.height);
|
||||
radius = BAN::Math::clamp<int>(radius + dr, 1, 100);
|
||||
|
||||
if (state.buttons[12])
|
||||
if (state.buttons[JSB_FACE_UP])
|
||||
color = 0xFF0000;
|
||||
if (state.buttons[13])
|
||||
if (state.buttons[JSB_FACE_DOWN])
|
||||
color = 0x00FF00;
|
||||
if (state.buttons[14])
|
||||
if (state.buttons[JSB_FACE_LEFT])
|
||||
color = 0x0000FF;
|
||||
if (state.buttons[15])
|
||||
if (state.buttons[JSB_FACE_RIGHT])
|
||||
color = 0xFFFFFF;
|
||||
|
||||
if ((state.buttons[JSB_SHOULDER_LEFT] && !old_lshoulder) != (state.buttons[JSB_SHOULDER_RIGHT] && !old_rshoulder))
|
||||
{
|
||||
if (state.buttons[JSB_SHOULDER_LEFT] && !old_lshoulder)
|
||||
led_bitmap = (led_bitmap - 1) & 0x0F;
|
||||
if (state.buttons[JSB_SHOULDER_RIGHT] && !old_rshoulder)
|
||||
led_bitmap = (led_bitmap + 1) & 0x0F;
|
||||
ioctl(joystick_fd, JOYSTICK_SET_LEDS, &led_bitmap);
|
||||
}
|
||||
old_lshoulder = state.buttons[JSB_SHOULDER_LEFT];
|
||||
old_rshoulder = state.buttons[JSB_SHOULDER_RIGHT];
|
||||
|
||||
if ((state.axis[JSA_TRIGGER_LEFT] > 0) != (state.axis[JSA_TRIGGER_RIGHT] > 0))
|
||||
{
|
||||
const auto old_rumble = rumble_strength;
|
||||
if (state.axis[JSA_TRIGGER_LEFT] > 0)
|
||||
rumble_strength = (rumble_strength <= 0x05) ? 0 : rumble_strength - 5;
|
||||
if (state.axis[JSA_TRIGGER_RIGHT] > 0)
|
||||
rumble_strength = (rumble_strength >= 0xFA) ? 0 : rumble_strength + 5;
|
||||
if (rumble_strength != old_rumble)
|
||||
ioctl(joystick_fd, JOYSTICK_SET_RUMBLE, &rumble_strength);
|
||||
}
|
||||
|
||||
draw_circle(circle_x, circle_y, radius, color);
|
||||
|
||||
msync(fb_mmap, fb_bytes, MS_SYNC);
|
||||
|
||||
usleep(16666);
|
||||
const uint64_t current_us =
|
||||
current_ts.tv_sec * 1'000'000 +
|
||||
current_ts.tv_nsec / 1000;
|
||||
const uint64_t last_frame_us =
|
||||
prev_frame_ts.tv_sec * 1'000'000 +
|
||||
prev_frame_ts.tv_nsec / 1000;
|
||||
|
||||
msync(fb_mmap, fb_bytes, MS_SYNC);
|
||||
const uint64_t wakeup_us = last_frame_us + 16'666;
|
||||
if (current_us < wakeup_us)
|
||||
{
|
||||
const uint32_t sleep_us = wakeup_us - current_us;
|
||||
const timespec sleep_ts {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = static_cast<long>(wakeup_us - current_us) * 1000,
|
||||
};
|
||||
nanosleep(&sleep_ts, nullptr);
|
||||
}
|
||||
|
||||
prev_frame_ts = current_ts;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user