minesweeper

A minewseeper implementation to play around with Hare and Raylib
git clone https://git.tronto.net/minesweeper
Download | Log | Files | Refs | README | LICENSE

rcore_drm.c (75745B)


      1 /**********************************************************************************************
      2 *
      3 *   rcore_drm - Functions to manage window, graphics device and inputs
      4 *
      5 *   PLATFORM: DRM
      6 *       - Raspberry Pi 0-5 (DRM/KMS)
      7 *       - Linux DRM subsystem (KMS mode)
      8 *
      9 *   LIMITATIONS:
     10 *       - Most of the window/monitor functions are not implemented (not required)
     11 *
     12 *   POSSIBLE IMPROVEMENTS:
     13 *       - Improvement 01
     14 *       - Improvement 02
     15 *
     16 *   ADDITIONAL NOTES:
     17 *       - TRACELOG() function is located in raylib [utils] module
     18 *
     19 *   CONFIGURATION:
     20 *       #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only)
     21 *           Reconfigure standard input to receive key inputs, works with SSH connection.
     22 *           WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other
     23 *           running processes orblocking the device if not restored properly. Use with care.
     24 *
     25 *   DEPENDENCIES:
     26 *       - DRM and GLM: System libraries for display initialization and configuration
     27 *       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
     28 *
     29 *
     30 *   LICENSE: zlib/libpng
     31 *
     32 *   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
     33 *
     34 *   This software is provided "as-is", without any express or implied warranty. In no event
     35 *   will the authors be held liable for any damages arising from the use of this software.
     36 *
     37 *   Permission is granted to anyone to use this software for any purpose, including commercial
     38 *   applications, and to alter it and redistribute it freely, subject to the following restrictions:
     39 *
     40 *     1. The origin of this software must not be misrepresented; you must not claim that you
     41 *     wrote the original software. If you use this software in a product, an acknowledgment
     42 *     in the product documentation would be appreciated but is not required.
     43 *
     44 *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
     45 *     as being the original software.
     46 *
     47 *     3. This notice may not be removed or altered from any source distribution.
     48 *
     49 **********************************************************************************************/
     50 
     51 #include <fcntl.h>   // POSIX file control definitions - open(), creat(), fcntl()
     52 #include <unistd.h>  // POSIX standard function definitions - read(), close(), STDIN_FILENO
     53 #include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
     54 #include <pthread.h> // POSIX threads management (inputs reading)
     55 #include <dirent.h>  // POSIX directory browsing
     56 
     57 #include <sys/ioctl.h>      // Required for: ioctl() - UNIX System call for device-specific input/output operations
     58 #include <linux/kd.h>       // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
     59 #include <linux/input.h>    // Linux: Keycodes constants definition (KEY_A, ...)
     60 #include <linux/joystick.h> // Linux: Joystick support library
     61 
     62 // WARNING: Both 'linux/input.h' and 'raylib.h' define KEY_F12
     63 // To avoid conflict with the capturing code in rcore.c we undefine the macro KEY_F12,
     64 // so the enum KEY_F12 from raylib is used
     65 #undef KEY_F12
     66 
     67 #include <gbm.h>            // Generic Buffer Management (native platform for EGL on DRM)
     68 #include <xf86drm.h>        // Direct Rendering Manager user-level library interface
     69 #include <xf86drmMode.h>    // Direct Rendering Manager mode setting (KMS) interface
     70 
     71 #include "EGL/egl.h"        // Native platform windowing system interface
     72 #include "EGL/eglext.h"     // EGL extensions
     73 
     74 #ifndef EGL_OPENGL_ES3_BIT
     75     #define EGL_OPENGL_ES3_BIT  0x40
     76 #endif
     77 
     78 //----------------------------------------------------------------------------------
     79 // Defines and Macros
     80 //----------------------------------------------------------------------------------
     81 #define USE_LAST_TOUCH_DEVICE       // When multiple touchscreens are connected, only use the one with the highest event<N> number
     82 
     83 #define DEFAULT_EVDEV_PATH       "/dev/input/"      // Path to the linux input events
     84 
     85 // So actually the biggest key is KEY_CNT but we only really map the keys up to
     86 // KEY_ALS_TOGGLE
     87 #define KEYMAP_SIZE KEY_ALS_TOGGLE
     88 
     89 //----------------------------------------------------------------------------------
     90 // Types and Structures Definition
     91 //----------------------------------------------------------------------------------
     92 
     93 typedef struct {
     94     // Display data
     95     int fd;                             // File descriptor for /dev/dri/...
     96     drmModeConnector *connector;        // Direct Rendering Manager (DRM) mode connector
     97     drmModeCrtc *crtc;                  // CRT Controller
     98     int modeIndex;                      // Index of the used mode of connector->modes
     99     struct gbm_device *gbmDevice;       // GBM device
    100     struct gbm_surface *gbmSurface;     // GBM surface
    101     struct gbm_bo *prevBO;              // Previous GBM buffer object (during frame swapping)
    102     uint32_t prevFB;                    // Previous GBM framebufer (during frame swapping)
    103 
    104     EGLDisplay device;                  // Native display device (physical screen connection)
    105     EGLSurface surface;                 // Surface to draw on, framebuffers (connected to context)
    106     EGLContext context;                 // Graphic context, mode in which drawing can be done
    107     EGLConfig config;                   // Graphic config
    108 
    109     // Keyboard data
    110     int defaultKeyboardMode;            // Default keyboard mode
    111     bool eventKeyboardMode;             // Keyboard in event mode
    112     int defaultFileFlags;               // Default IO file flags
    113     struct termios defaultSettings;     // Default keyboard settings
    114     int keyboardFd;                     // File descriptor for the evdev keyboard
    115 
    116     // Mouse data
    117     Vector2 eventWheelMove;             // Registers the event mouse wheel variation
    118     // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update
    119     char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab
    120     bool cursorRelative;                // Relative cursor mode
    121     int mouseFd;                        // File descriptor for the evdev mouse/touch/gestures
    122     Rectangle absRange;                 // Range of values for absolute pointing devices (touchscreens)
    123     int touchSlot;                      // Hold the touch slot number of the currently being sent multitouch block
    124 
    125     // Gamepad data
    126     int gamepadStreamFd[MAX_GAMEPADS];  // Gamepad device file descriptor
    127     int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXIS][2]; // [0] = min, [1] = range value of the axis
    128     int gamepadAbsAxisMap[MAX_GAMEPADS][ABS_CNT]; // Maps the axes gamepads from the evdev api to a sequential one
    129     int gamepadCount;                   // The number of gamepads registered
    130 } PlatformData;
    131 
    132 //----------------------------------------------------------------------------------
    133 // Global Variables Definition
    134 //----------------------------------------------------------------------------------
    135 extern CoreData CORE;                   // Global CORE state context
    136 
    137 static PlatformData platform = { 0 };   // Platform specific data
    138 
    139 //----------------------------------------------------------------------------------
    140 // Local Variables Definition
    141 //----------------------------------------------------------------------------------
    142 
    143 // NOTE: The complete evdev EV_KEY list can be found at /usr/include/linux/input-event-codes.h
    144 // TODO: Complete the LUT with all unicode decimal values
    145 // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard:
    146 // Currently non US keyboards will have the wrong mapping for some keys
    147 // NOTE: Replacing this with the keymap from X11 would probably be useless, as people use the drm
    148 // backend to *avoid* X11
    149 static const int evkeyToUnicodeLUT[] = {
    150     0, 27, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 8, 0, 113, 119, 101, 114,
    151     116, 121, 117, 105, 111, 112, 0, 0, 13, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 59,
    152     39, 96, 0, 92, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 32
    153     // LUT currently incomplete, just mapped the most essential keys
    154 };
    155 
    156 // This is the map used to map any keycode returned from linux to a raylib code from 'raylib.h'
    157 // NOTE: Use short here to save a little memory
    158 static const short linuxToRaylibMap[KEYMAP_SIZE] = {
    159     // We don't map those with designated initialization, because we would getting
    160     // into loads of naming conflicts
    161     0,   256, 49,  50,  51,  52,  53,  54,
    162     55,  56,  57,  48,  45,  61,  259, 258,
    163     81,  87,  69,  82,  84,  89,  85,  73,
    164     79,  80,  91,  93,  257, 341, 65,  83,
    165     68,  70,  71,  72,  74,  75,  76,  59,
    166     39,  96,  340, 92,  90,  88,  67,  86,
    167     66,  78,  77,  44,  46,  47,  344, 332,
    168     342, 32,  280, 290, 291, 292, 293, 294,
    169     295, 296, 297, 298, 299, 282, 281, 327,
    170     328, 329, 333, 324, 325, 326, 334, 321,
    171     322, 323, 320, 330, 0,   85,  86,  300,
    172     301, 89,  90,  91,  92,  93,  94,  95,
    173     335, 345, 331, 283, 346, 101, 268, 265,
    174     266, 263, 262, 269, 264, 267, 260, 261,
    175     112, 113, 114, 115, 116, 117, 118, 119,
    176     120, 121, 122, 123, 124, 125, 347, 127,
    177     128, 129, 130, 131, 132, 133, 134, 135,
    178     136, 137, 138, 139, 140, 141, 142, 143,
    179     144, 145, 146, 147, 148, 149, 150, 151,
    180     152, 153, 154, 155, 156, 157, 158, 159,
    181     160, 161, 162, 163, 164, 165, 166, 167,
    182     168, 169, 170, 171, 172, 173, 174, 175,
    183     176, 177, 178, 179, 180, 181, 182, 183,
    184     184, 185, 186, 187, 188, 189, 190, 191,
    185     192, 193, 194, 0,   0,   0,   0,   0,
    186     200, 201, 202, 203, 204, 205, 206, 207,
    187     208, 209, 210, 211, 212, 213, 214, 215,
    188     216, 217, 218, 219, 220, 221, 222, 223,
    189     224, 225, 226, 227, 228, 229, 230, 231,
    190     232, 233, 234, 235, 236, 237, 238, 239,
    191     240, 241, 242, 243, 244, 245, 246, 247,
    192     248, 0,   0,   0,   0,   0,   0,   0,
    193 
    194     // Gamepads are mapped according to:
    195     // https://www.kernel.org/doc/html/next/input/gamepad.html
    196     // Those mappings are standardized, but that doesn't mean people follow
    197     // the standards, so this is more of an approximation
    198     [BTN_DPAD_UP] = GAMEPAD_BUTTON_LEFT_FACE_UP,
    199     [BTN_DPAD_RIGHT] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
    200     [BTN_DPAD_DOWN] = GAMEPAD_BUTTON_LEFT_FACE_DOWN,
    201     [BTN_DPAD_LEFT] = GAMEPAD_BUTTON_LEFT_FACE_LEFT,
    202     [BTN_Y] = GAMEPAD_BUTTON_RIGHT_FACE_UP,
    203     [BTN_B] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
    204     [BTN_A] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
    205     [BTN_X] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
    206     [BTN_TL] = GAMEPAD_BUTTON_LEFT_TRIGGER_1,
    207     [BTN_TL2] = GAMEPAD_BUTTON_LEFT_TRIGGER_2,
    208     [BTN_TR] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
    209     [BTN_TR2] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
    210     [BTN_SELECT] = GAMEPAD_BUTTON_MIDDLE_LEFT,
    211     [BTN_MODE] = GAMEPAD_BUTTON_MIDDLE,
    212     [BTN_START] = GAMEPAD_BUTTON_MIDDLE_RIGHT,
    213     [BTN_THUMBL] = GAMEPAD_BUTTON_LEFT_THUMB,
    214     [BTN_THUMBR] = GAMEPAD_BUTTON_RIGHT_THUMB,
    215 };
    216 
    217 //----------------------------------------------------------------------------------
    218 // Module Internal Functions Declaration
    219 //----------------------------------------------------------------------------------
    220 int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
    221 void ClosePlatform(void);        // Close platform
    222 
    223 #if defined(SUPPORT_SSH_KEYBOARD_RPI)
    224 static void InitKeyboard(void);                 // Initialize raw keyboard system
    225 static void RestoreKeyboard(void);              // Restore keyboard system
    226 static void ProcessKeyboard(void);              // Process keyboard events
    227 #endif
    228 
    229 static void InitEvdevInput(void);               // Initialize evdev inputs
    230 static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate
    231 static void PollKeyboardEvents(void);           // Process evdev keyboard events
    232 static void PollGamepadEvents(void);            // Process evdev gamepad events
    233 static void PollMouseEvents(void);              // Process evdev mouse events
    234 
    235 static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode);                               // Search matching DRM mode in connector's mode list
    236 static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced);      // Search exactly matching DRM connector mode in connector's list
    237 static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced);    // Search the nearest matching DRM connector mode in connector's list
    238 
    239 //----------------------------------------------------------------------------------
    240 // Module Functions Declaration
    241 //----------------------------------------------------------------------------------
    242 // NOTE: Functions declaration is provided by raylib.h
    243 
    244 //----------------------------------------------------------------------------------
    245 // Module Functions Definition: Window and Graphics Device
    246 //----------------------------------------------------------------------------------
    247 
    248 // Check if application should close
    249 // NOTE: By default, if KEY_ESCAPE pressed
    250 bool WindowShouldClose(void)
    251 {
    252     if (CORE.Window.ready) return CORE.Window.shouldClose;
    253     else return true;
    254 }
    255 
    256 // Toggle fullscreen mode
    257 void ToggleFullscreen(void)
    258 {
    259     TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
    260 }
    261 
    262 // Toggle borderless windowed mode
    263 void ToggleBorderlessWindowed(void)
    264 {
    265     TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
    266 }
    267 
    268 // Set window state: maximized, if resizable
    269 void MaximizeWindow(void)
    270 {
    271     TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
    272 }
    273 
    274 // Set window state: minimized
    275 void MinimizeWindow(void)
    276 {
    277     TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
    278 }
    279 
    280 // Set window state: not minimized/maximized
    281 void RestoreWindow(void)
    282 {
    283     TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
    284 }
    285 
    286 // Set window configuration state using flags
    287 void SetWindowState(unsigned int flags)
    288 {
    289     TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
    290 }
    291 
    292 // Clear window configuration state flags
    293 void ClearWindowState(unsigned int flags)
    294 {
    295     TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
    296 }
    297 
    298 // Set icon for window
    299 void SetWindowIcon(Image image)
    300 {
    301     TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
    302 }
    303 
    304 // Set icon for window
    305 void SetWindowIcons(Image *images, int count)
    306 {
    307     TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
    308 }
    309 
    310 // Set title for window
    311 void SetWindowTitle(const char *title)
    312 {
    313     CORE.Window.title = title;
    314 }
    315 
    316 // Set window position on screen (windowed mode)
    317 void SetWindowPosition(int x, int y)
    318 {
    319     TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
    320 }
    321 
    322 // Set monitor for the current window
    323 void SetWindowMonitor(int monitor)
    324 {
    325     TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
    326 }
    327 
    328 // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
    329 void SetWindowMinSize(int width, int height)
    330 {
    331     CORE.Window.screenMin.width = width;
    332     CORE.Window.screenMin.height = height;
    333 }
    334 
    335 // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
    336 void SetWindowMaxSize(int width, int height)
    337 {
    338     CORE.Window.screenMax.width = width;
    339     CORE.Window.screenMax.height = height;
    340 }
    341 
    342 // Set window dimensions
    343 void SetWindowSize(int width, int height)
    344 {
    345     TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
    346 }
    347 
    348 // Set window opacity, value opacity is between 0.0 and 1.0
    349 void SetWindowOpacity(float opacity)
    350 {
    351     TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
    352 }
    353 
    354 // Set window focused
    355 void SetWindowFocused(void)
    356 {
    357     TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
    358 }
    359 
    360 // Get native window handle
    361 void *GetWindowHandle(void)
    362 {
    363     TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
    364     return NULL;
    365 }
    366 
    367 // Get number of monitors
    368 int GetMonitorCount(void)
    369 {
    370     TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
    371     return 1;
    372 }
    373 
    374 // Get number of monitors
    375 int GetCurrentMonitor(void)
    376 {
    377     TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
    378     return 0;
    379 }
    380 
    381 // Get selected monitor position
    382 Vector2 GetMonitorPosition(int monitor)
    383 {
    384     TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
    385     return (Vector2){ 0, 0 };
    386 }
    387 
    388 // Get selected monitor width (currently used by monitor)
    389 int GetMonitorWidth(int monitor)
    390 {
    391     int width = 0;
    392 
    393     if (monitor != 0)
    394     {
    395         TRACELOG(LOG_WARNING, "GetMonitorWidth() implemented for first monitor only");
    396     }
    397     else if ((platform.connector) && (platform.modeIndex >= 0))
    398     {
    399         width = platform.connector->modes[platform.modeIndex].hdisplay;
    400     }
    401 
    402     return width;
    403 }
    404 
    405 // Get selected monitor height (currently used by monitor)
    406 int GetMonitorHeight(int monitor)
    407 {
    408     int height = 0;
    409 
    410     if (monitor != 0)
    411     {
    412         TRACELOG(LOG_WARNING, "GetMonitorHeight() implemented for first monitor only");
    413     }
    414     else if ((platform.connector) && (platform.modeIndex >= 0))
    415     {
    416         height = platform.connector->modes[platform.modeIndex].vdisplay;
    417     }
    418 
    419     return height;
    420 }
    421 
    422 // Get selected monitor physical width in millimetres
    423 int GetMonitorPhysicalWidth(int monitor)
    424 {
    425     int physicalWidth = 0;
    426 
    427     if (monitor != 0)
    428     {
    429         TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() implemented for first monitor only");
    430     }
    431     else if ((platform.connector) && (platform.modeIndex >= 0))
    432     {
    433         physicalWidth = platform.connector->mmWidth;
    434     }
    435 
    436     return physicalWidth;
    437 }
    438 
    439 // Get selected monitor physical height in millimetres
    440 int GetMonitorPhysicalHeight(int monitor)
    441 {
    442     int physicalHeight = 0;
    443 
    444     if (monitor != 0)
    445     {
    446         TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() implemented for first monitor only");
    447     }
    448     else if ((platform.connector) && (platform.modeIndex >= 0))
    449     {
    450         physicalHeight = platform.connector->mmHeight;
    451     }
    452 
    453     return physicalHeight;
    454 }
    455 
    456 // Get selected monitor refresh rate
    457 int GetMonitorRefreshRate(int monitor)
    458 {
    459     int refresh = 0;
    460 
    461     if ((platform.connector) && (platform.modeIndex >= 0))
    462     {
    463         refresh = platform.connector->modes[platform.modeIndex].vrefresh;
    464     }
    465 
    466     return refresh;
    467 }
    468 
    469 // Get the human-readable, UTF-8 encoded name of the selected monitor
    470 const char *GetMonitorName(int monitor)
    471 {
    472     const char *name = "";
    473 
    474     if (monitor != 0)
    475     {
    476         TRACELOG(LOG_WARNING, "GetMonitorName() implemented for first monitor only");
    477     }
    478     else if ((platform.connector) && (platform.modeIndex >= 0))
    479     {
    480         name = platform.connector->modes[platform.modeIndex].name;
    481     }
    482 
    483     return name;
    484 }
    485 
    486 // Get window position XY on monitor
    487 Vector2 GetWindowPosition(void)
    488 {
    489     return (Vector2){ 0, 0 };
    490 }
    491 
    492 // Get window scale DPI factor for current monitor
    493 Vector2 GetWindowScaleDPI(void)
    494 {
    495     return (Vector2){ 1.0f, 1.0f };
    496 }
    497 
    498 // Set clipboard text content
    499 void SetClipboardText(const char *text)
    500 {
    501     TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
    502 }
    503 
    504 // Get clipboard text content
    505 // NOTE: returned string is allocated and freed by GLFW
    506 const char *GetClipboardText(void)
    507 {
    508     TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
    509     return NULL;
    510 }
    511 
    512 // Show mouse cursor
    513 void ShowCursor(void)
    514 {
    515     CORE.Input.Mouse.cursorHidden = false;
    516 }
    517 
    518 // Hides mouse cursor
    519 void HideCursor(void)
    520 {
    521     CORE.Input.Mouse.cursorHidden = true;
    522 }
    523 
    524 // Enables cursor (unlock cursor)
    525 void EnableCursor(void)
    526 {
    527     // Set cursor position in the middle
    528     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
    529 
    530     platform.cursorRelative = false;
    531     CORE.Input.Mouse.cursorHidden = false;
    532 }
    533 
    534 // Disables cursor (lock cursor)
    535 void DisableCursor(void)
    536 {
    537     // Set cursor position in the middle
    538     SetMousePosition(0, 0);
    539 
    540     platform.cursorRelative = true;
    541     CORE.Input.Mouse.cursorHidden = true;
    542 }
    543 
    544 // Swap back buffer with front buffer (screen drawing)
    545 void SwapScreenBuffer(void)
    546 {
    547     eglSwapBuffers(platform.device, platform.surface);
    548 
    549     if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
    550 
    551     struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface);
    552     if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer");
    553 
    554     uint32_t fb = 0;
    555     int result = drmModeAddFB(platform.fd, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb);
    556     if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result);
    557 
    558     result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]);
    559     if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result);
    560 
    561     if (platform.prevFB)
    562     {
    563         result = drmModeRmFB(platform.fd, platform.prevFB);
    564         if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result);
    565     }
    566 
    567     platform.prevFB = fb;
    568 
    569     if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
    570 
    571     platform.prevBO = bo;
    572 }
    573 
    574 //----------------------------------------------------------------------------------
    575 // Module Functions Definition: Misc
    576 //----------------------------------------------------------------------------------
    577 
    578 // Get elapsed time measure in seconds since InitTimer()
    579 double GetTime(void)
    580 {
    581     double time = 0.0;
    582     struct timespec ts = { 0 };
    583     clock_gettime(CLOCK_MONOTONIC, &ts);
    584     unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
    585 
    586     time = (double)(nanoSeconds - CORE.Time.base)*1e-9;  // Elapsed time since InitTimer()
    587 
    588     return time;
    589 }
    590 
    591 // Open URL with default system browser (if available)
    592 // NOTE: This function is only safe to use if you control the URL given.
    593 // A user could craft a malicious string performing another action.
    594 // Only call this function yourself not with user input or make sure to check the string yourself.
    595 // Ref: https://github.com/raysan5/raylib/issues/686
    596 void OpenURL(const char *url)
    597 {
    598     TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform");
    599 }
    600 
    601 //----------------------------------------------------------------------------------
    602 // Module Functions Definition: Inputs
    603 //----------------------------------------------------------------------------------
    604 
    605 // Set internal gamepad mappings
    606 int SetGamepadMappings(const char *mappings)
    607 {
    608     TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
    609     return 0;
    610 }
    611 
    612 // Set gamepad vibration
    613 void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
    614 {
    615     TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform");
    616 }
    617 
    618 // Set mouse position XY
    619 void SetMousePosition(int x, int y)
    620 {
    621     CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
    622     CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
    623 }
    624 
    625 // Set mouse cursor
    626 void SetMouseCursor(int cursor)
    627 {
    628     TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
    629 }
    630 
    631 // Get physical key name.
    632 const char *GetKeyName(int key)
    633 {
    634     TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
    635     return "";
    636 }
    637 
    638 // Register all input events
    639 void PollInputEvents(void)
    640 {
    641 #if defined(SUPPORT_GESTURES_SYSTEM)
    642     // NOTE: Gestures update must be called every frame to reset gestures correctly
    643     // because ProcessGestureEvent() is just called on an event, not every frame
    644     UpdateGestures();
    645 #endif
    646 
    647     // Reset keys/chars pressed registered
    648     CORE.Input.Keyboard.keyPressedQueueCount = 0;
    649     CORE.Input.Keyboard.charPressedQueueCount = 0;
    650 
    651     // Reset last gamepad button/axis registered state
    652     CORE.Input.Gamepad.lastButtonPressed = 0;       // GAMEPAD_BUTTON_UNKNOWN
    653     //CORE.Input.Gamepad.axisCount = 0;
    654 
    655     // Register previous keys states
    656     for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
    657     {
    658         CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
    659         CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
    660     }
    661 
    662     PollKeyboardEvents();
    663 
    664 #if defined(SUPPORT_SSH_KEYBOARD_RPI)
    665     // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
    666     // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
    667     if (!platform.eventKeyboardMode) ProcessKeyboard();
    668 #endif
    669 
    670     // Check exit key
    671     if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
    672 
    673     // Register previous mouse position
    674     if (platform.cursorRelative) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
    675     else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
    676 
    677     // Register previous mouse states
    678     CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
    679     CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove;
    680     platform.eventWheelMove = (Vector2){ 0.0f, 0.0f };
    681 
    682     for (int i = 0; i < MAX_MOUSE_BUTTONS; i++)
    683     {
    684         CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
    685         CORE.Input.Mouse.currentButtonState[i] = platform.currentButtonStateEvdev[i];
    686         CORE.Input.Touch.currentTouchState[i] = platform.currentButtonStateEvdev[i];
    687     }
    688 
    689     // Register gamepads buttons events
    690     PollGamepadEvents();
    691 
    692     // Register previous touch states
    693     for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
    694 
    695     // Reset touch positions
    696     //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
    697 
    698     // Map touch position to mouse position for convenience
    699     CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
    700 
    701     // Handle the mouse/touch/gestures events:
    702     PollMouseEvents();
    703 }
    704 
    705 //----------------------------------------------------------------------------------
    706 // Module Internal Functions Definition
    707 //----------------------------------------------------------------------------------
    708 
    709 // Initialize platform: graphics, inputs and more
    710 int InitPlatform(void)
    711 {
    712     platform.fd = -1;
    713     platform.connector = NULL;
    714     platform.modeIndex = -1;
    715     platform.crtc = NULL;
    716     platform.gbmDevice = NULL;
    717     platform.gbmSurface = NULL;
    718     platform.prevBO = NULL;
    719     platform.prevFB = 0;
    720 
    721     // Initialize graphic device: display/window and graphic context
    722     //----------------------------------------------------------------------------
    723     CORE.Window.fullscreen = true;
    724     CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
    725 
    726 #if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
    727     platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
    728 #else
    729     TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card");
    730     platform.fd = open("/dev/dri/by-path/platform-gpu-card",  O_RDWR); // VideoCore VI (Raspberry Pi 4)
    731 
    732     if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL))
    733     {
    734         TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1");
    735         platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded
    736     }
    737 
    738     if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL))
    739     {
    740         TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0");
    741         platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
    742     }
    743 #endif
    744 
    745     if (platform.fd == -1)
    746     {
    747         TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card");
    748         return -1;
    749     }
    750 
    751     drmModeRes *res = drmModeGetResources(platform.fd);
    752     if (!res)
    753     {
    754         TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources");
    755         return -1;
    756     }
    757 
    758     TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors);
    759 
    760     for (size_t i = 0; i < res->count_connectors; i++)
    761     {
    762         TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i);
    763 
    764         drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]);
    765         TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes);
    766 
    767         // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected.
    768         // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output.
    769         if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id))
    770         {
    771             TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected");
    772             platform.connector = con;
    773             break;
    774         }
    775         else
    776         {
    777             TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)");
    778             drmModeFreeConnector(con);
    779         }
    780     }
    781 
    782     if (!platform.connector)
    783     {
    784         TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found");
    785         drmModeFreeResources(res);
    786         return -1;
    787     }
    788 
    789     drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id);
    790     if (!enc)
    791     {
    792         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder");
    793         drmModeFreeResources(res);
    794         return -1;
    795     }
    796 
    797     platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id);
    798     if (!platform.crtc)
    799     {
    800         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc");
    801         drmModeFreeEncoder(enc);
    802         drmModeFreeResources(res);
    803         return -1;
    804     }
    805 
    806     // If InitWindow should use the current mode find it in the connector's mode list
    807     if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0))
    808     {
    809         TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode...");
    810 
    811         platform.modeIndex = FindMatchingConnectorMode(platform.connector, &platform.crtc->mode);
    812 
    813         if (platform.modeIndex < 0)
    814         {
    815             TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found");
    816             drmModeFreeEncoder(enc);
    817             drmModeFreeResources(res);
    818             return -1;
    819         }
    820 
    821         CORE.Window.screen.width = CORE.Window.display.width;
    822         CORE.Window.screen.height = CORE.Window.display.height;
    823     }
    824 
    825     const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT;
    826     const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60;
    827 
    828     // Try to find an exact matching mode
    829     platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
    830 
    831     // If nothing found, try to find a nearly matching mode
    832     if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
    833 
    834     // If nothing found, try to find an exactly matching mode including interlaced
    835     if (platform.modeIndex < 0) platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
    836 
    837     // If nothing found, try to find a nearly matching mode including interlaced
    838     if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
    839 
    840     // If nothing found, there is no suitable mode
    841     if (platform.modeIndex < 0)
    842     {
    843         TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode");
    844         drmModeFreeEncoder(enc);
    845         drmModeFreeResources(res);
    846         return -1;
    847     }
    848 
    849     CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay;
    850     CORE.Window.display.height = platform.connector->modes[platform.modeIndex].vdisplay;
    851 
    852     TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name,
    853         platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay,
    854         (platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
    855         platform.connector->modes[platform.modeIndex].vrefresh);
    856 
    857     // Use the width and height of the surface for render
    858     CORE.Window.render.width = CORE.Window.screen.width;
    859     CORE.Window.render.height = CORE.Window.screen.height;
    860 
    861     drmModeFreeEncoder(enc);
    862     enc = NULL;
    863 
    864     drmModeFreeResources(res);
    865     res = NULL;
    866 
    867     platform.gbmDevice = gbm_create_device(platform.fd);
    868     if (!platform.gbmDevice)
    869     {
    870         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device");
    871         return -1;
    872     }
    873 
    874     platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay,
    875         platform.connector->modes[platform.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
    876     if (!platform.gbmSurface)
    877     {
    878         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface");
    879         return -1;
    880     }
    881 
    882     EGLint samples = 0;
    883     EGLint sampleBuffer = 0;
    884     if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
    885     {
    886         samples = 4;
    887         sampleBuffer = 1;
    888         TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
    889     }
    890 
    891     const EGLint framebufferAttribs[] =
    892     {
    893         EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT,      // Type of context support
    894         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,          // Don't use it on Android!
    895         EGL_RED_SIZE, 8,            // RED color bit depth (alternative: 5)
    896         EGL_GREEN_SIZE, 8,          // GREEN color bit depth (alternative: 6)
    897         EGL_BLUE_SIZE, 8,           // BLUE color bit depth (alternative: 5)
    898         EGL_ALPHA_SIZE, 8,        // ALPHA bit depth (required for transparent framebuffer)
    899         //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
    900         EGL_DEPTH_SIZE, 16,         // Depth buffer size (Required to use Depth testing!)
    901         //EGL_STENCIL_SIZE, 8,      // Stencil buffer size
    902         EGL_SAMPLE_BUFFERS, sampleBuffer,    // Activate MSAA
    903         EGL_SAMPLES, samples,       // 4x Antialiasing if activated (Free on MALI GPUs)
    904         EGL_NONE
    905     };
    906 
    907     const EGLint contextAttribs[] = {
    908         EGL_CONTEXT_CLIENT_VERSION, 2,
    909         EGL_NONE
    910     };
    911 
    912     EGLint numConfigs = 0;
    913 
    914     // Get an EGL device connection
    915     platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice);
    916     if (platform.device == EGL_NO_DISPLAY)
    917     {
    918         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
    919         return -1;
    920     }
    921 
    922     // Initialize the EGL device connection
    923     if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
    924     {
    925         // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
    926         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
    927         return -1;
    928     }
    929 
    930     if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs))
    931     {
    932         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
    933         return -1;
    934     }
    935 
    936     TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs);
    937 
    938     EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs));
    939     if (!configs)
    940     {
    941         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
    942         return -1;
    943     }
    944 
    945     EGLint matchingNumConfigs = 0;
    946     if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
    947     {
    948         TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
    949         free(configs);
    950         return -1;
    951     }
    952 
    953     TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs);
    954 
    955     // find the EGL config that matches the previously setup GBM format
    956     int found = 0;
    957     for (EGLint i = 0; i < matchingNumConfigs; ++i)
    958     {
    959         EGLint id = 0;
    960         if (!eglGetConfigAttrib(platform.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
    961         {
    962             TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError());
    963             continue;
    964         }
    965 
    966         if (GBM_FORMAT_ARGB8888 == id)
    967         {
    968             TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i);
    969             platform.config = configs[i];
    970             found = 1;
    971             break;
    972         }
    973     }
    974 
    975     RL_FREE(configs);
    976 
    977     if (!found)
    978     {
    979         TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config");
    980         return -1;
    981     }
    982 
    983     // Set rendering API
    984     eglBindAPI(EGL_OPENGL_ES_API);
    985 
    986     // Create an EGL rendering context
    987     platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
    988     if (platform.context == EGL_NO_CONTEXT)
    989     {
    990         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
    991         return -1;
    992     }
    993 
    994     // Create an EGL window surface
    995     platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL);
    996     if (EGL_NO_SURFACE == platform.surface)
    997     {
    998         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
    999         return -1;
   1000     }
   1001 
   1002     // At this point we need to manage render size vs screen size
   1003     // NOTE: This function use and modify global module variables:
   1004     //  -> CORE.Window.screen.width/CORE.Window.screen.height
   1005     //  -> CORE.Window.render.width/CORE.Window.render.height
   1006     //  -> CORE.Window.screenScale
   1007     SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
   1008 
   1009     // There must be at least one frame displayed before the buffers are swapped
   1010     //eglSwapInterval(platform.device, 1);
   1011 
   1012     EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
   1013 
   1014     // Check surface and context activation
   1015     if (result != EGL_FALSE)
   1016     {
   1017         CORE.Window.ready = true;
   1018 
   1019         CORE.Window.render.width = CORE.Window.screen.width;
   1020         CORE.Window.render.height = CORE.Window.screen.height;
   1021         CORE.Window.currentFbo.width = CORE.Window.render.width;
   1022         CORE.Window.currentFbo.height = CORE.Window.render.height;
   1023 
   1024         TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
   1025         TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
   1026         TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
   1027         TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
   1028         TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
   1029     }
   1030     else
   1031     {
   1032         TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
   1033         return -1;
   1034     }
   1035 
   1036     if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
   1037 
   1038     // If graphic device is no properly initialized, we end program
   1039     if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
   1040     else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
   1041 
   1042     // Set some default window flags
   1043     CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;       // false
   1044     CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;    // false
   1045     CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;     // true
   1046     CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;    // false
   1047 
   1048     // Load OpenGL extensions
   1049     // NOTE: GL procedures address loader is required to load extensions
   1050     rlLoadExtensions(eglGetProcAddress);
   1051     //----------------------------------------------------------------------------
   1052 
   1053     // Initialize timming system
   1054     //----------------------------------------------------------------------------
   1055     // NOTE: timming system must be initialized before the input events system
   1056     InitTimer();
   1057     //----------------------------------------------------------------------------
   1058 
   1059     // Initialize input events system
   1060     //----------------------------------------------------------------------------
   1061     InitEvdevInput();   // Evdev inputs initialization
   1062 
   1063 #if defined(SUPPORT_SSH_KEYBOARD_RPI)
   1064     InitKeyboard();     // Keyboard init (stdin)
   1065 #endif
   1066     //----------------------------------------------------------------------------
   1067 
   1068     // Initialize storage system
   1069     //----------------------------------------------------------------------------
   1070     CORE.Storage.basePath = GetWorkingDirectory();
   1071     //----------------------------------------------------------------------------
   1072 
   1073     TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully");
   1074 
   1075     return 0;
   1076 }
   1077 
   1078 // Close platform
   1079 void ClosePlatform(void)
   1080 {
   1081     if (platform.prevFB)
   1082     {
   1083         drmModeRmFB(platform.fd, platform.prevFB);
   1084         platform.prevFB = 0;
   1085     }
   1086 
   1087     if (platform.prevBO)
   1088     {
   1089         gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
   1090         platform.prevBO = NULL;
   1091     }
   1092 
   1093     if (platform.gbmSurface)
   1094     {
   1095         gbm_surface_destroy(platform.gbmSurface);
   1096         platform.gbmSurface = NULL;
   1097     }
   1098 
   1099     if (platform.gbmDevice)
   1100     {
   1101         gbm_device_destroy(platform.gbmDevice);
   1102         platform.gbmDevice = NULL;
   1103     }
   1104 
   1105     if (platform.crtc)
   1106     {
   1107         if (platform.connector)
   1108         {
   1109             drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id,
   1110                 platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode);
   1111             drmModeFreeConnector(platform.connector);
   1112             platform.connector = NULL;
   1113         }
   1114 
   1115         drmModeFreeCrtc(platform.crtc);
   1116         platform.crtc = NULL;
   1117     }
   1118 
   1119     if (platform.fd != -1)
   1120     {
   1121         close(platform.fd);
   1122         platform.fd = -1;
   1123     }
   1124 
   1125     // Close surface, context and display
   1126     if (platform.device != EGL_NO_DISPLAY)
   1127     {
   1128         if (platform.surface != EGL_NO_SURFACE)
   1129         {
   1130             eglDestroySurface(platform.device, platform.surface);
   1131             platform.surface = EGL_NO_SURFACE;
   1132         }
   1133 
   1134         if (platform.context != EGL_NO_CONTEXT)
   1135         {
   1136             eglDestroyContext(platform.device, platform.context);
   1137             platform.context = EGL_NO_CONTEXT;
   1138         }
   1139 
   1140         eglTerminate(platform.device);
   1141         platform.device = EGL_NO_DISPLAY;
   1142     }
   1143 
   1144     CORE.Window.shouldClose = true;   // Added to force threads to exit when the close window is called
   1145 
   1146     // Close the evdev devices
   1147 
   1148     if (platform.mouseFd != -1)
   1149     {
   1150         close(platform.mouseFd);
   1151         platform.mouseFd = -1;
   1152     }
   1153 
   1154     for (int i = 0; i < platform.gamepadCount; i++)
   1155     {
   1156         close(platform.gamepadStreamFd[i]);
   1157         platform.gamepadStreamFd[i] = -1;
   1158     }
   1159 
   1160     if (platform.keyboardFd != -1)
   1161     {
   1162         close(platform.keyboardFd);
   1163         platform.keyboardFd = -1;
   1164     }
   1165 }
   1166 
   1167 #if defined(SUPPORT_SSH_KEYBOARD_RPI)
   1168 // Initialize Keyboard system (using standard input)
   1169 static void InitKeyboard(void)
   1170 {
   1171     // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor,
   1172     // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
   1173 
   1174     // Save terminal keyboard settings
   1175     tcgetattr(STDIN_FILENO, &platform.defaultSettings);
   1176 
   1177     // Reconfigure terminal with new settings
   1178     struct termios keyboardNewSettings = { 0 };
   1179     keyboardNewSettings = platform.defaultSettings;
   1180 
   1181     // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
   1182     // NOTE: ISIG controls if ^C and ^Z generate break signals or not
   1183     keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG);
   1184     //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
   1185     keyboardNewSettings.c_cc[VMIN] = 1;
   1186     keyboardNewSettings.c_cc[VTIME] = 0;
   1187 
   1188     // Set new keyboard settings (change occurs immediately)
   1189     tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings);
   1190 
   1191     // Save old keyboard mode to restore it at the end
   1192     platform.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0);          // F_GETFL: Get the file access mode and the file status flags
   1193     fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified
   1194 
   1195     // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno)
   1196     int result = ioctl(STDIN_FILENO, KDGKBMODE, &platform.defaultKeyboardMode);
   1197 
   1198     // In case of failure, it could mean a remote keyboard is used (SSH)
   1199     if (result < 0) TRACELOG(LOG_WARNING, "DRM: Failed to change keyboard mode, an SSH keyboard is probably used");
   1200     else
   1201     {
   1202         // Reconfigure keyboard mode to get:
   1203         //    - scancodes (K_RAW)
   1204         //    - keycodes (K_MEDIUMRAW)
   1205         //    - ASCII chars (K_XLATE)
   1206         //    - UNICODE chars (K_UNICODE)
   1207         ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE);  // ASCII chars
   1208     }
   1209 
   1210     // Register keyboard restore when program finishes
   1211     atexit(RestoreKeyboard);
   1212 }
   1213 
   1214 // Restore default keyboard input
   1215 static void RestoreKeyboard(void)
   1216 {
   1217     // Reset to default keyboard settings
   1218     tcsetattr(STDIN_FILENO, TCSANOW, &platform.defaultSettings);
   1219 
   1220     // Reconfigure keyboard to default mode
   1221     fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags);
   1222     ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode);
   1223 }
   1224 
   1225 // Process keyboard inputs
   1226 static void ProcessKeyboard(void)
   1227 {
   1228     #define MAX_KEYBUFFER_SIZE      32      // Max size in bytes to read
   1229 
   1230     // Keyboard input polling (fill keys[256] array with status)
   1231     int bufferByteCount = 0;                        // Bytes available on the buffer
   1232     char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 };    // Max keys to be read at a time
   1233 
   1234     // Read availables keycodes from stdin
   1235     bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE);     // POSIX system call
   1236 
   1237     // Reset pressed keys array (it will be filled below)
   1238     for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
   1239     {
   1240         CORE.Input.Keyboard.currentKeyState[i] = 0;
   1241         CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
   1242     }
   1243 
   1244     // Fill all read bytes (looking for keys)
   1245     for (int i = 0; i < bufferByteCount; i++)
   1246     {
   1247         // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code!
   1248         // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42
   1249         if (keysBuffer[i] == 0x1b)
   1250         {
   1251             // Check if ESCAPE key has been pressed to stop program
   1252             if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1;
   1253             else
   1254             {
   1255                 if (keysBuffer[i + 1] == 0x5b)    // Special function key
   1256                 {
   1257                     if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32))
   1258                     {
   1259                         // Process special function keys (F1 - F12)
   1260                         switch (keysBuffer[i + 3])
   1261                         {
   1262                             case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break;    // raylib KEY_F1
   1263                             case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break;    // raylib KEY_F2
   1264                             case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break;    // raylib KEY_F3
   1265                             case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break;    // raylib KEY_F4
   1266                             case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break;    // raylib KEY_F5
   1267                             case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break;    // raylib KEY_F6
   1268                             case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break;    // raylib KEY_F7
   1269                             case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break;    // raylib KEY_F8
   1270                             case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break;    // raylib KEY_F9
   1271                             case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break;    // raylib KEY_F10
   1272                             case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break;    // raylib KEY_F11
   1273                             case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break;    // raylib KEY_F12
   1274                             default: break;
   1275                         }
   1276 
   1277                         if (keysBuffer[i + 2] == 0x5b) i += 4;
   1278                         else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5;
   1279                     }
   1280                     else
   1281                     {
   1282                         switch (keysBuffer[i + 2])
   1283                         {
   1284                             case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break;    // raylib KEY_UP
   1285                             case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break;    // raylib KEY_DOWN
   1286                             case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break;    // raylib KEY_RIGHT
   1287                             case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break;    // raylib KEY_LEFT
   1288                             default: break;
   1289                         }
   1290 
   1291                         i += 3;  // Jump to next key
   1292                     }
   1293 
   1294                     // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT)
   1295                 }
   1296             }
   1297         }
   1298         else if (keysBuffer[i] == 0x0a)     // raylib KEY_ENTER (don't mix with <linux/input.h> KEY_*)
   1299         {
   1300             CORE.Input.Keyboard.currentKeyState[257] = 1;
   1301 
   1302             CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257;     // Add keys pressed into queue
   1303             CORE.Input.Keyboard.keyPressedQueueCount++;
   1304         }
   1305         else if (keysBuffer[i] == 0x7f)     // raylib KEY_BACKSPACE
   1306         {
   1307             CORE.Input.Keyboard.currentKeyState[259] = 1;
   1308 
   1309             CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257;     // Add keys pressed into queue
   1310             CORE.Input.Keyboard.keyPressedQueueCount++;
   1311         }
   1312         else
   1313         {
   1314             // Translate lowercase a-z letters to A-Z
   1315             if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122))
   1316             {
   1317                 CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1;
   1318             }
   1319             else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1;
   1320 
   1321             CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i];     // Add keys pressed into queue
   1322             CORE.Input.Keyboard.keyPressedQueueCount++;
   1323         }
   1324     }
   1325 }
   1326 #endif  // SUPPORT_SSH_KEYBOARD_RPI
   1327 
   1328 // Initialise user input from evdev(/dev/input/event<N>)
   1329 // this means mouse, keyboard or gamepad devices
   1330 static void InitEvdevInput(void)
   1331 {
   1332     char path[MAX_FILEPATH_LENGTH] = { 0 };
   1333     DIR *directory = NULL;
   1334     struct dirent *entity = NULL;
   1335 
   1336     // Initialise keyboard file descriptor
   1337     platform.keyboardFd = -1;
   1338     platform.mouseFd = -1;
   1339 
   1340     // Reset variables
   1341     for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
   1342     {
   1343         CORE.Input.Touch.position[i].x = -1;
   1344         CORE.Input.Touch.position[i].y = -1;
   1345     }
   1346 
   1347     // Reset keyboard key state
   1348     for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
   1349     {
   1350         CORE.Input.Keyboard.currentKeyState[i] = 0;
   1351         CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
   1352     }
   1353 
   1354     // Open the linux directory of "/dev/input"
   1355     directory = opendir(DEFAULT_EVDEV_PATH);
   1356 
   1357     if (directory)
   1358     {
   1359         while ((entity = readdir(directory)) != NULL)
   1360         {
   1361             if ((strncmp("event", entity->d_name, strlen("event")) == 0) ||     // Search for devices named "event*"
   1362                 (strncmp("mouse", entity->d_name, strlen("mouse")) == 0))       // Search for devices named "mouse*"
   1363             {
   1364                 sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
   1365                 ConfigureEvdevDevice(path);                                     // Configure the device if appropriate
   1366             }
   1367         }
   1368 
   1369         closedir(directory);
   1370     }
   1371     else TRACELOG(LOG_WARNING, "INPUT: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH);
   1372 }
   1373 
   1374 // Identifies a input device and configures it for use if appropriate
   1375 static void ConfigureEvdevDevice(char *device)
   1376 {
   1377     #define BITS_PER_LONG   (8*sizeof(long))
   1378     #define NBITS(x)        ((((x) - 1)/BITS_PER_LONG) + 1)
   1379     #define OFF(x)          ((x)%BITS_PER_LONG)
   1380     #define BIT(x)          (1UL<<OFF(x))
   1381     #define LONG(x)         ((x)/BITS_PER_LONG)
   1382     #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
   1383 
   1384     unsigned long evBits[NBITS(EV_MAX)] = { 0 };
   1385     unsigned long absBits[NBITS(ABS_MAX)] = { 0 };
   1386     unsigned long relBits[NBITS(REL_MAX)] = { 0 };
   1387     unsigned long keyBits[NBITS(KEY_MAX)] = { 0 };
   1388 
   1389     // Open the device
   1390     int fd = open(device, O_RDONLY | O_NONBLOCK);
   1391     if (fd < 0)
   1392     {
   1393         TRACELOG(LOG_WARNING, "DRM: Failed to open input device: %s", device);
   1394         return;
   1395     }
   1396 
   1397     // At this point we have a connection to the device, but we don't yet know what the device is.
   1398     // It could be many things, even as simple as a power button...
   1399     //-------------------------------------------------------------------------------------------------------
   1400 
   1401     // Identify the device
   1402     //-------------------------------------------------------------------------------------------------------
   1403     struct {
   1404         bool exist;
   1405         struct input_absinfo info;
   1406     } absinfo[ABS_CNT] = { 0 };
   1407 
   1408     // These flags aren't really a one of
   1409     // Some devices could have properties we assosciate with keyboards as well as properties
   1410     // we assosciate with mice
   1411     bool isKeyboard = false;
   1412     bool isMouse = false;
   1413     bool isTouch = false;
   1414     bool isGamepad = false;
   1415 
   1416     int absAxisCount = 0;
   1417 
   1418     ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits);
   1419     ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
   1420 
   1421     if (TEST_BIT(evBits, EV_ABS))
   1422     {
   1423         ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
   1424 
   1425         // If the device has an X an Y axis it's either a touch device, a special mouse or a gamepad
   1426         bool hasAbsXY = TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y);
   1427 
   1428         if (hasAbsXY)
   1429         {
   1430             absAxisCount += 2;
   1431 
   1432             absinfo[ABS_X].exist = true;
   1433             absinfo[ABS_Y].exist = true;
   1434 
   1435             ioctl(fd, EVIOCGABS(ABS_X), &absinfo[ABS_X].info);
   1436             ioctl(fd, EVIOCGABS(ABS_Y), &absinfo[ABS_Y].info);
   1437         }
   1438 
   1439         // If it has any of these buttons it's a touch device
   1440         if (hasAbsXY &&
   1441            (TEST_BIT(keyBits, BTN_STYLUS) ||
   1442             TEST_BIT(keyBits, BTN_TOOL_PEN) ||
   1443             TEST_BIT(keyBits, BTN_TOOL_FINGER) ||
   1444             TEST_BIT(keyBits, BTN_TOUCH))) isTouch = true;
   1445 
   1446         // Absolute mice should really only exist with VMWare, but it shouldn't
   1447         // matter if we support them
   1448         else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
   1449 
   1450         // If any of the common joystick axis is present, we assume it's a gamepad
   1451         else
   1452         {
   1453             for (int axis = (hasAbsXY? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++)
   1454             {
   1455                 if (TEST_BIT(absBits, axis))
   1456                 {
   1457                     absinfo[axis].exist = true;
   1458                     isGamepad = true;
   1459                     absAxisCount++;
   1460 
   1461                     ioctl(fd, EVIOCGABS(axis), &absinfo[axis].info);
   1462                 }
   1463             }
   1464         }
   1465 
   1466         // If the device has multitouch axes, it's a touch device
   1467         if (TEST_BIT(absBits, ABS_MT_POSITION_X) &&
   1468             TEST_BIT(absBits, ABS_MT_POSITION_Y)) isTouch = true;
   1469     }
   1470 
   1471     if (TEST_BIT(evBits, EV_REL))
   1472     {
   1473         ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
   1474 
   1475         // If it has any of the gamepad or touch features we tested so far, it's not a mouse
   1476         if (!isTouch &&
   1477             !isGamepad &&
   1478             TEST_BIT(relBits, REL_X) &&
   1479             TEST_BIT(relBits, REL_Y) &&
   1480             TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
   1481     }
   1482 
   1483     if (TEST_BIT(evBits, EV_KEY))
   1484     {
   1485         // The first 32 keys as defined in input-event-codes.h are pretty much
   1486         // exclusive to keyboards, so we can test them using a mask
   1487         // Leave out the first bit to not test KEY_RESERVED
   1488         const unsigned long mask = 0xFFFFFFFE;
   1489         if ((keyBits[0] & mask) == mask) isKeyboard = true;
   1490 
   1491         // If we find any of the common gamepad buttons we assume it's a gamepad
   1492         else
   1493         {
   1494             for (int button = BTN_JOYSTICK; button < BTN_DIGI; ++button)
   1495             {
   1496                 if (TEST_BIT(keyBits, button)) isGamepad = true;
   1497             }
   1498 
   1499             for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40; button++)
   1500             {
   1501                 if (TEST_BIT(keyBits, button)) isGamepad = true;
   1502             }
   1503         }
   1504     }
   1505 
   1506     const char *deviceKindStr = "unknown";
   1507     if (isMouse || isTouch)
   1508     {
   1509         deviceKindStr = "mouse";
   1510         if (platform.mouseFd != -1) close(platform.mouseFd);
   1511         platform.mouseFd = fd;
   1512 
   1513         if (absAxisCount > 0)
   1514         {
   1515             platform.absRange.x = absinfo[ABS_X].info.minimum;
   1516             platform.absRange.width = absinfo[ABS_X].info.maximum - absinfo[ABS_X].info.minimum;
   1517 
   1518             platform.absRange.y = absinfo[ABS_Y].info.minimum;
   1519             platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum;
   1520         }
   1521     }
   1522     else if (isGamepad && !isMouse && !isKeyboard && platform.gamepadCount < MAX_GAMEPADS)
   1523     {
   1524         deviceKindStr = "gamepad";
   1525         int index = platform.gamepadCount++;
   1526 
   1527         platform.gamepadStreamFd[index] = fd;
   1528         CORE.Input.Gamepad.ready[index] = true;
   1529 
   1530         ioctl(platform.gamepadStreamFd[index], EVIOCGNAME(64), &CORE.Input.Gamepad.name[index]);
   1531         CORE.Input.Gamepad.axisCount[index] = absAxisCount;
   1532 
   1533         if (absAxisCount > 0)
   1534         {
   1535             // TODO / NOTE
   1536             // So gamepad axis (as in the actual linux joydev.c) are just simply enumerated
   1537             // and (at least for some input drivers like xpat) it's convention to use
   1538             // ABS_X, ABX_Y for one joystick ABS_RX, ABS_RY for the other and the Z axes for the
   1539             // shoulder buttons
   1540             // If these are now enumerated you get LJOY_X, LJOY_Y, LEFT_SHOULDERB, RJOY_X, ...
   1541             // That means they don't match the GamepadAxis enum
   1542             // This could be fixed
   1543             int axisIndex = 0;
   1544             for (int axis = ABS_X; axis < ABS_PRESSURE; axis++)
   1545             {
   1546                 if (absinfo[axis].exist)
   1547                 {
   1548                     platform.gamepadAbsAxisRange[index][axisIndex][0] = absinfo[axisIndex].info.minimum;
   1549                     platform.gamepadAbsAxisRange[index][axisIndex][1] = absinfo[axisIndex].info.maximum - absinfo[axisIndex].info.minimum;
   1550 
   1551                     platform.gamepadAbsAxisMap[index][axis] = axisIndex;
   1552                     axisIndex++;
   1553                 }
   1554             }
   1555         }
   1556     }
   1557     else if (isKeyboard && (platform.keyboardFd == -1))
   1558     {
   1559         deviceKindStr = "keyboard";
   1560         platform.keyboardFd = fd;
   1561     }
   1562     else
   1563     {
   1564         close(fd);
   1565         return;
   1566     }
   1567 
   1568     TRACELOG(LOG_INFO, "INPUT: Initialized input device %s as %s", device, deviceKindStr);
   1569 }
   1570 
   1571 // Poll and process evdev keyboard events
   1572 static void PollKeyboardEvents(void)
   1573 {
   1574     int fd = platform.keyboardFd;
   1575     if (fd == -1) return;
   1576 
   1577     struct input_event event = { 0 };
   1578     int keycode = -1;
   1579 
   1580     // Try to read data from the keyboard and only continue if successful
   1581     while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
   1582     {
   1583         // Check if the event is a key event
   1584         if (event.type != EV_KEY) continue;
   1585 
   1586 #if defined(SUPPORT_SSH_KEYBOARD_RPI)
   1587         // If the event was a key, we know a working keyboard is connected, so disable the SSH keyboard
   1588         platform.eventKeyboardMode = true;
   1589 #endif
   1590 
   1591         // Keyboard keys appear for codes 1 to 255, ignore everthing else
   1592         if ((event.code >= 1) && (event.code <= 255))
   1593         {
   1594 
   1595             // Lookup the scancode in the keymap to get a keycode
   1596             keycode = linuxToRaylibMap[event.code];
   1597 
   1598             // Make sure we got a valid keycode
   1599             if ((keycode > 0) && (keycode < MAX_KEYBOARD_KEYS))
   1600             {
   1601 
   1602                 // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
   1603                 // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL,
   1604                 // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat
   1605                 CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1);
   1606                 CORE.Input.Keyboard.keyRepeatInFrame[keycode] = (event.value == 2);
   1607 
   1608                 // If the key is pressed add it to the queues
   1609                 if (event.value == 1)
   1610                 {
   1611                     if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
   1612                     {
   1613                         CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;
   1614                         CORE.Input.Keyboard.keyPressedQueueCount++;
   1615                     }
   1616 
   1617                     if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
   1618                     {
   1619                         // TODO/FIXME: This is not actually converting to unicode properly because it's not taking things like shift into account
   1620                         CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = evkeyToUnicodeLUT[event.code];
   1621                         CORE.Input.Keyboard.charPressedQueueCount++;
   1622                     }
   1623                 }
   1624 
   1625                 TRACELOG(LOG_DEBUG, "INPUT: KEY_%s Keycode(linux): %4i KeyCode(raylib): %4i", (event.value == 0)? "UP  " : "DOWN", event.code, keycode);
   1626             }
   1627         }
   1628     }
   1629 }
   1630 
   1631 // Poll gamepad input events
   1632 static void PollGamepadEvents(void)
   1633 {
   1634     // Read gamepad event
   1635     struct input_event event = { 0 };
   1636 
   1637     for (int i = 0; i < platform.gamepadCount; i++)
   1638     {
   1639         if (!CORE.Input.Gamepad.ready[i]) continue;
   1640 
   1641         // Register previous gamepad states
   1642         for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
   1643 
   1644         while (read(platform.gamepadStreamFd[i], &event, sizeof(event)) == (int)sizeof(event))
   1645         {
   1646             if (event.type == EV_KEY)
   1647             {
   1648                 if (event.code < KEYMAP_SIZE)
   1649                 {
   1650                     short keycodeRaylib = linuxToRaylibMap[event.code];
   1651 
   1652                     TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: KEY_%s Keycode(linux): %4i Keycode(raylib): %4i", i, (event.value == 0)? "UP" : "DOWN", event.code, keycodeRaylib);
   1653 
   1654                     if ((keycodeRaylib != 0) && (keycodeRaylib < MAX_GAMEPAD_BUTTONS))
   1655                     {
   1656                         // 1 - button pressed, 0 - button released
   1657                         CORE.Input.Gamepad.currentButtonState[i][keycodeRaylib] = event.value;
   1658 
   1659                         CORE.Input.Gamepad.lastButtonPressed = (event.value == 1)? keycodeRaylib : GAMEPAD_BUTTON_UNKNOWN;
   1660                     }
   1661                 }
   1662             }
   1663             else if (event.type == EV_ABS)
   1664             {
   1665                 if (event.code < ABS_CNT)
   1666                 {
   1667                     int axisRaylib = platform.gamepadAbsAxisMap[i][event.code];
   1668 
   1669                     TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: Axis: %2i Value: %i", i, axisRaylib, event.value);
   1670 
   1671                     if (axisRaylib < MAX_GAMEPAD_AXIS)
   1672                     {
   1673                         int min = platform.gamepadAbsAxisRange[i][event.code][0];
   1674                         int range = platform.gamepadAbsAxisRange[i][event.code][1];
   1675 
   1676                         // NOTE: Scaling of event.value to get values between -1..1
   1677                         CORE.Input.Gamepad.axisState[i][axisRaylib] = (2*(float)(event.value - min)/range) - 1;
   1678                     }
   1679                 }
   1680             }
   1681         }
   1682     }
   1683 }
   1684 
   1685 // Poll mouse input events
   1686 static void PollMouseEvents(void)
   1687 {
   1688     int fd = platform.mouseFd;
   1689     if (fd == -1) return;
   1690 
   1691     struct input_event event = { 0 };
   1692     int touchAction = -1;           // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
   1693 
   1694     // Try to read data from the mouse/touch/gesture and only continue if successful
   1695     while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
   1696     {
   1697         // Relative movement parsing
   1698         if (event.type == EV_REL)
   1699         {
   1700             if (event.code == REL_X)
   1701             {
   1702                 if (platform.cursorRelative)
   1703                 {
   1704                     CORE.Input.Mouse.currentPosition.x = event.value;
   1705                     CORE.Input.Mouse.previousPosition.x = 0.0f;
   1706                 }
   1707                 else CORE.Input.Mouse.currentPosition.x += event.value;
   1708 
   1709                 CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
   1710                 touchAction = 2;    // TOUCH_ACTION_MOVE
   1711             }
   1712 
   1713             if (event.code == REL_Y)
   1714             {
   1715                 if (platform.cursorRelative)
   1716                 {
   1717                     CORE.Input.Mouse.currentPosition.y = event.value;
   1718                     CORE.Input.Mouse.previousPosition.y = 0.0f;
   1719                 }
   1720                 else CORE.Input.Mouse.currentPosition.y += event.value;
   1721 
   1722                 CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
   1723                 touchAction = 2;    // TOUCH_ACTION_MOVE
   1724             }
   1725 
   1726             if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value;
   1727         }
   1728 
   1729         // Absolute movement parsing
   1730         if (event.type == EV_ABS)
   1731         {
   1732             // Basic movement
   1733             if (event.code == ABS_X)
   1734             {
   1735                 CORE.Input.Mouse.currentPosition.x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;    // Scale according to absRange
   1736                 CORE.Input.Touch.position[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;        // Scale according to absRange
   1737 
   1738                 touchAction = 2;    // TOUCH_ACTION_MOVE
   1739             }
   1740 
   1741             if (event.code == ABS_Y)
   1742             {
   1743                 CORE.Input.Mouse.currentPosition.y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;  // Scale according to absRange
   1744                 CORE.Input.Touch.position[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;      // Scale according to absRange
   1745 
   1746                 touchAction = 2;    // TOUCH_ACTION_MOVE
   1747             }
   1748 
   1749             // Multitouch movement
   1750             if (event.code == ABS_MT_SLOT) platform.touchSlot = event.value;   // Remember the slot number for the folowing events
   1751 
   1752             if (event.code == ABS_MT_POSITION_X)
   1753             {
   1754                 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
   1755             }
   1756 
   1757             if (event.code == ABS_MT_POSITION_Y)
   1758             {
   1759                 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
   1760             }
   1761 
   1762             if (event.code == ABS_MT_TRACKING_ID)
   1763             {
   1764                 if ((event.value < 0) && (platform.touchSlot < MAX_TOUCH_POINTS))
   1765                 {
   1766                     // Touch has ended for this point
   1767                     CORE.Input.Touch.position[platform.touchSlot].x = -1;
   1768                     CORE.Input.Touch.position[platform.touchSlot].y = -1;
   1769                 }
   1770             }
   1771 
   1772             // Touchscreen tap
   1773             if (event.code == ABS_PRESSURE)
   1774             {
   1775                 int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
   1776 
   1777                 if (!event.value && previousMouseLeftButtonState)
   1778                 {
   1779                     platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
   1780                     touchAction = 0;    // TOUCH_ACTION_UP
   1781                 }
   1782 
   1783                 if (event.value && !previousMouseLeftButtonState)
   1784                 {
   1785                     platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
   1786                     touchAction = 1;    // TOUCH_ACTION_DOWN
   1787                 }
   1788             }
   1789 
   1790         }
   1791 
   1792         // Button parsing
   1793         if (event.type == EV_KEY)
   1794         {
   1795             // Mouse button parsing
   1796             if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
   1797             {
   1798                 platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
   1799 
   1800                 if (event.value > 0) touchAction = 1;   // TOUCH_ACTION_DOWN
   1801                 else touchAction = 0;       // TOUCH_ACTION_UP
   1802             }
   1803 
   1804             if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
   1805             if (event.code == BTN_MIDDLE) platform.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value;
   1806             if (event.code == BTN_SIDE) platform.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value;
   1807             if (event.code == BTN_EXTRA) platform.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value;
   1808             if (event.code == BTN_FORWARD) platform.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value;
   1809             if (event.code == BTN_BACK) platform.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value;
   1810         }
   1811 
   1812         // Screen confinement
   1813         if (!CORE.Input.Mouse.cursorHidden)
   1814         {
   1815             if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
   1816             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;
   1817 
   1818             if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
   1819             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;
   1820         }
   1821 
   1822         // Update touch point count
   1823         CORE.Input.Touch.pointCount = 0;
   1824         for (int i = 0; i < MAX_TOUCH_POINTS; i++)
   1825         {
   1826             if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++;
   1827         }
   1828 
   1829 #if defined(SUPPORT_GESTURES_SYSTEM)
   1830         if (touchAction > -1)
   1831         {
   1832             GestureEvent gestureEvent = { 0 };
   1833 
   1834             gestureEvent.touchAction = touchAction;
   1835             gestureEvent.pointCount = CORE.Input.Touch.pointCount;
   1836 
   1837             for (int i = 0; i < MAX_TOUCH_POINTS; i++)
   1838             {
   1839                 gestureEvent.pointId[i] = i;
   1840                 gestureEvent.position[i] = CORE.Input.Touch.position[i];
   1841             }
   1842 
   1843             ProcessGestureEvent(gestureEvent);
   1844 
   1845             touchAction = -1;
   1846         }
   1847 #endif
   1848     }
   1849 }
   1850 
   1851 // Search matching DRM mode in connector's mode list
   1852 static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode)
   1853 {
   1854     if (NULL == connector) return -1;
   1855     if (NULL == mode) return -1;
   1856 
   1857     // safe bitwise comparison of two modes
   1858     #define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b))? sizeof(a) : sizeof(b))
   1859 
   1860     for (size_t i = 0; i < connector->count_modes; i++)
   1861     {
   1862         TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
   1863             connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
   1864 
   1865         if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i;
   1866     }
   1867 
   1868     return -1;
   1869 
   1870     #undef BINCMP
   1871 }
   1872 
   1873 // Search exactly matching DRM connector mode in connector's list
   1874 static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
   1875 {
   1876     TRACELOG(LOG_TRACE, "DISPLAY: Searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no");
   1877 
   1878     if (NULL == connector) return -1;
   1879 
   1880     for (int i = 0; i < platform.connector->count_modes; i++)
   1881     {
   1882         const drmModeModeInfo *const mode = &platform.connector->modes[i];
   1883 
   1884         TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
   1885 
   1886         if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) continue;
   1887 
   1888         if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i;
   1889     }
   1890 
   1891     TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found");
   1892     return -1;
   1893 }
   1894 
   1895 // Search the nearest matching DRM connector mode in connector's list
   1896 static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
   1897 {
   1898     TRACELOG(LOG_TRACE, "DISPLAY: Searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no");
   1899 
   1900     if (NULL == connector) return -1;
   1901 
   1902     int nearestIndex = -1;
   1903     for (int i = 0; i < platform.connector->count_modes; i++)
   1904     {
   1905         const drmModeModeInfo *const mode = &platform.connector->modes[i];
   1906 
   1907         TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
   1908             (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
   1909 
   1910         if ((mode->hdisplay < width) || (mode->vdisplay < height))
   1911         {
   1912             TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small");
   1913             continue;
   1914         }
   1915 
   1916         if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced))
   1917         {
   1918             TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode");
   1919             continue;
   1920         }
   1921 
   1922         if (nearestIndex < 0)
   1923         {
   1924             nearestIndex = i;
   1925             continue;
   1926         }
   1927 
   1928         const int widthDiff = abs(mode->hdisplay - width);
   1929         const int heightDiff = abs(mode->vdisplay - height);
   1930         const int fpsDiff = abs(mode->vrefresh - fps);
   1931 
   1932         const int nearestWidthDiff = abs(platform.connector->modes[nearestIndex].hdisplay - width);
   1933         const int nearestHeightDiff = abs(platform.connector->modes[nearestIndex].vdisplay - height);
   1934         const int nearestFpsDiff = abs(platform.connector->modes[nearestIndex].vrefresh - fps);
   1935 
   1936         if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) nearestIndex = i;
   1937     }
   1938 
   1939     return nearestIndex;
   1940 }
   1941 
   1942 // EOF