فهرست منبع

Organizing the drm backend to only use one api to allow for more devices (#3879)

* Updating rcore_drm.c to only use one api for input

* Change RPI log prefix to DRM

* Remove relative checking which is not supported currently

* Loop should continue on invalid event in drm backend

* Fixed and cleaned up PollKeyboardEvents() in drm backend
MrMugame 1 سال پیش
والد
کامیت
30781c423b
1فایلهای تغییر یافته به همراه351 افزوده شده و 628 حذف شده
  1. 351 628
      src/platforms/rcore_drm.c

+ 351 - 628
src/platforms/rcore_drm.c

@@ -57,6 +57,12 @@
 #include <sys/ioctl.h>      // Required for: ioctl() - UNIX System call for device-specific input/output operations
 #include <linux/kd.h>       // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
 #include <linux/input.h>    // Linux: Keycodes constants definition (KEY_A, ...)
+
+// So both `linux/input.h` and `raylib.h` define KEY_F12
+// To avoid conflict with the capturing code in rcore.c we undefine the macro KEY_F12,
+// so the enum KEY_F12 from raylib is used
+#undef KEY_F12
+
 #include <linux/joystick.h> // Linux: Joystick support library
 
 #include <gbm.h>         // Generic Buffer Management (native platform for EGL on DRM)
@@ -71,27 +77,12 @@
 //----------------------------------------------------------------------------------
 #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
 
 //----------------------------------------------------------------------------------
 // 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 {
     // Display data
     int fd;                             // File descriptor for /dev/dri/...
@@ -108,9 +99,6 @@ typedef struct {
     EGLContext context;                 // Graphic context, mode in which drawing can be done
     EGLConfig config;                   // Graphic config
 
-    // Input data
-    InputEventWorker eventWorker[10];   // List of worker threads for every monitored "/dev/input/event<N>"
-
     // Keyboard data
     int defaultKeyboardMode;            // Default keyboard mode
     bool eventKeyboardMode;             // Keyboard in event mode
@@ -129,7 +117,9 @@ typedef struct {
 
     // Gamepad data
     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;
 
 //----------------------------------------------------------------------------------
@@ -145,6 +135,8 @@ static PlatformData platform = { 0 };   // Platform specific data
 // 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:
 // 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[] = {
     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,
@@ -178,19 +170,17 @@ static const int EvkeyToUnicodeLUT[] = {
 int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
 void ClosePlatform(void);        // Close platform
 
+#if defined(SUPPORT_SSH_KEYBOARD_RPI)
 static void InitKeyboard(void);                 // Initialize raw keyboard system
 static void RestoreKeyboard(void);              // Restore keyboard system
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
 static void ProcessKeyboard(void);              // Process keyboard events
 #endif
 
 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 PollKeyboardEvents(void);           // Process evdev keyboard events
-static void *EventThread(void *arg);            // Input device events reading thread
-
-static void InitGamepad(void);                  // Initialize raw gamepad input
-static void PollGamepadEvents(void);            // Gamepad reading function
+static void PollGamepadEvents(void);            // Process evdev gamepad events
+static void PollMouseEvents(void);              // Process evdev mouse events
 
 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
@@ -564,6 +554,16 @@ void PollInputEvents(void)
 
     PollKeyboardEvents();
 
+    #if defined(SUPPORT_SSH_KEYBOARD_RPI)
+        // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
+        // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
+
+        if (!platform.eventKeyboardMode) ProcessKeyboard();
+    #endif
+
+    // Check exit key
+    if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
+
     // Register previous mouse position
     if (platform.cursorRelative) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
     else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
@@ -591,181 +591,9 @@ void PollInputEvents(void)
     // Map touch position to mouse position for convenience
     CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
 
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
-    // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
-    // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
-
-    if (!platform.eventKeyboardMode) ProcessKeyboard();
-#endif
-
     // Handle the mouse/touch/gestures events:
     // NOTE: Replaces the EventThread handling that is now commented.
-    {
-        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
-        }
-    }
+    PollMouseEvents();
 }
 
 //----------------------------------------------------------------------------------
@@ -1123,8 +951,11 @@ int InitPlatform(void)
     // Initialize input events system
     //----------------------------------------------------------------------------
     InitEvdevInput();   // Evdev inputs initialization
-    InitGamepad();      // Gamepad init
-    InitKeyboard();     // Keyboard init (stdin)
+
+    #if defined(SUPPORT_SSH_KEYBOARD_RPI)
+        InitKeyboard();     // Keyboard init (stdin)
+    #endif
+
     //----------------------------------------------------------------------------
 
     // Initialize storage system
@@ -1203,28 +1034,30 @@ void ClosePlatform(void)
         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
 
-    // Close the evdev keyboard
-    if (platform.keyboardFd != -1)
+    // Close the evdev devices
+
+    if (platform.mouseFd != -1) {
+        close(platform.mouseFd);
+        platform.mouseFd = -1;
+    }
+
+    for (int i = 0; i < platform.gamepadCount; i++)
     {
-        close(platform.keyboardFd);
-        platform.keyboardFd = -1;
+        close(platform.gamepadStreamFd[i]);
+        platform.gamepadStreamFd[i] = -1;
     }
 
-    for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
+    if (platform.keyboardFd != -1)
     {
-        if (platform.eventWorker[i].threadId)
-        {
-            pthread_join(platform.eventWorker[i].threadId, NULL);
-        }
+        close(platform.keyboardFd);
+        platform.keyboardFd = -1;
     }
 }
 
+#if defined(SUPPORT_SSH_KEYBOARD_RPI)
+
 // Initialize Keyboard system (using standard input)
 static void InitKeyboard(void)
 {
@@ -1256,7 +1089,7 @@ static void InitKeyboard(void)
     int result = ioctl(STDIN_FILENO, KDGKBMODE, &platform.defaultKeyboardMode);
 
     // In case of failure, it could mean a remote keyboard is used (SSH)
-    if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used");
+    if (result < 0) TRACELOG(LOG_WARNING, "DRM: Failed to change keyboard mode, an SSH keyboard is probably used");
     else
     {
         // Reconfigure keyboard mode to get:
@@ -1282,7 +1115,6 @@ static void RestoreKeyboard(void)
     ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode);
 }
 
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
 // Process keyboard inputs
 static void ProcessKeyboard(void)
 {
@@ -1383,18 +1215,6 @@ static void ProcessKeyboard(void)
             CORE.Input.Keyboard.keyPressedQueueCount++;
         }
     }
