Updating rcore_drm.c to only use one api for input

This commit is contained in:
MrMugame 2024-03-21 22:57:27 +01:00
parent b905dafca0
commit 1f50818430

View File

@ -71,27 +71,12 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number
#define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...)
#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Types and Structures Definition // Types and Structures Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
typedef struct {
pthread_t threadId; // Event reading thread id
int fd; // File descriptor to the device it is assigned to
int eventNum; // Number of 'event<N>' device
Rectangle absRange; // Range of values for absolute pointing devices (touchscreens)
int touchSlot; // Hold the touch slot number of the currently being sent multitouch block
bool isMouse; // True if device supports relative X Y movements
bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH
bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH
bool isKeyboard; // True if device has letter keycodes
bool isGamepad; // True if device has gamepad buttons
} InputEventWorker;
typedef struct { typedef struct {
// Display data // Display data
int fd; // File descriptor for /dev/dri/... int fd; // File descriptor for /dev/dri/...
@ -108,9 +93,6 @@ typedef struct {
EGLContext context; // Graphic context, mode in which drawing can be done EGLContext context; // Graphic context, mode in which drawing can be done
EGLConfig config; // Graphic config EGLConfig config; // Graphic config
// Input data
InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event<N>"
// Keyboard data // Keyboard data
int defaultKeyboardMode; // Default keyboard mode int defaultKeyboardMode; // Default keyboard mode
bool eventKeyboardMode; // Keyboard in event mode bool eventKeyboardMode; // Keyboard in event mode
@ -129,7 +111,9 @@ typedef struct {
// Gamepad data // Gamepad data
int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor
int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXIS][2]; // [0] = min, [1] = range value of the axis
int gamepadAbsAxisMap[MAX_GAMEPADS][ABS_CNT]; // Maps the axes gamepads from the evdev api to a sequential one
int gamepadCount; // The number of gamepads registered
} PlatformData; } PlatformData;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -145,6 +129,8 @@ static PlatformData platform = { 0 }; // Platform specific data
// Scancode to keycode mapping for US keyboards // Scancode to keycode mapping for US keyboards
// TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard: // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard:
// Currently non US keyboards will have the wrong mapping for some keys // Currently non US keyboards will have the wrong mapping for some keys
// NOTE: Replacing this with the keymap from X11 would probably be useless, as people use the drm
// backend to *avoid* X11
static const int keymapUS[] = { static const int keymapUS[] = {
0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84,
89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96, 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96,
@ -178,19 +164,17 @@ static const int EvkeyToUnicodeLUT[] = {
int InitPlatform(void); // Initialize platform (graphics, inputs and more) int InitPlatform(void); // Initialize platform (graphics, inputs and more)
void ClosePlatform(void); // Close platform void ClosePlatform(void); // Close platform
#if defined(SUPPORT_SSH_KEYBOARD_RPI)
static void InitKeyboard(void); // Initialize raw keyboard system static void InitKeyboard(void); // Initialize raw keyboard system
static void RestoreKeyboard(void); // Restore keyboard system static void RestoreKeyboard(void); // Restore keyboard system
#if defined(SUPPORT_SSH_KEYBOARD_RPI)
static void ProcessKeyboard(void); // Process keyboard events static void ProcessKeyboard(void); // Process keyboard events
#endif #endif
static void InitEvdevInput(void); // Initialize evdev inputs static void InitEvdevInput(void); // Initialize evdev inputs
static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate
static void PollKeyboardEvents(void); // Process evdev keyboard events static void PollKeyboardEvents(void); // Process evdev keyboard events
static void *EventThread(void *arg); // Input device events reading thread static void PollGamepadEvents(void); // Process evdev gamepad events
static void PollMouseEvents(void); // Process evdev mouse events
static void InitGamepad(void); // Initialize raw gamepad input
static void PollGamepadEvents(void); // Gamepad reading function
static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list
static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list
@ -600,172 +584,7 @@ void PollInputEvents(void)
// Handle the mouse/touch/gestures events: // Handle the mouse/touch/gestures events:
// NOTE: Replaces the EventThread handling that is now commented. // NOTE: Replaces the EventThread handling that is now commented.
{ PollMouseEvents();
int fd = platform.mouseFd;
if (fd == -1) return;
struct input_event event = { 0 };
int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
// Try to read data from the mouse/touch/gesture and only continue if successful
while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
{
// Relative movement parsing
if (event.type == EV_REL)
{
if (event.code == REL_X)
{
if (platform.cursorRelative)
{
CORE.Input.Mouse.currentPosition.x = event.value;
CORE.Input.Mouse.previousPosition.x = 0.0f;
}
else CORE.Input.Mouse.currentPosition.x += event.value;
CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
touchAction = 2; // TOUCH_ACTION_MOVE
}
if (event.code == REL_Y)
{
if (platform.cursorRelative)
{
CORE.Input.Mouse.currentPosition.y = event.value;
CORE.Input.Mouse.previousPosition.y = 0.0f;
}
else CORE.Input.Mouse.currentPosition.y += event.value;
CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
touchAction = 2; // TOUCH_ACTION_MOVE
}
if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value;
}
// Absolute movement parsing
if (event.type == EV_ABS)
{
// Basic movement
if (event.code == ABS_X)
{
CORE.Input.Mouse.currentPosition.x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange
CORE.Input.Touch.position[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange
touchAction = 2; // TOUCH_ACTION_MOVE
}
if (event.code == ABS_Y)
{
CORE.Input.Mouse.currentPosition.y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange
CORE.Input.Touch.position[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange
touchAction = 2; // TOUCH_ACTION_MOVE
}
// Multitouch movement
if (event.code == ABS_MT_SLOT) platform.touchSlot = event.value; // Remember the slot number for the folowing events
if (event.code == ABS_MT_POSITION_X)
{
if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange
}
if (event.code == ABS_MT_POSITION_Y)
{
if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange
}
if (event.code == ABS_MT_TRACKING_ID)
{
if ((event.value < 0) && (platform.touchSlot < MAX_TOUCH_POINTS))
{
// Touch has ended for this point
CORE.Input.Touch.position[platform.touchSlot].x = -1;
CORE.Input.Touch.position[platform.touchSlot].y = -1;
}
}
// Touchscreen tap
if (event.code == ABS_PRESSURE)
{
int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
if (!event.value && previousMouseLeftButtonState)
{
platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
touchAction = 0; // TOUCH_ACTION_UP
}
if (event.value && !previousMouseLeftButtonState)
{
platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
touchAction = 1; // TOUCH_ACTION_DOWN
}
}
}
// Button parsing
if (event.type == EV_KEY)
{
// Mouse button parsing
if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
{
platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN
else touchAction = 0; // TOUCH_ACTION_UP
}
if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
if (event.code == BTN_MIDDLE) platform.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value;
if (event.code == BTN_SIDE) platform.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value;
if (event.code == BTN_EXTRA) platform.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value;
if (event.code == BTN_FORWARD) platform.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value;
if (event.code == BTN_BACK) platform.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value;
}
// Screen confinement
if (!CORE.Input.Mouse.cursorHidden)
{
if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x;
if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y;
}
// Update touch point count
CORE.Input.Touch.pointCount = 0;
for (int i = 0; i < MAX_TOUCH_POINTS; i++)
{
if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++;
}
#if defined(SUPPORT_GESTURES_SYSTEM)
if (touchAction > -1)
{
GestureEvent gestureEvent = { 0 };
gestureEvent.touchAction = touchAction;
gestureEvent.pointCount = CORE.Input.Touch.pointCount;
for (int i = 0; i < MAX_TOUCH_POINTS; i++)
{
gestureEvent.pointId[i] = i;
gestureEvent.position[i] = CORE.Input.Touch.position[i];
}
ProcessGestureEvent(gestureEvent);
touchAction = -1;
}
#endif
}
}
} }
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -1123,8 +942,11 @@ int InitPlatform(void)
// Initialize input events system // Initialize input events system
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
InitEvdevInput(); // Evdev inputs initialization InitEvdevInput(); // Evdev inputs initialization
InitGamepad(); // Gamepad init
#if defined(SUPPORT_SSH_KEYBOARD_RPI)
InitKeyboard(); // Keyboard init (stdin) InitKeyboard(); // Keyboard init (stdin)
#endif
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Initialize storage system // Initialize storage system
@ -1203,27 +1025,29 @@ void ClosePlatform(void)
platform.device = EGL_NO_DISPLAY; platform.device = EGL_NO_DISPLAY;
} }
// Wait for mouse and gamepad threads to finish before closing
// NOTE: Those threads should already have finished at this point
// because they are controlled by CORE.Window.shouldClose variable
CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called
// Close the evdev keyboard // Close the evdev devices
if (platform.mouseFd != -1) {
close(platform.mouseFd);
platform.mouseFd = -1;
}
for (int i = 0; i < platform.gamepadCount; i++)
{
close(platform.gamepadStreamFd[i]);
platform.gamepadStreamFd[i] = -1;
}
if (platform.keyboardFd != -1) if (platform.keyboardFd != -1)
{ {
close(platform.keyboardFd); close(platform.keyboardFd);
platform.keyboardFd = -1; platform.keyboardFd = -1;
} }
}
for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) #if defined(SUPPORT_SSH_KEYBOARD_RPI)
{
if (platform.eventWorker[i].threadId)
{
pthread_join(platform.eventWorker[i].threadId, NULL);
}
}
}
// Initialize Keyboard system (using standard input) // Initialize Keyboard system (using standard input)
static void InitKeyboard(void) static void InitKeyboard(void)
@ -1282,7 +1106,6 @@ static void RestoreKeyboard(void)
ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode); ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode);
} }
#if defined(SUPPORT_SSH_KEYBOARD_RPI)
// Process keyboard inputs // Process keyboard inputs
static void ProcessKeyboard(void) static void ProcessKeyboard(void)
{ {
@ -1408,6 +1231,7 @@ static void InitEvdevInput(void)
// Initialise keyboard file descriptor // Initialise keyboard file descriptor
platform.keyboardFd = -1; platform.keyboardFd = -1;
platform.mouseFd = -1;
// Reset variables // Reset variables
for (int i = 0; i < MAX_TOUCH_POINTS; ++i) for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
@ -1453,62 +1277,18 @@ static void ConfigureEvdevDevice(char *device)
#define LONG(x) ((x)/BITS_PER_LONG) #define LONG(x) ((x)/BITS_PER_LONG)
#define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1) #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
struct input_absinfo absinfo = { 0 };
unsigned long evBits[NBITS(EV_MAX)] = { 0 }; unsigned long evBits[NBITS(EV_MAX)] = { 0 };
unsigned long absBits[NBITS(ABS_MAX)] = { 0 }; unsigned long absBits[NBITS(ABS_MAX)] = { 0 };
unsigned long relBits[NBITS(REL_MAX)] = { 0 }; unsigned long relBits[NBITS(REL_MAX)] = { 0 };
unsigned long keyBits[NBITS(KEY_MAX)] = { 0 }; unsigned long keyBits[NBITS(KEY_MAX)] = { 0 };
bool hasAbs = false;
bool hasRel = false;
bool hasAbsMulti = false;
int freeWorkerId = -1;
int fd = -1;
InputEventWorker *worker = NULL;
// Open the device and allocate worker
//-------------------------------------------------------------------------------------------------------
// Find a free spot in the workers array
for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
{
if (platform.eventWorker[i].threadId == 0)
{
freeWorkerId = i;
break;
}
}
// Select the free worker from array
if (freeWorkerId >= 0)
{
worker = &(platform.eventWorker[freeWorkerId]); // Grab a pointer to the worker
memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker
}
else
{
TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device);
return;
}
// Open the device // Open the device
fd = open(device, O_RDONLY | O_NONBLOCK); int fd = open(device, O_RDONLY | O_NONBLOCK);
if (fd < 0) if (fd < 0)
{ {
TRACELOG(LOG_WARNING, "RPI: Failed to open input device: %s", device); TRACELOG(LOG_WARNING, "Failed to open input device: %s", device);
return; return;
} }
worker->fd = fd;
// Grab number on the end of the devices name "event<N>"
int devNum = 0;
char *ptrDevName = strrchr(device, 't');
worker->eventNum = -1;
if (ptrDevName != NULL)
{
if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum;
}
else worker->eventNum = 0; // TODO: HACK: Grab number for mouse0 device!
// At this point we have a connection to the device, but we don't yet know what the device is. // At this point we have a connection to the device, but we don't yet know what the device is.
// It could be many things, even as simple as a power button... // It could be many things, even as simple as a power button...
@ -1516,149 +1296,166 @@ static void ConfigureEvdevDevice(char *device)
// Identify the device // Identify the device
//------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------
ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties struct input_absinfo absinfo[ABS_CNT] = { 0 };
// Check for absolute input devices // These flags aren't really a one of
if (TEST_BIT(evBits, EV_ABS)) // Some devices could have properties we assosciate with keyboards as well as properties
{ // we assosciate with mice
bool isKeyboard = false;
bool isMouse = false;
bool isTouch = false;
bool isGamepad = false;
int absAxisCount = 0;
ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits);
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
if (TEST_BIT(evBits, EV_ABS)) {
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
// Check for absolute movement support (usually touchscreens, but also joysticks) // If the device has an X an Y axis it's either a touch device, a special mouse or a gamepad
if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) bool hasAbsXY = TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y);
if (hasAbsXY)
{ {
hasAbs = true; absAxisCount += 2;
ioctl(fd, EVIOCGABS(ABS_X), &absinfo[ABS_X]);
// Get the scaling values ioctl(fd, EVIOCGABS(ABS_Y), &absinfo[ABS_Y]);
ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
worker->absRange.x = absinfo.minimum;
worker->absRange.width = absinfo.maximum - absinfo.minimum;
platform.absRange.x = absinfo.minimum;
platform.absRange.width = absinfo.maximum - absinfo.minimum;
ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
worker->absRange.y = absinfo.minimum;
worker->absRange.height = absinfo.maximum - absinfo.minimum;
platform.absRange.y = absinfo.minimum;
platform.absRange.height = absinfo.maximum - absinfo.minimum;
} }
// Check for multiple absolute movement support (usually multitouch touchscreens) // If it has any of these buttons it's a touch device
if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) if (hasAbsXY &&
(TEST_BIT(keyBits, BTN_STYLUS) ||
TEST_BIT(keyBits, BTN_TOOL_PEN) ||
TEST_BIT(keyBits, BTN_TOOL_FINGER) ||
TEST_BIT(keyBits, BTN_TOUCH))) isTouch = true;
// Absolute mice should really only exist with VMWare, but it shouldn't
// matter if we support them
else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
// If any of the common joystick axis is present, we assume it's a gamepad
else {
for (int axis = (hasAbsXY ? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++)
{ {
hasAbsMulti = true; if (TEST_BIT(absBits, axis)) {
isGamepad = true;
absAxisCount++;
// Get the scaling values ioctl(fd, EVIOCGABS(axis), &absinfo[axis]);
ioctl(fd, EVIOCGABS(ABS_X), &absinfo); }
worker->absRange.x = absinfo.minimum;
worker->absRange.width = absinfo.maximum - absinfo.minimum;
platform.absRange.x = absinfo.minimum;
platform.absRange.width = absinfo.maximum - absinfo.minimum;
ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
worker->absRange.y = absinfo.minimum;
worker->absRange.height = absinfo.maximum - absinfo.minimum;
platform.absRange.y = absinfo.minimum;
platform.absRange.height = absinfo.maximum - absinfo.minimum;
} }
} }
// Check for relative movement support (usually mouse) // If the device has multitouch axes, it's a touch device
if (TEST_BIT(absBits, ABS_MT_POSITION_X) &&
TEST_BIT(absBits, ABS_MT_POSITION_Y)) isTouch = true;
}
if (TEST_BIT(evBits, EV_REL)) if (TEST_BIT(evBits, EV_REL))
{ {
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; // If it has any of the gamepad or touch features we tested so far, it's not a mouse
if (!isTouch &&
!isGamepad &&
TEST_BIT(relBits, REL_X) &&
TEST_BIT(relBits, REL_Y) &&
TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
} }
// Check for button support to determine the device type(usually on all input devices)
if (TEST_BIT(evBits, EV_KEY)) if (TEST_BIT(evBits, EV_KEY))
{ {
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); // The first 32 keys as defined in input-event-codes.h are pretty much
// exclusive to keyboards, so we can test them using a mask
// Leave out the first bit to not test KEY_RESERVED
const unsigned long mask = 0xFFFFFFFE;
if ((keyBits[0] & mask) == mask) isKeyboard = true;
if (hasAbs || hasAbsMulti) // If we find any of the common gamepad buttons we assume it's a gamepad
else
{ {
if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen for (int button = BTN_JOYSTICK; button < BTN_DIGI; ++button)
if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet {
if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet if (TEST_BIT(keyBits, button)) isGamepad = true;
if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet
if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device
} }
if (hasRel) for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40; button++)
{ {
if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse if (TEST_BIT(keyBits, button)) isGamepad = true;
if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse }
}
} }
if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad const char *deviceKindStr = "unknown";
if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad if (isMouse || isTouch)
if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad
if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad
if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad
if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard
}
//-------------------------------------------------------------------------------------------------------
// Decide what to do with the device
//-------------------------------------------------------------------------------------------------------
if (worker->isKeyboard && (platform.keyboardFd == -1))
{ {
// Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a deviceKindStr = "mouse";
// keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate if (platform.mouseFd != -1) close(platform.mouseFd);
// threads so that they don't drop events when the frame rate is slow.
TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device);
platform.keyboardFd = worker->fd;
}
else if (worker->isTouch || worker->isMouse)
{
// Looks like an interesting device
TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device,
worker->isMouse? "mouse " : "",
worker->isMultitouch? "multitouch " : "",
worker->isTouch? "touchscreen " : "",
worker->isGamepad? "gamepad " : "");
platform.mouseFd = worker->fd;
// NOTE: moved the mouse/touch/gesture input to PollInputEvents()/ platform.mouseFd = fd;
// so added the "platform.mouseFd = worker->fd;" line above
// and commented the thread code below:
// Create a thread for this device
//int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker);
//if (error != 0)
//{
// TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error);
// worker->threadId = 0;
// close(fd);
//}
// TODO: What is this supposed to do?
// Previously if set, it just didn't do anything as the thread code got deleted, so no new
// threads were created and this deleted all old threads, thus doing nothing
// And the description isn't quite clear as to how it behaves together with a mouse
#if defined(USE_LAST_TOUCH_DEVICE) #if defined(USE_LAST_TOUCH_DEVICE)
// Find touchscreen with the highest index
int maxTouchNumber = -1;
for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
{
if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = platform.eventWorker[i].eventNum;
}
// Find touchscreens with lower indexes
for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
{
if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum < maxTouchNumber))
{
if (platform.eventWorker[i].threadId != 0)
{
TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i);
pthread_cancel(platform.eventWorker[i].threadId);
close(platform.eventWorker[i].fd);
}
}
}
#endif #endif
if (absAxisCount > 0)
{
platform.absRange.x = absinfo[ABS_X].minimum;
platform.absRange.width = absinfo[ABS_X].maximum - absinfo[ABS_X].minimum;
platform.absRange.y = absinfo[ABS_Y].minimum;
platform.absRange.height = absinfo[ABS_Y].maximum - absinfo[ABS_Y].minimum;
} }
else close(fd); // We are not interested in this device }
//------------------------------------------------------------------------------------------------------- else if (isGamepad && !isMouse && !isKeyboard && platform.gamepadCount < MAX_GAMEPADS)
{
deviceKindStr = "gamepad";
int index = platform.gamepadCount++;
platform.gamepadStreamFd[index] = fd;
CORE.Input.Gamepad.ready[index] = true;
ioctl(platform.gamepadStreamFd[index], EVIOCGNAME(64), &CORE.Input.Gamepad.name[index]);
CORE.Input.Gamepad.axisCount[index] = absAxisCount;
if (absAxisCount > 0)
{
// TODO / NOTE
// So gamepad axis (as in the actual linux joydev.c) are just simply enumerated
// and (at least for some input drivers like xpat) it's convention to use
// ABS_X, ABX_Y for one joystick ABS_RX, ABS_RY for the other and the Z axes for the
// shoulder buttons
// If these are now enumerated you get LJOY_X, LJOY_Y, LEFT_SHOULDERB, RJOY_X, ...
// That means they don't match the GamepadAxis enum
// This could be fixed
int axisIndex = 0;
for (int axis = ABS_X; axis < ABS_PRESSURE; axis++)
{
platform.gamepadAbsAxisRange[index][axisIndex][0] = absinfo[axisIndex].minimum;
platform.gamepadAbsAxisRange[index][axisIndex][1] = absinfo[axisIndex].maximum - absinfo[axisIndex].minimum;
platform.gamepadAbsAxisMap[index][axis] = axisIndex++;
}
}
}
else if (isKeyboard && (platform.keyboardFd == -1))
{
deviceKindStr = "keyboard";
platform.keyboardFd = fd;
}
else
{
close(fd);
return;
}
TRACELOG(LOG_INFO, "Initialized input device %s as %s", device, deviceKindStr);
} }
// Poll and process evdev keyboard events // Poll and process evdev keyboard events
@ -1722,27 +1519,70 @@ static void PollKeyboardEvents(void)
if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", (event.value == 0)? "UP" : "DOWN", event.code, keycode); TRACELOGD("KEY_%s ScanCode: %4i KeyCode: %4i", (event.value == 0)? "UP" : "DOWN", event.code, keycode);
} }
} }
} }
} }
} }
// Input device events reading thread static void PollGamepadEvents(void)
static void *EventThread(void *arg)
{ {
/* // Read gamepad event
struct input_event event = { 0 };
for (int i = 0; i < platform.gamepadCount; i++)
{
if (!CORE.Input.Gamepad.ready[i]) continue;
// Register previous gamepad states
for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
while (read(platform.gamepadStreamFd[i], &event, sizeof(event)) == (int)sizeof(event))
{
if (event.type == EV_KEY)
{
if (event.code >= MAX_GAMEPAD_BUTTONS) return;
TRACELOG(LOG_DEBUG, "Gamepad %i button: %i, value: %i", i, event.code, event.value);
// 1 - button pressed, 0 - button released
CORE.Input.Gamepad.currentButtonState[i][event.code] = event.value;
if (event.value == 1) CORE.Input.Gamepad.lastButtonPressed = event.code;
else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
}
else if (event.type == EV_ABS)
{
if (event.code >= MAX_GAMEPAD_AXIS) return;
TRACELOG(LOG_DEBUG, "Gamepad %i axis: %i, value: %i", i, platform.gamepadAbsAxisMap[i][event.code], event.value);
if (event.type == EV_REL) CORE.Input.Gamepad.axisState[i][event.code] += (float)event.value;
else
{
int min = platform.gamepadAbsAxisRange[i][event.code][0];
int range = platform.gamepadAbsAxisRange[i][event.code][1];
// NOTE: Scaling of event.value to get values between -1..1
CORE.Input.Gamepad.axisState[i][platform.gamepadAbsAxisMap[i][event.code]] = (2 * (float)(event.value - min) / range) - 1;
}
}
}
}
}
static void PollMouseEvents(void)
{
int fd = platform.mouseFd;
if (fd == -1) return;
struct input_event event = { 0 }; struct input_event event = { 0 };
InputEventWorker *worker = (InputEventWorker *)arg;
int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
bool gestureUpdate = false; // Flag to note gestures require to update
while (!CORE.Window.shouldClose) // Try to read data from the mouse/touch/gesture and only continue if successful
{ while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
// Try to read data from the device and only continue if successful
while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event))
{ {
// Relative movement parsing // Relative movement parsing
if (event.type == EV_REL) if (event.type == EV_REL)
@ -1751,34 +1591,26 @@ static void *EventThread(void *arg)
{ {
if (platform.cursorRelative) if (platform.cursorRelative)
{ {
CORE.Input.Mouse.currentPosition.x -= event.value; CORE.Input.Mouse.currentPosition.x = event.value;
CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; CORE.Input.Mouse.previousPosition.x = 0.0f;
} }
else else CORE.Input.Mouse.currentPosition.x += event.value;
{
CORE.Input.Mouse.currentPosition.x += event.value;
CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
}
touchAction = 2; // TOUCH_ACTION_MOVE touchAction = 2; // TOUCH_ACTION_MOVE
gestureUpdate = true;
} }
if (event.code == REL_Y) if (event.code == REL_Y)
{ {
if (platform.cursorRelative) if (platform.cursorRelative)
{ {
CORE.Input.Mouse.currentPosition.y -= event.value; CORE.Input.Mouse.currentPosition.y = event.value;
CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; CORE.Input.Mouse.previousPosition.y = 0.0f;
} }
else else CORE.Input.Mouse.currentPosition.y += event.value;
{
CORE.Input.Mouse.currentPosition.y += event.value;
CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
}
touchAction = 2; // TOUCH_ACTION_MOVE touchAction = 2; // TOUCH_ACTION_MOVE
gestureUpdate = true;
} }
if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value; if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value;
@ -1790,42 +1622,40 @@ static void *EventThread(void *arg)
// Basic movement // Basic movement
if (event.code == ABS_X) if (event.code == ABS_X)
{ {
CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange CORE.Input.Mouse.currentPosition.x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange
CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange CORE.Input.Touch.position[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange
touchAction = 2; // TOUCH_ACTION_MOVE touchAction = 2; // TOUCH_ACTION_MOVE
gestureUpdate = true;
} }
if (event.code == ABS_Y) if (event.code == ABS_Y)
{ {
CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange CORE.Input.Mouse.currentPosition.y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange
CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange CORE.Input.Touch.position[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange
touchAction = 2; // TOUCH_ACTION_MOVE touchAction = 2; // TOUCH_ACTION_MOVE
gestureUpdate = true;
} }
// Multitouch movement // Multitouch movement
if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remember the slot number for the folowing events if (event.code == ABS_MT_SLOT) platform.touchSlot = event.value; // Remember the slot number for the folowing events
if (event.code == ABS_MT_POSITION_X) if (event.code == ABS_MT_POSITION_X)
{ {
if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange
} }
if (event.code == ABS_MT_POSITION_Y) if (event.code == ABS_MT_POSITION_Y)
{ {
if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange
} }
if (event.code == ABS_MT_TRACKING_ID) if (event.code == ABS_MT_TRACKING_ID)
{ {
if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS)) if ((event.value < 0) && (platform.touchSlot < MAX_TOUCH_POINTS))
{ {
// Touch has ended for this point // Touch has ended for this point
CORE.Input.Touch.position[worker->touchSlot].x = -1; CORE.Input.Touch.position[platform.touchSlot].x = -1;
CORE.Input.Touch.position[worker->touchSlot].y = -1; CORE.Input.Touch.position[platform.touchSlot].y = -1;
} }
} }
@ -1839,7 +1669,6 @@ static void *EventThread(void *arg)
platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
touchAction = 0; // TOUCH_ACTION_UP touchAction = 0; // TOUCH_ACTION_UP
gestureUpdate = true;
} }
if (event.value && !previousMouseLeftButtonState) if (event.value && !previousMouseLeftButtonState)
@ -1847,7 +1676,6 @@ static void *EventThread(void *arg)
platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
touchAction = 1; // TOUCH_ACTION_DOWN touchAction = 1; // TOUCH_ACTION_DOWN
gestureUpdate = true;
} }
} }
@ -1863,7 +1691,6 @@ static void *EventThread(void *arg)
if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN
else touchAction = 0; // TOUCH_ACTION_UP else touchAction = 0; // TOUCH_ACTION_UP
gestureUpdate = true;
} }
if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
@ -1892,7 +1719,7 @@ static void *EventThread(void *arg)
} }
#if defined(SUPPORT_GESTURES_SYSTEM) #if defined(SUPPORT_GESTURES_SYSTEM)
if (gestureUpdate) if (touchAction > -1)
{ {
GestureEvent gestureEvent = { 0 }; GestureEvent gestureEvent = { 0 };
@ -1906,100 +1733,11 @@ static void *EventThread(void *arg)
} }
ProcessGestureEvent(gestureEvent); ProcessGestureEvent(gestureEvent);
touchAction = -1;
} }
#endif #endif
} }
WaitTime(0.005); // Sleep for 5ms to avoid hogging CPU time
}
close(worker->fd);
*/
return NULL;
}
// Initialize gamepad system
static void InitGamepad(void)
{
char gamepadDev[128] = { 0 };
for (int i = 0; i < MAX_GAMEPADS; i++)
{
sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i);
if ((platform.gamepadStreamFd[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0)
{
// NOTE: Only show message for first gamepad
if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available");
}
else
{
CORE.Input.Gamepad.ready[i] = true;
// NOTE: Only show message for first gamepad
if (i == 0) TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully");
ioctl(platform.gamepadStreamFd[i], JSIOCGNAME(64), &CORE.Input.Gamepad.name[i]);
ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount[i]);
}
}
}
// Process Gamepad (/dev/input/js0)
static void PollGamepadEvents(void)
{
#define JS_EVENT_BUTTON 0x01 // Button pressed/released
#define JS_EVENT_AXIS 0x02 // Joystick axis moved
#define JS_EVENT_INIT 0x80 // Initial state of device
struct js_event {
unsigned int time; // event timestamp in milliseconds
short value; // event value
unsigned char type; // event type
unsigned char number; // event axis/button number
};
// Read gamepad event
struct js_event gamepadEvent = { 0 };
for (int i = 0; i < MAX_GAMEPADS; i++)
{
if (CORE.Input.Gamepad.ready[i])
{
// Register previous gamepad states
for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
while (read(platform.gamepadStreamFd[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
{
gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events
// Process gamepad events by type
if (gamepadEvent.type == JS_EVENT_BUTTON)
{
TRACELOG(LOG_DEBUG, "RPI: Gamepad %i button: %i, value: %i", i, gamepadEvent.number, gamepadEvent.value);
if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
{
// 1 - button pressed, 0 - button released
CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value;
if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number;
else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
}
}
else if (gamepadEvent.type == JS_EVENT_AXIS)
{
TRACELOG(LOG_DEBUG, "RPI: Gamepad %i axis: %i, value: %i", i, gamepadEvent.number, gamepadEvent.value);
if (gamepadEvent.number < MAX_GAMEPAD_AXIS)
{
// NOTE: Scaling of gamepadEvent.value to get values between -1..1
CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768;
}
}
}
}
}
} }
// Search matching DRM mode in connector's mode list // Search matching DRM mode in connector's mode list