From e07ec6a2e8facaa6dd4d9229c0e4c8e080e1fcf4 Mon Sep 17 00:00:00 2001
From: Berni8k <berni8k@yahoo.com>
Date: Sun, 21 Oct 2018 00:09:17 +0100
Subject: [PATCH] Overhaul mouse and touch for RaspberryPi \n\nNow all
 '/dev/input/event*' devices are now used for input. No longer uses
 '/dev/input/mouse*', keyboard and gamepad continue to use existing
 method\nMultitouch is now supported on RPi with 10 point multitouch\nFixed
 bugs with IsMouseButtonPressed(Used to constantly fire when holding button)
 and GetMouseWheelMove(Did not work)\n Fixed exesive CPU usage of
 GamepadThread

---
 src/core.c   | 492 ++++++++++++++++++++++++++++++++++++---------------
 src/raylib.h |   2 +-
 2 files changed, 349 insertions(+), 145 deletions(-)

diff --git a/src/core.c b/src/core.c
index 91022b11d..cb6c7e416 100644
--- a/src/core.c
+++ b/src/core.c
@@ -189,6 +189,7 @@
     #include <unistd.h>         // POSIX standard function definitions - read(), close(), STDIN_FILENO
     #include <termios.h>        // POSIX terminal control definitions - tcgetattr(), tcsetattr()
     #include <pthread.h>        // POSIX threads management (mouse input)
+    #include <dirent.h>         // POSIX directory browsing
 
     #include <sys/ioctl.h>      // UNIX System call for device-specific input/output operations - ioctl()
     #include <linux/kd.h>       // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
@@ -223,9 +224,8 @@
 #if defined(PLATFORM_RPI)
     // Old device inputs system
     #define DEFAULT_KEYBOARD_DEV      STDIN_FILENO              // Standard input
-    #define DEFAULT_MOUSE_DEV         "/dev/input/mouse0"       // Mouse input
-    #define DEFAULT_TOUCH_DEV         "/dev/input/event4"       // Touch input virtual device (created by ts_uinput)
     #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
 
     // New device input events (evdev) (must be detected)
     //#define DEFAULT_KEYBOARD_DEV    "/dev/input/eventN"
@@ -329,11 +329,22 @@ static int currentMouseWheelY = 0;              // Registers current mouse wheel
 
 #if defined(PLATFORM_RPI)
 static int mouseStream = -1;                    // Mouse device file descriptor
-static bool mouseReady = false;                 // Flag to know if mouse is ready
-static pthread_t mouseThreadId;                 // Mouse reading thread id
-static int touchStream = -1;                    // Touch device file descriptor
-static bool touchReady = false;                 // Flag to know if touch interface is ready
-static pthread_t touchThreadId;                 // Touch reading thread id
+static char currentMouseStateEvdev[3] = { 0 };  // Holds the new mouse state for the next polling event to grab (Can't be written directly due to multithreading, app could miss the update)
+typedef struct {
+  pthread_t threadId;                           // Event reading thread id
+  int fd;                                       // File descriptor to the device it is assigned to
+  float sensitivity;                            // Sensitivitzy multiplier for relative mouse movements
+  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;
+
+static InputEventWorker eventWorkers[10];       // List of worker threads for every monitored "/dev/input/event<N>"
+
 #endif
 #if defined(PLATFORM_WEB)
 static bool toggleCursorLock = false;           // Ask for cursor pointer lock on next click
@@ -447,9 +458,8 @@ static void InitKeyboard(void);                         // Init raw keyboard sys
 static void ProcessKeyboard(void);                      // Process keyboard events
 static void RestoreKeyboard(void);                      // Restore keyboard system
 static void InitMouse(void);                            // Mouse initialization (including mouse thread)
-static void *MouseThread(void *arg);                    // Mouse reading thread
-static void InitTouch(void);                            // Touch device initialization (including touch thread)
-static void *TouchThread(void *arg);                    // Touch device reading thread
+static void EventThreadSpawn(char* device);             // Indetifies a input device and spawns a thread to handle it if needed
+static void *EventThread(void *arg);                    // Input device event reading thread
 static void InitGamepad(void);                          // Init raw gamepad input
 static void *GamepadThread(void *arg);                  // Mouse reading thread
 #endif
@@ -566,7 +576,6 @@ void InitWindow(int width, int height, const char *title)
 #if defined(PLATFORM_RPI)
     // Init raw input system
     InitMouse();        // Mouse init
-    InitTouch();        // Touch init
     InitKeyboard();     // Keyboard init
     InitGamepad();      // Gamepad init
 #endif
@@ -661,8 +670,13 @@ void CloseWindow(void)
 
     windowShouldClose = true;   // Added to force threads to exit when the close window is called
 
-    pthread_join(mouseThreadId, NULL);
-    pthread_join(touchThreadId, NULL);
+    for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
+    {
+        if(eventWorkers[i].threadId == 0)
+        {
+            pthread_join(eventWorkers[i].threadId, NULL);
+        }
+    }
     pthread_join(gamepadThreadId, NULL);
 #endif
 
@@ -1984,7 +1998,7 @@ bool IsMouseButtonPressed(int button)
 #else
     if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true;
 #endif
-
+    
     return pressed;
 }
 