-
-    // Check exit key (same functionality as GLFW3 KeyCallback())
-    if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
-
-#if defined(SUPPORT_SCREEN_CAPTURE)
-    // Check screen capture key (raylib key: KEY_F12)
-    if (CORE.Input.Keyboard.currentKeyState[301] == 1)
-    {
-        TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
-        screenshotCounter++;
-    }
-#endif
 }
 #endif  // SUPPORT_SSH_KEYBOARD_RPI
 
@@ -1408,6 +1228,7 @@ static void InitEvdevInput(void)
 
     // Initialise keyboard file descriptor
     platform.keyboardFd = -1;
+    platform.mouseFd = -1;
 
     // Reset variables
     for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
@@ -1440,7 +1261,7 @@ static void InitEvdevInput(void)
 
         closedir(directory);
     }
-    else TRACELOG(LOG_WARNING, "RPI: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH);
+    else TRACELOG(LOG_WARNING, "DRM: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH);
 }
 
 // Identifies a input device and configures it for use if appropriate
@@ -1453,62 +1274,18 @@ static void ConfigureEvdevDevice(char *device)
     #define LONG(x)         ((x)/BITS_PER_LONG)
     #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 absBits[NBITS(ABS_MAX)] = { 0 };
     unsigned long relBits[NBITS(REL_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
-    fd = open(device, O_RDONLY | O_NONBLOCK);
+    int fd = open(device, O_RDONLY | O_NONBLOCK);
     if (fd < 0)
     {
-        TRACELOG(LOG_WARNING, "RPI: Failed to open input device: %s", device);
+        TRACELOG(LOG_WARNING, "DRM: Failed to open input device: %s", device);
         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.
     // It could be many things, even as simple as a power button...
@@ -1516,149 +1293,166 @@ static void ConfigureEvdevDevice(char *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
-    if (TEST_BIT(evBits, EV_ABS))
-    {
+    // These flags aren't really a one of
+    // 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);
 
-        // Check for absolute movement support (usually touchscreens, but also joysticks)
-        if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y))
+        // If the device has an X an Y axis it's either a touch device, a special mouse or a gamepad
+        bool hasAbsXY = TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y);
+
+        if (hasAbsXY)
         {
-            hasAbs = true;
-
-            // Get the scaling values
-            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;
+            absAxisCount += 2;
+            ioctl(fd, EVIOCGABS(ABS_X), &absinfo[ABS_X]);
+            ioctl(fd, EVIOCGABS(ABS_Y), &absinfo[ABS_Y]);
         }
 
-        // Check for multiple absolute movement support (usually multitouch touchscreens)
-        if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y))
-        {
-            hasAbsMulti = true;
-
-            // Get the scaling values
-            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;
+        // If it has any of these buttons it's a touch device
+        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++)
+            {
+                if (TEST_BIT(absBits, axis)) {
+                    isGamepad = true;
+                    absAxisCount++;
+
+                    ioctl(fd, EVIOCGABS(axis), &absinfo[axis]);
+                }
+            }
         }
