From 30781c423b38405544c2880f15789a07f3f1e3ca Mon Sep 17 00:00:00 2001 From: MrMugame <40832361+MrMugame@users.noreply.github.com> Date: Thu, 28 Mar 2024 13:14:21 +0100 Subject: [PATCH] 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 --- src/platforms/rcore_drm.c | 979 ++++++++++++++------------------------ 1 file changed, 351 insertions(+), 628 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index d303ab1d..b911a1da 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -57,6 +57,12 @@ #include // Required for: ioctl() - UNIX System call for device-specific input/output operations #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition #include // 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 support library #include // 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 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' 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" - // 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" - 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 } }