@@ -2128,7 +2142,9 @@ Vector2 GetTouchPosition(int index)
         position.x = position.x*((float)renderWidth/(float)displayWidth) - renderOffsetX/2;
         position.y = position.y*((float)renderHeight/(float)displayHeight) - renderOffsetY/2;
     }
-#else   // PLATFORM_DESKTOP, PLATFORM_RPI
+#elif defined(PLATFORM_RPI)
+    position = touchPosition[index];
+#else   // PLATFORM_DESKTOP
     if (index == 0) position = GetMousePosition();
 #endif
 
@@ -2895,6 +2911,21 @@ static void PollInputEvents(void)
     gamepadAxisCount = 0;
 #endif
 
+#if defined(PLATFORM_RPI)
+    // Register previous keys states
+    for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
+
+    // Register previous mouse states
+    previousMouseWheelY = currentMouseWheelY;
+    currentMouseWheelY = 0;
+    for (int i = 0; i < 3; i++) 
+    {
+        previousMouseState[i] = currentMouseState[i];
+        currentMouseState[i] = currentMouseStateEvdev[i];
+    }
+
+#endif
+
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
     // Mouse input polling
     double mouseX;
@@ -3829,182 +3860,351 @@ static void RestoreKeyboard(void)
 // Mouse initialization (including mouse thread)
 static void InitMouse(void)
 {
-    // NOTE: We can use /dev/input/mice to read from all available mice
-    if ((mouseStream = open(DEFAULT_MOUSE_DEV, O_RDONLY|O_NONBLOCK)) < 0)
+    char Path[256];
+    DIR *d;
+    struct dirent *dir;
+
+    // Reset variables
+    for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
+    {
+        touchPosition[i].x = -1;
+        touchPosition[i].y = -1;
+    }
+
+    // Open the linux directory of "/dev/input"
+    d = opendir(DEFAULT_EVDEV_PATH);
+    if (d) 
     {
-        TraceLog(LOG_WARNING, "Mouse device could not be opened, no mouse available");
+        while ((dir = readdir(d)) != NULL) 
+        {
+            if(strncmp("event", dir->d_name, strlen("event")) == 0)         // Search for devices named "event*"
+            {
+                sprintf(Path, "%s%s", DEFAULT_EVDEV_PATH, dir->d_name);
+                EventThreadSpawn(Path);                                     // Identify the device and spawn a thread for it
+            }
+        }
+        closedir(d);
     }
     else
     {
-        mouseReady = true;
-
-        int error = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL);
-
-        if (error != 0) TraceLog(LOG_WARNING, "Error creating mouse input event thread");
-        else TraceLog(LOG_INFO, "Mouse device initialized successfully");
+        TraceLog(LOG_WARNING, "Unable to open linux event directory %s", DEFAULT_EVDEV_PATH);
     }
 }
 