+
+        // 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;
     }
 
-    // Check for relative movement support (usually mouse)
     if (TEST_BIT(evBits, EV_REL))
     {
         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))
     {
-        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
-            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, BTN_STYLUS)) worker->isTouch = true;         // This is a drawing tablet
-            if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true;   // This is a multitouch capable device
-        }
+            for (int button = BTN_JOYSTICK; button < BTN_DIGI; ++button)
+            {
+                if (TEST_BIT(keyBits, button)) isGamepad = true;
+            }
 
-        if (hasRel)
-        {
-            if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true;           // This is a mouse
-            if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true;          // This is a mouse
+            for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40; button++)
+            {
+                if (TEST_BIT(keyBits, button)) isGamepad = true;
+            }
         }
-
-        if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true;                // This is a gamepad
-        if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true;          // This is a gamepad
-        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
-        // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate
-        // 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)
+    const char *deviceKindStr = "unknown";
+    if (isMouse || isTouch)
     {
-        // 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()/
-        //       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);
-        //}
+        deviceKindStr = "mouse";
+        if (platform.mouseFd != -1) close(platform.mouseFd);
 
+        platform.mouseFd = 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)
-        // Find touchscreen with the highest index
-        int maxTouchNumber = -1;
+#endif
 
-        for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
+        if (absAxisCount > 0)
         {
-            if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = platform.eventWorker[i].eventNum;
+            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 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;
 
-        // Find touchscreens with lower indexes
-        for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
+        if (absAxisCount > 0)
         {
-            if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum < maxTouchNumber))
+            // 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++)
             {
-                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);
-                }
+                platform.gamepadAbsAxisRange[index][axisIndex][0] = absinfo[axisIndex].minimum;
+                platform.gamepadAbsAxisRange[index][axisIndex][1] = absinfo[axisIndex].maximum - absinfo[axisIndex].minimum;
+
+                platform.gamepadAbsAxisMap[index][axis] = axisIndex++;
             }
         }
-#endif
     }
