[rcore][drm] Improved touch input handling and multitouch support, closes #4842 (#5447)

* Improved touch input handling and multitouch support in drm platform

* revert

* made some fixes for the touch issue in drm platform

* updated touch input handling by adding multitouch support

* improved how it handles the multitouch

* added cleanup

* Remove touch last update tracking to simplify touch input handling

* improved multitouch support by tracking touch positions and IDs for each slot

* Better touch input handling

* Increase maximum touch points from 8 to 10 and enhance touchscreen prioritization logic

* Refactor touch input handling to use slot index as ID for stability and simplify touch clearing logic

* Improve touch input handling by activating slot 0 based on mouse click or touch events

* touch event handling to use tracking ID for unique touch identification

* Add multitouch detection to PollMouseEvents for improved touch handling

* Fix conditional formatting in PollMouseEvents for clarity

* Refactor conditional statements in PollMouseEvents and InitPlatform for improved readability

* Fix formatting in PollMouseEvents for improved readability
This commit is contained in:
MULTi
2025-12-29 17:24:30 +05:30
committed by GitHub
parent 00f42e4199
commit 1c6f683161
2 changed files with 212 additions and 43 deletions

View File

@ -105,7 +105,7 @@
#define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad)
#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
#define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds
#define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
#define MAX_TOUCH_POINTS 10 // Maximum number of touch points supported
#define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
#define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue

View File