-// Mouse reading thread
-// NOTE: We need a separate thread to avoid loosing mouse events,
-// if too much time passes between reads, queue gets full and new events override older ones...
-static void *MouseThread(void *arg)
-{
-    const unsigned char XSIGN = (1 << 4);
-    const unsigned char YSIGN = (1 << 5);
-
-    typedef struct {
-        char buttons;
-        char dx, dy;
-    } MouseEvent;
-
-    MouseEvent mouse;
+static void EventThreadSpawn(char* device)
+{
+    #define BITS_PER_LONG (sizeof(long) * 8)
+    #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+    #define OFF(x)  ((x)%BITS_PER_LONG)
+    #define BIT(x)  (1UL<<OFF(x))
+    #define LONG(x) ((x)/BITS_PER_LONG)
+    #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
+    struct input_absinfo absinfo;
+    unsigned long ev_bits[NBITS(EV_MAX)];
+    unsigned long abs_bits[NBITS(ABS_MAX)];
+    unsigned long rel_bits[NBITS(REL_MAX)];
+    unsigned long key_bits[NBITS(KEY_MAX)];
+    bool hasAbs = false;
+    bool hasRel = false;
+    bool hasAbsMulti = false;
+    int FreeWorkerId = -1;
+    int fd = -1;
+    InputEventWorker* Worker;
+
+    /////////////////////////////////// Open the device and allocate worker  /////////////////////////////////////////////
+
+    // Find a free spot in the workers array
+    for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
+    {
+        if(eventWorkers[i].threadId == 0)
+        {
+            FreeWorkerId = i;
+            break;
+        }
+    }
 
-    int mouseRelX = 0;
-    int mouseRelY = 0;
+    // Select the free worker from array
+    if(FreeWorkerId >= 0)
+    {
+        Worker = &(eventWorkers[FreeWorkerId]);       // Grab a pointer to the worker
+        memset(Worker, 0, sizeof(InputEventWorker));  // Clear the worker
+    }
+    else
+    {
+        TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Out of worker slots", device);
+        return;
+    }
 
-    while (!windowShouldClose)
+    // Open the device
+    fd = open(device, O_RDONLY | O_NONBLOCK);
+    if(fd < 0)
     {
-        if (read(mouseStream, &mouse, sizeof(MouseEvent)) == (int)sizeof(MouseEvent))
-        {
-            if ((mouse.buttons & 0x08) == 0) break;   // This bit should always be set
+        TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Can't open device (Err: %d)", device, Worker->fd);
+        return;
+    }
+    Worker->fd = fd;
 
-            // Check Left button pressed
-            if ((mouse.buttons & 0x01) > 0) currentMouseState[0] = 1;
-            else currentMouseState[0] = 0;
+    // At this point we have a connection to the device, 
+    // but we don't yet know what the device is (Could be 
+    // many things, even as simple as a power button)
 
-            // Check Right button pressed
-            if ((mouse.buttons & 0x02) > 0) currentMouseState[1] = 1;
-            else currentMouseState[1] = 0;
+    /////////////////////////////////// Identify the device  /////////////////////////////////////////////
 
-            // Check Middle button pressed
-            if ((mouse.buttons & 0x04) > 0) currentMouseState[2] = 1;
-            else currentMouseState[2] = 0;
+    ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);              // Read a bitfield of the avalable device properties
+
+    // Check for absolute input devices
+    if (TEST_BIT(ev_bits, EV_ABS)) 
+    {
+        ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)),abs_bits);
 