-    else close(fd);  // We are not interested in this device
-    //-------------------------------------------------------------------------------------------------------
+    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
@@ -1673,332 +1467,261 @@ static void PollKeyboardEvents(void)
     // Try to read data from the keyboard and only continue if successful
     while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
     {
-        // Button parsing
-        if (event.type == EV_KEY)
-        {
+        // Check if the event is a key event
+        if (event.type != EV_KEY) continue;
+
 #if defined(SUPPORT_SSH_KEYBOARD_RPI)
-            // Change keyboard mode to events
-            platform.eventKeyboardMode = true;
+        // If the event was a key, we know a working keyboard is connected, so disable the SSH keyboard
+        platform.eventKeyboardMode = true;
 #endif
-            // Keyboard button parsing
-            if ((event.code >= 1) && (event.code <= 255))     //Keyboard keys appear for codes 1 to 255
+
+        // Keyboard keys appear for codes 1 to 255, ignore everthing else
+        if ((event.code >= 1) && (event.code <= 255))
+        {
+
+            // Lookup the scancode in the keymap to get a keycode
+            keycode = keymapUS[event.code];
+
+            // Make sure we got a valid keycode
+            if ((keycode > 0) && (keycode < MAX_KEYBOARD_KEYS))
             {
-                keycode = keymapUS[event.code & 0xFF];     // The code we get is a scancode so we look up the appropriate keycode
 
-                // Make sure we got a valid keycode
-                if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState)))
+                // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
+                // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL,
+                // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat
+                CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1);
+                CORE.Input.Keyboard.keyRepeatInFrame[keycode] = (event.value == 2);
+
+                // If the key is pressed add it to the queues
+                if (event.value == 1)
                 {
-                    // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
-                    // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL,
-                    // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat
-                    CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0;
-                    CORE.Input.Keyboard.keyRepeatInFrame[keycode] = (event.value == 2)? 1 : 0;
-                    if (event.value >= 1)
+                    if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
                     {
-                        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;     // Register last key pressed
+                        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;
                         CORE.Input.Keyboard.keyPressedQueueCount++;
                     }
 
-                #if defined(SUPPORT_SCREEN_CAPTURE)
-                    // Check screen capture key (raylib key: KEY_F12)
-                    if (CORE.Input.Keyboard.currentKeyState[301] == 1)
+                    if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
                     {
-                        TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
-                        screenshotCounter++;
+                        // TODO/FIXME: This is not actually converting to unicode properly because it's not taking things like shift into account
+                        CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = EvkeyToUnicodeLUT[event.code];
+                        CORE.Input.Keyboard.charPressedQueueCount++;
                     }
-                #endif
-
-                    // Detect char presses (unicode)
-                    if (event.value == 1)
-                    {
-                        // Check if there is space available in the queue
-                        if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
-                        {
-                            // Add character to the queue
-                            CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = EvkeyToUnicodeLUT[event.code];
-                            CORE.Input.Keyboard.charPressedQueueCount++;
-                        }
-                    }
-
-                    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);
                 }
+
+                TRACELOG(LOG_DEBUG, "DRM: KEY_%s Keycode(linux): %4i KeyCode(raylib): %4i", (event.value == 0) ? "UP  " : "DOWN", event.code, keycode);
             }
         }
     }
 }
 