@ -135,8 +135,12 @@ typedef struct {
char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab
bool cursorRelative; // Relative cursor mode
int mouseFd; // File descriptor for the evdev mouse/touch/gestures
bool mouseIsTouch; // Check if the current mouse device is actually a touchscreen
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 touchActive[MAX_TOUCH_POINTS]; // Track which touch points are currently active
Vector2 touchPosition[MAX_TOUCH_POINTS]; // Track touch positions for each slot
int touchId[MAX_TOUCH_POINTS]; // Track touch IDs for each slot
// Gamepad data
int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor
@ -1115,9 +1119,6 @@ void PollInputEvents(void)
// Register previous touch states
for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
// Reset touch positions to invalid state
for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ -1, -1 };
// Map touch position to mouse position for convenience
// NOTE: For DRM touchscreen devices, this mapping is disabled to avoid false touch detection
// CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
@ -1565,7 +1566,11 @@ int InitPlatform(void)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
// If graphic device is no properly initialized, we end program
if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
if (!CORE.Window.ready)
{
TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device");
return -1;
}
else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
// Set some default window flags
@ -1883,8 +1888,15 @@ static void InitEvdevInput(void)
{
CORE.Input.Touch.position[i].x = -1;
CORE.Input.Touch.position[i].y = -1;
platform.touchActive[i] = false;
platform.touchPosition[i].x = -1;
platform.touchPosition[i].y = -1;
platform.touchId[i] = -1;
}
// Initialize touch slot
platform.touchSlot = 0;
// Reset keyboard key state
for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
{
@ -2047,9 +2059,32 @@ static void ConfigureEvdevDevice(char *device)
const char *deviceKindStr = "unknown";
if (isMouse || isTouch)
{
deviceKindStr = "mouse";
if (platform.mouseFd != -1) close(platform.mouseFd);
bool prioritize = false;
// Priority logic: Touchscreens override Mice.
// 1. No device set yet? Take it.
if (platform.mouseFd == -1) prioritize = true;
// 2. Current is Mouse, New is Touch? Upgrade to Touch.
else if (isTouch && !platform.mouseIsTouch) prioritize = true;
// 3. Current is Touch, New is Touch? Use the new one (Last one found wins, standard behavior).
else if (isTouch && platform.mouseIsTouch) prioritize = true;
// 4. Current is Mouse, New is Mouse? Use the new one.
else if (!isTouch && !platform.mouseIsTouch) prioritize = true;
// 5. Current is Touch, New is Mouse? IGNORE the mouse. Keep the touchscreen.
else prioritize = false;
if (prioritize)
{
deviceKindStr = isTouch ? "touchscreen" : "mouse";
if (platform.mouseFd != -1)
{
TRACELOG(LOG_INFO, "INPUT: Overwriting previous input device with new %s", deviceKindStr);
close(platform.mouseFd);
}
platform.mouseFd = fd;
platform.mouseIsTouch = isTouch;
if (absAxisCount > 0)
{
@ -2059,6 +2094,15 @@ static void ConfigureEvdevDevice(char *device)
platform.absRange.y = absinfo[ABS_Y].info.minimum;
platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum;
}
TRACELOG(LOG_INFO, "INPUT: Initialized input device %s as %s", device, deviceKindStr);
}
else
{
TRACELOG(LOG_INFO, "INPUT: Ignoring device %s (keeping higher priority %s device)", device, platform.mouseIsTouch ? "touchscreen" : "mouse");
close(fd);
return;
}
}
else if (isGamepad && !isMouse && !isKeyboard && (platform.gamepadCount < MAX_GAMEPADS))
{
@ -2231,6 +2275,7 @@ static void PollMouseEvents(void)
struct input_event event = { 0 };
int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
static bool isMultitouch = false; // Detect if device supports MT events
// Try to read data from the mouse/touch/gesture and only continue if successful
while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
@ -2276,54 +2321,118 @@ static void PollMouseEvents(void)
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
// Update single touch position only if it's active and no MT events are being used
if ((platform.touchActive[0]) && (!isMultitouch))
{
platform.touchPosition[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;
if (touchAction == -1) 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
// Update single touch position only if it's active and no MT events are being used
if ((platform.touchActive[0]) && (!isMultitouch))
{
platform.touchPosition[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;
if (touchAction == -1) 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 ((event.code) == (ABS_MT_SLOT))
{
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
platform.touchSlot = event.value;
isMultitouch = true;
}
if (event.code == ABS_MT_POSITION_Y)
if ((event.code) == (ABS_MT_POSITION_X))
{
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
isMultitouch = true;
if ((platform.touchSlot) < (MAX_TOUCH_POINTS))
{
platform.touchPosition[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;
// If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active.
// Only set to MOVE if we haven't already detected a DOWN or UP event this frame
if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE
}
}
if (event.code == ABS_MT_TRACKING_ID)
if ((event.code) == (ABS_MT_POSITION_Y))
{
if ((event.value < 0) && (platform.touchSlot < MAX_TOUCH_POINTS))
if ((platform.touchSlot) < (MAX_TOUCH_POINTS))
{
platform.touchPosition[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;
// If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active.
// Only set to MOVE if we haven't already detected a DOWN or UP event this frame
if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE
}
}
if ((event.code) == (ABS_MT_TRACKING_ID))
{
if ((platform.touchSlot) < (MAX_TOUCH_POINTS))
{
if (event.value >= 0)
{
platform.touchActive[platform.touchSlot] = true;
platform.touchId[platform.touchSlot] = event.value; // Use Tracking ID for unique IDs
touchAction = 1; // TOUCH_ACTION_DOWN
}
else
{
// Touch has ended for this point
CORE.Input.Touch.position[platform.touchSlot].x = -1;
CORE.Input.Touch.position[platform.touchSlot].y = -1;
platform.touchActive[platform.touchSlot] = false;
platform.touchPosition[platform.touchSlot].x = -1;
platform.touchPosition[platform.touchSlot].y = -1;
platform.touchId[platform.touchSlot] = -1;
// Force UP action if we haven't already set a DOWN action
// (DOWN takes priority over UP if both happen in one frame, though rare)
if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP
}
}
}
// Handle ABS_MT_PRESSURE (0x3a) if available, as some devices use it for lift-off
#ifndef ABS_MT_PRESSURE
#define ABS_MT_PRESSURE 0x3a
#endif
if ((event.code) == (ABS_MT_PRESSURE))
{
if ((platform.touchSlot) < (MAX_TOUCH_POINTS))
{
if (event.value <= 0) // Pressure 0 means lift
{
platform.touchActive[platform.touchSlot] = false;
platform.touchPosition[platform.touchSlot].x = -1;
platform.touchPosition[platform.touchSlot].y = -1;
platform.touchId[platform.touchSlot] = -1;
if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP
}
}
}
// Touchscreen tap
if (event.code == ABS_PRESSURE)
if ((event.code) == (ABS_PRESSURE))
{
int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
if (!event.value && previousMouseLeftButtonState)
if ((!event.value) && (previousMouseLeftButtonState))
{
platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
touchAction = 0; // TOUCH_ACTION_UP
if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP
}
if (event.value && !previousMouseLeftButtonState)
if ((event.value) && (!previousMouseLeftButtonState))
{
platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
touchAction = 1; // TOUCH_ACTION_DOWN
@ -2340,8 +2449,46 @@ static void PollMouseEvents(void)
{
platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN
else touchAction = 0; // TOUCH_ACTION_UP
if (event.value > 0)
{
bool activateSlot0 = false;
if (event.code == BTN_LEFT)
{
activateSlot0 = true; // Mouse click always activates
}
else if (event.code == BTN_TOUCH)
{
bool anyActive = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (platform.touchActive[i]) { anyActive = true; break; }
}
if (!anyActive) activateSlot0 = true;
}
if (activateSlot0)
{
platform.touchActive[0] = true;
platform.touchId[0] = 0;
}
touchAction = 1; // TOUCH_ACTION_DOWN
}
else
{
// Only clear touch 0 for actual mouse clicks (BTN_LEFT)
if (event.code == BTN_LEFT)
{
platform.touchActive[0] = false;
platform.touchPosition[0].x = -1;
platform.touchPosition[0].y = -1;
}
else if (event.code == BTN_TOUCH)
{
platform.touchSlot = 0; // Reset slot index to 0
}
touchAction = 0; // TOUCH_ACTION_UP
}
}
if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
@ -2362,11 +2509,33 @@ static void PollMouseEvents(void)
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;
// Repack active touches into CORE.Input.Touch
int k = 0;
for (int i = 0; i < MAX_TOUCH_POINTS; i++)
{
if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++;
if (platform.touchActive[i])
{
CORE.Input.Touch.position[k] = platform.touchPosition[i];
CORE.Input.Touch.pointId[k] = platform.touchId[i];
k++;
}
}
CORE.Input.Touch.pointCount = k;
// Clear remaining slots
for (int i = k; i < MAX_TOUCH_POINTS; i++)
{
CORE.Input.Touch.position[i].x = -1;
CORE.Input.Touch.position[i].y = -1;
CORE.Input.Touch.pointId[i] = -1;
}
// Debug logging
static int lastTouchCount = 0;
if (CORE.Input.Touch.pointCount != lastTouchCount && (touchAction == 0 || touchAction == 1))
{
TRACELOG(LOG_DEBUG, "TOUCH: Count changed from %d to %d (action: %d)", lastTouchCount, CORE.Input.Touch.pointCount, touchAction);
lastTouchCount = CORE.Input.Touch.pointCount;
}
#if defined(SUPPORT_GESTURES_SYSTEM)