-            mouseRelX = (int)mouse.dx;
-            mouseRelY = (int)mouse.dy;
+        // Check for absolute movement support (usualy touchscreens, but also joysticks)
+        if (TEST_BIT(abs_bits, ABS_X) && TEST_BIT(abs_bits, ABS_Y)) 
+        {
+            hasAbs = true;
+            // Get the scaling values
+            ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
+            Worker->absRange.x = absinfo.minimum;
+            Worker->absRange.width = absinfo.maximum - absinfo.minimum;
+            ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
+            Worker->absRange.y = absinfo.minimum;
+            Worker->absRange.height = absinfo.maximum - absinfo.minimum;
+        }
+        
+        // Check for multiple absolute movement support (usualy multitouch touchscreens)
+        if (TEST_BIT(abs_bits, ABS_MT_POSITION_X) && TEST_BIT(abs_bits, 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;
+            ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
+            Worker->absRange.y = absinfo.minimum;
+            Worker->absRange.height = absinfo.maximum - absinfo.minimum;
+        }
+    }
 
-            if ((mouse.buttons & XSIGN) > 0) mouseRelX = -1*(255 - mouseRelX);
-            if ((mouse.buttons & YSIGN) > 0) mouseRelY = -1*(255 - mouseRelY);
+    // Check for relative movement support (usualy mouse)
+    if (TEST_BIT(ev_bits, EV_REL)) 
+    {
+        ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)),rel_bits);
+        if (TEST_BIT(rel_bits, REL_X) && TEST_BIT(rel_bits, REL_Y))
+        {
+            hasRel = true;
+        }
+    }
 
-            // NOTE: Mouse movement is normalized to not be screen resolution dependant
-            // We suppose 2*255 (max relative movement) is equivalent to screenWidth (max pixels width)
-            // Result after normalization is multiplied by MOUSE_SENSITIVITY factor
+    // Check for button support to determine the device type(usualy on all input devices)
+    if (TEST_BIT(ev_bits, EV_KEY)) 
+    {
+        ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)),key_bits);
 
-            mousePosition.x += (float)mouseRelX*((float)screenWidth/(2*255))*MOUSE_SENSITIVITY;
-            mousePosition.y -= (float)mouseRelY*((float)screenHeight/(2*255))*MOUSE_SENSITIVITY;
+        if(hasAbs || hasAbsMulti)
+        {
+            if(TEST_BIT(key_bits, BTN_TOUCH))
+                Worker->isTouch = true;                 // This is a touchscreen
+            if(TEST_BIT(key_bits, BTN_TOOL_FINGER))
+                Worker->isTouch = true;                 // This is a drawing tablet
+            if(TEST_BIT(key_bits, BTN_TOOL_PEN))
+                Worker->isTouch = true;                 // This is a drawing tablet
+            if(TEST_BIT(key_bits, BTN_STYLUS))
+                Worker->isTouch = true;                 // This is a drawing tablet
+            if(Worker->isTouch || hasAbsMulti)
+                Worker->isMultitouch = true;            // This is a multitouch capable device
+        }
 
-            if (mousePosition.x < 0) mousePosition.x = 0;
-            if (mousePosition.y < 0) mousePosition.y = 0;
+        if(hasRel)
+        {
+            if (TEST_BIT(key_bits, BTN_LEFT))
+                Worker->isMouse = true;                  // This is a mouse
+            if (TEST_BIT(key_bits, BTN_RIGHT))
+                Worker->isMouse = true;                  // This is a mouse
+        }
 
-            if (mousePosition.x > screenWidth) mousePosition.x = screenWidth;
-            if (mousePosition.y > screenHeight) mousePosition.y = screenHeight;
-       }
-       //else read(mouseStream, &mouse, 1);   // Try to sync up again
+        if (TEST_BIT(key_bits, BTN_A))
+            Worker->isGamepad = true;                   // This is a gamepad
+        if (TEST_BIT(key_bits, BTN_TRIGGER))
+            Worker->isGamepad = true;                   // This is a gamepad
+        if (TEST_BIT(key_bits, BTN_START))
+            Worker->isGamepad = true;                   // This is a gamepad
+        if (TEST_BIT(key_bits, BTN_TL))
+            Worker->isGamepad = true;                   // This is a gamepad
+        if (TEST_BIT(key_bits, BTN_TL))
+            Worker->isGamepad = true;                   // This is a gamepad
+
+        if (TEST_BIT(key_bits, KEY_SPACE))
+            Worker->isKeyboard = true;                  // This is a keyboard
     }
 