-// Input device events reading thread
-static void *EventThread(void *arg)
+static void PollGamepadEvents(void)
 {
-/*
+    // Read gamepad event
     struct input_event event = { 0 };
-    InputEventWorker *worker = (InputEventWorker *)arg;
-
-    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)
+    for (int i = 0; i < platform.gamepadCount; i++)
     {
-        // Try to read data from the device and only continue if successful
-        while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event))
+        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))
         {
-            // Relative movement parsing
-            if (event.type == EV_REL)
+            if (event.type == EV_KEY)
             {
-                if (event.code == REL_X)
-                {
-                    if (platform.cursorRelative)
-                    {
-                        CORE.Input.Mouse.currentPosition.x -= event.value;
-                        CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
-                    }
-                    else
-                    {
-                        CORE.Input.Mouse.currentPosition.x += event.value;
-                        CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
-                    }
+                if (event.code >= MAX_GAMEPAD_BUTTONS) continue;
 
-                    touchAction = 2;    // TOUCH_ACTION_MOVE
-                    gestureUpdate = true;
-                }
-
-                if (event.code == REL_Y)
-                {
-                    if (platform.cursorRelative)
-                    {
-                        CORE.Input.Mouse.currentPosition.y -= event.value;
-                        CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
-                    }
-                    else
-                    {
-                        CORE.Input.Mouse.currentPosition.y += event.value;
-                        CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
-                    }
+                TRACELOG(LOG_DEBUG, "DRM: Gamepad %i button: %i, value: %i", i, event.code, event.value);
 
-                    touchAction = 2;    // TOUCH_ACTION_MOVE
-                    gestureUpdate = true;
-                }
+                // 1 - button pressed, 0 - button released
+                CORE.Input.Gamepad.currentButtonState[i][event.code] = event.value;
 
-                if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value;
+                if (event.value == 1) CORE.Input.Gamepad.lastButtonPressed = event.code;
+                else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
             }
-
-            // Absolute movement parsing
-            if (event.type == EV_ABS)
+            else if (event.type == EV_ABS)
             {
-                // Basic movement
-                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.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width;        // Scale according to absRange
-
-                    touchAction = 2;    // TOUCH_ACTION_MOVE
-                    gestureUpdate = true;
-                }
+                if (event.code >= MAX_GAMEPAD_AXIS) continue;
 
-                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.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height;      // Scale according to absRange
+                TRACELOG(LOG_DEBUG, "DRM: Gamepad %i axis: %i, value: %i", i, platform.gamepadAbsAxisMap[i][event.code], event.value);
 
-                    touchAction = 2;    // TOUCH_ACTION_MOVE
-                    gestureUpdate = true;
-                }
+                int min = platform.gamepadAbsAxisRange[i][event.code][0];
+                int range = platform.gamepadAbsAxisRange[i][event.code][1];
 
-                // Multitouch movement
-                if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value;   // Remember the slot number for the folowing events
+                // 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;
+            }
+        }
+    }
+}
 
-                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
-                }
+static void PollMouseEvents(void)
+{
+    int fd = platform.mouseFd;
+    if (fd == -1) return;
 
-                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
-                }
+    struct input_event event = { 0 };
 
-                if (event.code == ABS_MT_TRACKING_ID)
-                {
-                    if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS))
-                    {
-                        // Touch has ended for this point
-                        CORE.Input.Touch.position[worker->touchSlot].x = -1;
-                        CORE.Input.Touch.position[worker->touchSlot].y = -1;
-                    }
-                }
+    int touchAction = -1;           // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
 
-                // Touchscreen tap
-                if (event.code == ABS_PRESSURE)
+    // 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)
                 {
-                    int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
-
-                    if (!event.value && previousMouseLeftButtonState)
-                    {
-                        platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
-
-                        touchAction = 0;    // TOUCH_ACTION_UP
-                        gestureUpdate = true;
-                    }
-
-                    if (event.value && !previousMouseLeftButtonState)
-                    {
-                        platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
-
-                        touchAction = 1;    // TOUCH_ACTION_DOWN
-                        gestureUpdate = true;
-                    }
+                    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
             }
 
-            // Button parsing
-            if (event.type == EV_KEY)
+            if (event.code == REL_Y)
             {
-                // Mouse button parsing
-                if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
+                if (platform.cursorRelative)
                 {
-                    platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
-
-                    if (event.value > 0) touchAction = 1;   // TOUCH_ACTION_DOWN
-                    else touchAction = 0;       // TOUCH_ACTION_UP
-                    gestureUpdate = true;
+                    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;
 
-                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;
+                touchAction = 2;    // TOUCH_ACTION_MOVE
             }
 
-            // Screen confinement
-            if (!CORE.Input.Mouse.cursorHidden)
+            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)
             {
-                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;
+                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
 
-                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;
+                touchAction = 2;    // TOUCH_ACTION_MOVE
             }
 
-            // Update touch point count
-            CORE.Input.Touch.pointCount = 0;
-            for (int i = 0; i < MAX_TOUCH_POINTS; i++)
+            if (event.code == ABS_Y)
             {
-                if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++;
+                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
             }
 
-#if defined(SUPPORT_GESTURES_SYSTEM)
-            if (gestureUpdate)
+            // 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)
             {
-                GestureEvent gestureEvent = { 0 };
+                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
+            }
 
-                gestureEvent.touchAction = touchAction;
-                gestureEvent.pointCount = CORE.Input.Touch.pointCount;
+            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
+            }
 
-                for (int i = 0; i < MAX_TOUCH_POINTS; i++)
+            if (event.code == ABS_MT_TRACKING_ID)
+            {
+                if ((event.value < 0) && (platform.touchSlot < MAX_TOUCH_POINTS))
                 {
-                    gestureEvent.pointId[i] = i;
-                    gestureEvent.position[i] = CORE.Input.Touch.position[i];
+                    // Touch has ended for this point
+                    CORE.Input.Touch.position[platform.touchSlot].x = -1;
+                    CORE.Input.Touch.position[platform.touchSlot].y = -1;
                 }
-
-                ProcessGestureEvent(gestureEvent);
             }
-#endif
-        }
 
-        WaitTime(0.005);    // Sleep for 5ms to avoid hogging CPU time
-    }
+            // Touchscreen tap
+            if (event.code == ABS_PRESSURE)
+            {
+                int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
 
-    close(worker->fd);
-*/
-    return NULL;
-}
+                if (!event.value && previousMouseLeftButtonState)
+                {
+                    platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
 
-// Initialize gamepad system
-static void InitGamepad(void)
-{
-    char gamepadDev[128] = { 0 };
+                    touchAction = 0;    // TOUCH_ACTION_UP
+                }
 
-    for (int i = 0; i < MAX_GAMEPADS; i++)
-    {
-        sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i);
+                if (event.value && !previousMouseLeftButtonState)
+                {
+                    platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
+
+                    touchAction = 1;    // TOUCH_ACTION_DOWN
+                }
+            }
 
-        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
+
+        // Button parsing
+        if (event.type == EV_KEY)
         {
-            CORE.Input.Gamepad.ready[i] = true;
+            // Mouse button parsing
+            if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
+            {
+                platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
 
-            // NOTE: Only show message for first gamepad
-            if (i == 0) TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully");
+                if (event.value > 0) touchAction = 1;   // TOUCH_ACTION_DOWN
+                else touchAction = 0;       // TOUCH_ACTION_UP
+            }
 
-            ioctl(platform.gamepadStreamFd[i], JSIOCGNAME(64), &CORE.Input.Gamepad.name[i]);
-            ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount[i]);
+            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;
         }
-    }
-}
 
-// 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
-    };
+        // 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;
 
-    // Read gamepad event
-    struct js_event gamepadEvent = { 0 };
+            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;
+        }
 
-    for (int i = 0; i < MAX_GAMEPADS; i++)
-    {
-        if (CORE.Input.Gamepad.ready[i])
+        // Update touch point count
+        CORE.Input.Touch.pointCount = 0;
+        for (int i = 0; i < MAX_TOUCH_POINTS; 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];
+            if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++;
+        }
 
-            while (read(platform.gamepadStreamFd[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
-            {
-                gamepadEvent.type &= ~JS_EVENT_INIT;     // Ignore synthetic events
+#if defined(SUPPORT_GESTURES_SYSTEM)
+        if (touchAction > -1)
+        {
+            GestureEvent gestureEvent = { 0 };
 
-                // 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);
+            gestureEvent.touchAction = touchAction;
+            gestureEvent.pointCount = CORE.Input.Touch.pointCount;
 
-                    if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
-                    {
-                        // 1 - button pressed, 0 - button released
-                        CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value;
+            for (int i = 0; i < MAX_TOUCH_POINTS; i++)
+            {
+                gestureEvent.pointId[i] = i;
+                gestureEvent.position[i] = CORE.Input.Touch.position[i];
+            }
 
-                        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);
+            ProcessGestureEvent(gestureEvent);
 
-                    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;
-                    }
-                }
-            }
+            touchAction = -1;
         }
+#endif
     }
 }