-    return NULL;
-}
 
-// Touch initialization (including touch thread)
-static void InitTouch(void)
-{
-    if ((touchStream = open(DEFAULT_TOUCH_DEV, O_RDONLY|O_NONBLOCK)) < 0)
+    /////////////////////////////////// Decide what to do with the device  /////////////////////////////////////////////
+    if(Worker->isTouch || Worker->isMouse)
     {
-        TraceLog(LOG_WARNING, "Touch device could not be opened, no touchscreen available");
+        // Looks like a interesting device
+        TraceLog(LOG_INFO, "Opening input device '%s' (%s%s%s%s%s)", device,
+            Worker->isMouse ? "mouse " : "",
+            Worker->isMultitouch ? "multitouch " : "",
+            Worker->isTouch ? "touchscreen " : "",
+            Worker->isGamepad ? "gamepad " : "",
+            Worker->isKeyboard ? "keyboard " : ""
+            );
+        // Create a thread for this device
+        int error = pthread_create(&Worker->threadId, NULL, &EventThread, (void*)Worker);
+        if(error != 0)
+        {
+            TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Can't create thread (Err: %d)", device, error);
+            Worker->threadId = 0;
+            close(fd);
+        }
     }
     else
     {
-        touchReady = true;
-
-        int error = pthread_create(&touchThreadId, NULL, &TouchThread, NULL);
-
-        if (error != 0) TraceLog(LOG_WARNING, "Error creating touch input event thread");
-        else TraceLog(LOG_INFO, "Touch device initialized successfully");
+        // We are not interested in this device
+        close(fd);
     }
 }
 
-// Touch reading thread.
-// This reads from a Virtual Input Event /dev/input/event4 which is
-// created by the ts_uinput daemon. This takes, filters and scales
-// raw input from the Touchscreen (which appears in /dev/input/event3)
-// based on the Calibration data referenced by tslib.
-static void *TouchThread(void *arg)
+static void *EventThread(void *arg)
 {
     struct input_event ev;
     GestureEvent gestureEvent;
+    InputEventWorker* Worker = (InputEventWorker*)arg;
+    bool GestureNeedsUpdate = false;
 
     while (!windowShouldClose)
     {
-        if (read(touchStream, &ev, sizeof(ev)) == (int)sizeof(ev))
+        if (read(Worker->fd, &ev, sizeof(ev)) == (int)sizeof(ev))
         {
-            // if pressure > 0 then simulate left mouse button click
-            if (ev.type == EV_ABS && ev.code == 24 && ev.value == 0 && currentMouseState[0] == 1)
+            /////////////////////////////// Relative movement parsing ////////////////////////////////////
+            if(ev.type == EV_REL)
             {
-                currentMouseState[0] = 0;
-                gestureEvent.touchAction = TOUCH_UP;
-                gestureEvent.pointCount = 1;
-                gestureEvent.pointerId[0] = 0;
-                gestureEvent.pointerId[1] = 1;
-                gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y };
-                gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y };
-                gestureEvent.position[0].x /= (float)GetScreenWidth();
-                gestureEvent.position[0].y /= (float)GetScreenHeight();
-                gestureEvent.position[1].x /= (float)GetScreenWidth();
-                gestureEvent.position[1].y /= (float)GetScreenHeight();
-                ProcessGestureEvent(gestureEvent);
+                if(ev.code == REL_X)
+                {
+                    mousePosition.x += ev.value;
+                    touchPosition[0].x = mousePosition.x;
+                    gestureEvent.touchAction = TOUCH_MOVE;
+                    GestureNeedsUpdate = true;
+                }
+
+                if(ev.code == REL_Y)
+                {
+                    mousePosition.y += ev.value;
+                    touchPosition[0].y = mousePosition.y;
+                    gestureEvent.touchAction = TOUCH_MOVE;
+                    GestureNeedsUpdate = true;
+                }
+
+                if(ev.code == REL_WHEEL)
+                {
+                    currentMouseWheelY += ev.value;
+                }              
             }
-            if (ev.type == EV_ABS && ev.code == 24 && ev.value > 0 && currentMouseState[0] == 0)
+
+            /////////////////////////////// Absolute movement parsing ////////////////////////////////////
+            if(ev.type == EV_ABS)
             {
-                currentMouseState[0] = 1;
-                gestureEvent.touchAction = TOUCH_DOWN;
-                gestureEvent.pointCount = 1;
-                gestureEvent.pointerId[0] = 0;
-                gestureEvent.pointerId[1] = 1;
-                gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y };
-                gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y };
-                gestureEvent.position[0].x /= (float)GetScreenWidth();
-                gestureEvent.position[0].y /= (float)GetScreenHeight();
-                gestureEvent.position[1].x /= (float)GetScreenWidth();
-                gestureEvent.position[1].y /= (float)GetScreenHeight();
-                ProcessGestureEvent(gestureEvent);
+                // Basic movement
+                if(ev.code == ABS_X)
+                {
+                    mousePosition.x = (ev.value - Worker->absRange.x) * screenWidth / Worker->absRange.width;  //Scale acording to absRange
+                    gestureEvent.touchAction = TOUCH_MOVE;
+                    GestureNeedsUpdate = true;
+                }
+
+                if(ev.code == ABS_Y)
+                {
+                    mousePosition.y = (ev.value - Worker->absRange.y) * screenHeight / Worker->absRange.height;  //Scale acording to absRange
+                    gestureEvent.touchAction = TOUCH_MOVE;
+                    GestureNeedsUpdate = true;
+                }
+
+                //Multitouch movement
+                if(ev.code == ABS_MT_SLOT)
+                {
+                    Worker->touchSlot = ev.value;   //Remeber the slot number for the folowing events
+                }
+
+                if(ev.code == ABS_MT_POSITION_X)
+                {
+                    if(Worker->touchSlot < MAX_TOUCH_POINTS)
+                        touchPosition[Worker->touchSlot].x = (ev.value - Worker->absRange.x) * screenWidth / Worker->absRange.width; //Scale acording to absRange
+                }
+
+                if(ev.code == ABS_MT_POSITION_Y)
+                {
+                    if(Worker->touchSlot < MAX_TOUCH_POINTS)
+                        touchPosition[Worker->touchSlot].y = (ev.value - Worker->absRange.y) * screenHeight / Worker->absRange.height; //Scale acording to absRange
+                }
+
+                if(ev.code == ABS_MT_TRACKING_ID)
+                {
+                    if( (ev.value < 0) && (Worker->touchSlot < MAX_TOUCH_POINTS) )
+                    {
+                        //Touch has ended for this point
+                        touchPosition[Worker->touchSlot].x = -1;
+                        touchPosition[Worker->touchSlot].y = -1;
+                    }
+                }
             }
-            // x & y values supplied by event4 have been scaled & de-jittered using tslib calibration data
-            if (ev.type == EV_ABS && ev.code == 0)
+
+            /////////////////////////////// Button parsing ////////////////////////////////////
+            if(ev.type == EV_KEY)
             {
-                mousePosition.x = ev.value;
-                if (mousePosition.x < 0) mousePosition.x = 0;
-                if (mousePosition.x > screenWidth) mousePosition.x = screenWidth;
-                gestureEvent.touchAction = TOUCH_MOVE;
-                gestureEvent.pointCount = 1;
-                gestureEvent.pointerId[0] = 0;
-                gestureEvent.pointerId[1] = 1;
-                gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y };
-                gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y };
-                gestureEvent.position[0].x /= (float)GetScreenWidth();
-                gestureEvent.position[0].y /= (float)GetScreenHeight();
-                gestureEvent.position[1].x /= (float)GetScreenWidth();
-                gestureEvent.position[1].y /= (float)GetScreenHeight();
-                ProcessGestureEvent(gestureEvent);
+                if((ev.code == BTN_TOUCH) || (ev.code == BTN_LEFT))
+                {
+                    currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = ev.value;
+                    if(ev.value > 0)
+                        gestureEvent.touchAction = TOUCH_DOWN;
+                    else
+                        gestureEvent.touchAction = TOUCH_UP;
+                    GestureNeedsUpdate = true;
+                }
+
+                if(ev.code == BTN_RIGHT)
+                {
+                    currentMouseStateEvdev[MOUSE_RIGHT_BUTTON] =  ev.value;
+                }
+
+                if(ev.code == BTN_MIDDLE)
+                {
+                    currentMouseStateEvdev[MOUSE_MIDDLE_BUTTON] =  ev.value;
+                }
+
             }
-            if (ev.type == EV_ABS && ev.code == 1)
+
+            /////////////////////////////// Screen confinement ////////////////////////////////////
+            if(mousePosition.x < 0)
+                mousePosition.x = 0;
+            if(mousePosition.x > screenWidth / mouseScale)
+                mousePosition.x = screenWidth / mouseScale;
+
+            if(mousePosition.y < 0)
+                mousePosition.y = 0;
+            if(mousePosition.y > screenHeight / mouseScale)
+                mousePosition.y = screenHeight / mouseScale;
+
+            /////////////////////////////// Gesture update ////////////////////////////////////
+            if(GestureNeedsUpdate)
             {
-                mousePosition.y = ev.value;
-                if (mousePosition.y < 0) mousePosition.y = 0;
-                if (mousePosition.y > screenHeight) mousePosition.y = screenHeight;
-                gestureEvent.touchAction = TOUCH_MOVE;
-                gestureEvent.pointCount = 1;
+                gestureEvent.pointCount = 0;
+                if(touchPosition[0].x >= 0) gestureEvent.pointCount++;
+                if(touchPosition[1].x >= 0) gestureEvent.pointCount++;
+                if(touchPosition[2].x >= 0) gestureEvent.pointCount++;
+                if(touchPosition[3].x >= 0) gestureEvent.pointCount++;
                 gestureEvent.pointerId[0] = 0;
                 gestureEvent.pointerId[1] = 1;
-                gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y };
-                gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y };
-                gestureEvent.position[0].x /= (float)GetScreenWidth();
-                gestureEvent.position[0].y /= (float)GetScreenHeight();
-                gestureEvent.position[1].x /= (float)GetScreenWidth();
-                gestureEvent.position[1].y /= (float)GetScreenHeight();
+                gestureEvent.pointerId[2] = 2;
+                gestureEvent.pointerId[3] = 3;
+                gestureEvent.position[0] = touchPosition[0];
+                gestureEvent.position[1] = touchPosition[1];
+                gestureEvent.position[2] = touchPosition[2];
+                gestureEvent.position[3] = touchPosition[3];
                 ProcessGestureEvent(gestureEvent);
             }
-
+        }
+        else
+        {
+            usleep(5000); //Sleep for 5ms to avoid hogging CPU time
         }
     }
     return NULL;
@@ -4090,6 +4290,10 @@ static void *GamepadThread(void *arg)
                     }
                 }
             }
+            else
+            {
+                usleep(1000); //Sleep for 1ms to avoid hogging CPU time
+            }
         }
     }
 
diff --git a/src/raylib.h b/src/raylib.h
index aa5bd16f2..8256b745a 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -223,7 +223,7 @@
 #define MOUSE_MIDDLE_BUTTON   2
 
 // Touch points registered
-#define MAX_TOUCH_POINTS      2
+#define MAX_TOUCH_POINTS      10
 
 // Gamepad Number
 #define GAMEPAD_PLAYER1       0