Bläddra i källkod

WARNING: REDESIGN: Move platform specific data to platform submodules #3313

REVIEWED: Defines, macros, types and tweaks
Ray 1 år sedan
förälder
incheckning
d445fdaa19
7 ändrade filer med 519 tillägg och 520 borttagningar
  1. 24 46
      src/rcore.c
  2. 21 128
      src/rcore.h
  3. 86 70
      src/rcore_android.c
  4. 101 88
      src/rcore_desktop.c
  5. 239 159
      src/rcore_drm.c
  6. 47 28
      src/rcore_web.c
  7. 1 1
      src/utils.h

+ 24 - 46
src/rcore.c

@@ -1,33 +1,18 @@
 /**********************************************************************************************
 *
-*   rcore - Basic functions to manage windows, OpenGL context and input on multiple platforms
+*   rcore - Window/display management, Graphic device/context management and input management
 *
 *   PLATFORMS SUPPORTED:
 *       - PLATFORM_DESKTOP: Windows (Win32, Win64)
 *       - PLATFORM_DESKTOP: Linux (X11 desktop mode)
 *       - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
 *       - PLATFORM_DESKTOP: OSX/macOS
+*       - PLATFORM_WEB:     HTML5 (WebAssembly)
+*       - PLATFORM_DRM:     Raspberry Pi 0-5
+*       - PLATFORM_DRM:     Linux native mode (KMS driver)
 *       - PLATFORM_ANDROID: Android (ARM, ARM64)
-*       - PLATFORM_DRM:     Linux native mode, including Raspberry Pi 4 with V3D fkms driver
-*       - PLATFORM_WEB:     HTML5 with WebAssembly
 *
 *   CONFIGURATION:
-*       #define PLATFORM_DESKTOP
-*           Windowing and input system configured for desktop platforms:
-*               Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly
-*
-*       #define PLATFORM_ANDROID
-*           Windowing and input system configured for Android device, app activity managed internally in this module.
-*           NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL
-*
-*       #define PLATFORM_DRM
-*           Windowing and input system configured for DRM native mode (RPI4 and other devices)
-*           graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
-*
-*       #define PLATFORM_WEB
-*           Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js
-*           using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code.
-*
 *       #define SUPPORT_DEFAULT_FONT (default)
 *           Default font is loaded on window initialization to be available for the user to render simple text.
 *           NOTE: If enabled, uses external module functions to load default raylib font (module: text)
@@ -42,11 +27,6 @@
 *       #define SUPPORT_MOUSE_GESTURES
 *           Mouse gestures are directly mapped like touches and processed by gestures system.
 *
-*       #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only)
-*           Reconfigure standard input to receive key inputs, works with SSH connection.
-*           WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other
-*           running processes orblocking the device if not restored properly. Use with care.
-*
 *       #define SUPPORT_BUSY_WAIT_LOOP
 *           Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
 *
@@ -68,7 +48,6 @@
 *           Support automatic generated events, loading and recording of those events when required
 *
 *   DEPENDENCIES:
-*       rglfw    - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX, FreeBSD...)
 *       raymath  - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
 *       camera   - Multiple 3D camera modes (free, orbital, 1st person, 3rd person)
 *       gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
@@ -76,7 +55,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -102,7 +81,7 @@
     #include "config.h"             // Defines module configuration flags
 #endif
 
-#include "rcore.h"
+#include "rcore.h"                  // Defines types and globals
 
 #define RLGL_IMPLEMENTATION
 #include "rlgl.h"                   // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
@@ -142,21 +121,19 @@
 #endif
 
 // Platform specific defines to handle GetApplicationDirectory()
-#if defined (PLATFORM_DESKTOP)
-    #if defined(_WIN32)
-        #ifndef MAX_PATH
-            #define MAX_PATH 1025
-        #endif
-    __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize);
-    __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize);
-    __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default);
-    #elif defined(__linux__)
-        #include <unistd.h>
-    #elif defined(__APPLE__)
-        #include <sys/syslimits.h>
-        #include <mach-o/dyld.h>
-    #endif // OSs
-#endif // PLATFORM_DESKTOP
+#if defined(_WIN32)
+    #ifndef MAX_PATH
+        #define MAX_PATH 1025
+    #endif
+__declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize);
+__declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize);
+__declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default);
+#elif defined(__linux__)
+    #include <unistd.h>
+#elif defined(__APPLE__)
+    #include <sys/syslimits.h>
+    #include <mach-o/dyld.h>
+#endif // OSs
 
 #define _CRT_INTERNAL_NONSTDC_NAMES  1
 #include <sys/stat.h>               // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()]
@@ -165,7 +142,7 @@
     #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
 #endif
 
-#if defined(PLATFORM_DESKTOP) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
+#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
     #define DIRENT_MALLOC RL_MALLOC
     #define DIRENT_FREE RL_FREE
 
@@ -333,7 +310,8 @@ const char *TextFormat(const char *text, ...);       // Formatting of text with
 #elif defined(PLATFORM_ANDROID)
     #include "rcore_android.c"
 #else
-    // Software rendering backend, user needs to provide buffer ;)
+    // TODO: Include your custom platform backend!
+    // i.e software rendering backend or console backend!
 #endif
 
 //----------------------------------------------------------------------------------
@@ -1897,7 +1875,7 @@ bool IsKeyPressed(int key)
     return pressed;
 }
 
-// Check if a key has been pressed again (only PLATFORM_DESKTOP)
+// Check if a key has been pressed again
 bool IsKeyPressedRepeat(int key)
 {
     bool repeat = false;
@@ -2291,7 +2269,7 @@ void InitTimer(void)
     timeBeginPeriod(1);                 // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
 #endif
 
-#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM)
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
     struct timespec now = { 0 };
 
     if (clock_gettime(CLOCK_MONOTONIC, &now) == 0)  // Success

+ 21 - 128
src/rcore.h

@@ -35,64 +35,6 @@
 #ifndef RCORE_H
 #define RCORE_H
 
-#include <stdlib.h>                 // Required for: srand(), rand(), atexit()
-#include <stdio.h>                  // Required for: sprintf() [Used in OpenURL()]
-#include <string.h>                 // Required for: strrchr(), strcmp(), strlen(), memset()
-#include <time.h>                   // Required for: time() [Used in InitTimer()]
-#include <math.h>                   // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
-
-#include "utils.h"                  // Required for: TRACELOG() macros
-
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
-    #define GLFW_INCLUDE_NONE       // Disable the standard OpenGL header inclusion on GLFW3
-                                    // NOTE: Already provided by rlgl implementation (on glad.h)
-    #include "GLFW/glfw3.h"         // GLFW3 library: Windows, OpenGL context and Input management
-                                    // NOTE: GLFW3 already includes gl.h (OpenGL) headers
-#endif
-
-#if defined(PLATFORM_ANDROID)
-    #include <EGL/egl.h>            // Native platform windowing system interface
-    //#include <GLES2/gl2.h>        // OpenGL ES 2.0 library (not required in this module, only in rlgl)
-#endif
-
-#if defined(PLATFORM_DRM)
-
-    #include <fcntl.h>   // POSIX file control definitions - open(), creat(), fcntl()
-    #include <unistd.h>  // POSIX standard function definitions - read(), close(), STDIN_FILENO
-    #include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
-    #include <pthread.h> // POSIX threads management (inputs reading)
-    #include <dirent.h>  // POSIX directory browsing
-
-    #include <sys/ioctl.h>      // Required for: ioctl() - UNIX System call for device-specific input/output operations
-    #include <linux/kd.h>       // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
-    #include <linux/input.h>    // Linux: Keycodes constants definition (KEY_A, ...)
-    #include <linux/joystick.h> // Linux: Joystick support library
-
-    #include <gbm.h>         // Generic Buffer Management (native platform for EGL on DRM)
-    #include <xf86drm.h>     // Direct Rendering Manager user-level library interface
-    #include <xf86drmMode.h> // Direct Rendering Manager mode setting (KMS) interface
-
-    #include "EGL/egl.h"    // Native platform windowing system interface
-    #include "EGL/eglext.h" // EGL extensions
-
-    typedef struct
-    {
-        pthread_t threadId; // Event reading thread id
-        int fd;             // File descriptor to the device it is assigned to
-        int eventNum;       // Number of 'event<N>' device
-        Rectangle absRange; // Range of values for absolute pointing devices (touchscreens)
-        int touchSlot;      // Hold the touch slot number of the currently being sent multitouch block
-        bool isMouse;       // True if device supports relative X Y movements
-        bool isTouch;       // True if device supports absolute X Y movements and has BTN_TOUCH
-        bool isMultitouch;  // True if device supports multiple absolute movevents and has BTN_TOUCH
-        bool isKeyboard;    // True if device has letter keycodes
-        bool isGamepad;     // True if device has gamepad buttons
-    } InputEventWorker;
-
-#endif
-
-// TODO: PROVIDE A HEADER TO BE USED BY ALL THE rcore_* IMPLEMENTATIONS
-
 #include "raylib.h"
 
 #include "rlgl.h"
@@ -100,16 +42,17 @@
 #define RAYMATH_IMPLEMENTATION
 #include "raymath.h"
 
+#include "utils.h"                  // Required for: TRACELOG() macros
+
+#include <stdlib.h>                 // Required for: srand(), rand(), atexit()
+#include <stdio.h>                  // Required for: sprintf() [Used in OpenURL()]
+#include <string.h>                 // Required for: strrchr(), strcmp(), strlen(), memset()
+#include <time.h>                   // Required for: time() [Used in InitTimer()]
+#include <math.h>                   // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
+
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-#if defined(PLATFORM_DRM)
-    #define USE_LAST_TOUCH_DEVICE       // When multiple touchscreens are connected, only use the one with the highest event<N> number
-
-    #define DEFAULT_GAMEPAD_DEV    "/dev/input/js"  // Gamepad input (base dev for all gamepads: js0, js1, ...)
-    #define DEFAULT_EVDEV_PATH       "/dev/input/"  // Path to the linux input events
-#endif
-
 #ifndef MAX_FILEPATH_CAPACITY
     #define MAX_FILEPATH_CAPACITY       8192        // Maximum capacity for filepath
 #endif
@@ -152,12 +95,6 @@
 #define FLAG_TOGGLE(n, f) ((n) ^= (f))
 #define FLAG_CHECK(n, f) ((n) & (f))
 
-// TODO: HACK: Added flag if not provided by GLFW when using external library
-// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev
-#if !defined(GLFW_MOUSE_PASSTHROUGH)
-    #define GLFW_MOUSE_PASSTHROUGH      0x0002000D
-#endif
-
 #if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L)
     #undef _POSIX_C_SOURCE
     #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
@@ -172,25 +109,6 @@ typedef struct { unsigned int width; unsigned int height; } Size;
 // Core global state context data
 typedef struct CoreData {
     struct {
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
-        GLFWwindow *handle;                 // GLFW window handle (graphic device)
-#endif
-#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM)
-#if defined(PLATFORM_DRM)
-        int fd;                             // File descriptor for /dev/dri/...
-        drmModeConnector *connector;        // Direct Rendering Manager (DRM) mode connector
-        drmModeCrtc *crtc;                  // CRT Controller
-        int modeIndex;                      // Index of the used mode of connector->modes
-        struct gbm_device *gbmDevice;       // GBM device
-        struct gbm_surface *gbmSurface;     // GBM surface
-        struct gbm_bo *prevBO;              // Previous GBM buffer object (during frame swapping)
-        uint32_t prevFB;                    // Previous GBM framebufer (during frame swapping)
-#endif  // PLATFORM_DRM
-        EGLDisplay device;                  // Native display device (physical screen connection)
-        EGLSurface surface;                 // Surface to draw on, framebuffers (connected to context)
-        EGLContext context;                 // Graphic context, mode in which drawing can be done
-        EGLConfig config;                   // Graphic config
-#endif
         const char *title;                  // Window text title const pointer
         unsigned int flags;                 // Configuration flags (bit based), keeps window state
         bool ready;                         // Check if window has been initialized successfully
@@ -215,45 +133,27 @@ typedef struct CoreData {
 
         char **dropFilepaths;               // Store dropped files paths pointers (provided by GLFW)
         unsigned int dropFileCount;         // Count dropped files strings
-
+        
     } Window;
-#if defined(PLATFORM_ANDROID)
-    struct {
-        bool appEnabled;                    // Flag to detect if app is active ** = true
-        struct android_app *app;            // Android activity
-        struct android_poll_source *source; // Android events polling source
-        bool contextRebindRequired;         // Used to know context rebind required
-    } Android;
-#endif
     struct {
         const char *basePath;               // Base path for data storage
+        
     } Storage;
     struct {
-#if defined(PLATFORM_DRM)
-        InputEventWorker eventWorker[10];   // List of worker threads for every monitored "/dev/input/event<N>"
-#endif
         struct {
             int exitKey;                    // Default exit key
-            char currentKeyState[MAX_KEYBOARD_KEYS];        // Registers current frame key state
-            char previousKeyState[MAX_KEYBOARD_KEYS];       // Registers previous frame key state
+            char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state
+            char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state
+            
             // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially
-            char keyRepeatInFrame[MAX_KEYBOARD_KEYS];       // Registers key repeats for current frame.
+            char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame.
 
-            int keyPressedQueue[MAX_KEY_PRESSED_QUEUE];     // Input keys queue
+            int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue
             int keyPressedQueueCount;       // Input keys queue count
 
-            int charPressedQueue[MAX_CHAR_PRESSED_QUEUE];   // Input characters queue (unicode)
+            int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode)
             int charPressedQueueCount;      // Input characters queue count
 
-#if defined(PLATFORM_DRM)
-            int defaultMode;                // Default keyboard mode
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
-            bool evtMode;                   // Keyboard in event mode
-#endif
-            int defaultFileFlags;           // Default IO file flags
-            struct termios defaultSettings; // Default keyboard settings
-            int fd;                         // File descriptor for the evdev keyboard
-#endif
         } Keyboard;
         struct {
             Vector2 offset;                 // Mouse offset
@@ -269,11 +169,7 @@ typedef struct CoreData {
             char previousButtonState[MAX_MOUSE_BUTTONS];    // Registers previous mouse button state
             Vector2 currentWheelMove;       // Registers current mouse wheel variation
             Vector2 previousWheelMove;      // Registers previous mouse wheel variation
-#if defined(PLATFORM_DRM)
-            Vector2 eventWheelMove;         // Registers the event mouse wheel variation
-            // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update
-            char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab
-#endif
+
         } Mouse;
         struct {
             int pointCount;                             // Number of touch points active
@@ -281,6 +177,7 @@ typedef struct CoreData {
             Vector2 position[MAX_TOUCH_POINTS];         // Touch position on screen
             char currentTouchState[MAX_TOUCH_POINTS];   // Registers current touch state
             char previousTouchState[MAX_TOUCH_POINTS];  // Registers previous touch state
+            
         } Touch;
         struct {
             int lastButtonPressed;          // Register last gamepad button pressed
@@ -290,10 +187,7 @@ typedef struct CoreData {
             char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];     // Current gamepad buttons state
             char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];    // Previous gamepad buttons state
             float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS];                // Gamepad axis state
-#if defined(PLATFORM_DRM)
-            pthread_t threadId;             // Gamepad reading thread id
-            int streamId[MAX_GAMEPADS];     // Gamepad device file descriptor
-#endif
+
         } Gamepad;
     } Input;
     struct {
@@ -303,10 +197,9 @@ typedef struct CoreData {
         double draw;                        // Time measure for frame draw
         double frame;                       // Time measure for one frame
         double target;                      // Desired time for one frame, if 0 not applied
-#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM)
-        unsigned long long int base;        // Base time measure for hi-res timer
-#endif
+        unsigned long long int base;        // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM)
         unsigned int frameCounter;          // Frame counter
+        
     } Time;
 } CoreData;
 

+ 86 - 70
src/rcore_android.c

@@ -53,15 +53,31 @@
 #include <android_native_app_glue.h>    // Required for: android_app struct and activity management
 #include <jni.h>                        // Required for: JNIEnv and JavaVM [Used in OpenURL()]
 
+#include <EGL/egl.h>                    // Native platform windowing system interface
+    
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
-//...
+typedef struct {
+    // Application data
+    struct android_app *app;            // Android activity
+    struct android_poll_source *source; // Android events polling source
+    bool appEnabled;                    // Flag to detect if app is active ** = true
+    bool contextRebindRequired;         // Used to know context rebind required
+    
+    // Display data
+    EGLDisplay device;                  // Native display device (physical screen connection)
+    EGLSurface surface;                 // Surface to draw on, framebuffers (connected to context)
+    EGLContext context;                 // Graphic context, mode in which drawing can be done
+    EGLConfig config;                   // Graphic config
+} PlatformData;
 
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-extern CoreData CORE;           // Global CORE state context
+extern CoreData CORE;                   // Global CORE state context
+
+static PlatformData platform = { 0 };   // Platform specific data
 
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
@@ -89,7 +105,7 @@ extern int main(int argc, char *argv[]);
 void android_main(struct android_app *app)
 {
     char arg0[] = "raylib";     // NOTE: argv[] are mutable
-    CORE.Android.app = app;
+    platform.app = app;
 
     // NOTE: Return from main is ignored
     (void)main(1, (char *[]) { arg0, NULL });
@@ -104,9 +120,9 @@ void android_main(struct android_app *app)
     // Waiting for application events before complete finishing
     while (!app->destroyRequested)
     {
-        while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0)
+        while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&platform.source)) >= 0)
         {
-            if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source);
+            if (platform.source != NULL) platform.source->process(app, platform.source);
         }
     }
 }
@@ -114,7 +130,7 @@ void android_main(struct android_app *app)
 // NOTE: Add this to header (if apps really need it)
 struct android_app *GetAndroidApp(void)
 {
-    return CORE.Android.app;
+    return platform.app;
 }
 
 // Initialize window and OpenGL context
@@ -169,9 +185,9 @@ void InitWindow(int width, int height, const char *title)
     CORE.Window.currentFbo.height = height;
 
     // Set desired windows flags before initializing anything
-    ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0);  //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
+    ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0);  //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
 
-    int orientation = AConfiguration_getOrientation(CORE.Android.app->config);
+    int orientation = AConfiguration_getOrientation(platform.app->config);
 
     if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait");
     else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape");
@@ -179,32 +195,32 @@ void InitWindow(int width, int height, const char *title)
     // TODO: Automatic orientation doesn't seem to work
     if (width <= height)
     {
-        AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT);
+        AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT);
         TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait");
     }
     else
     {
-        AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND);
+        AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND);
         TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape");
     }
 
-    //AConfiguration_getDensity(CORE.Android.app->config);
-    //AConfiguration_getKeyboard(CORE.Android.app->config);
-    //AConfiguration_getScreenSize(CORE.Android.app->config);
-    //AConfiguration_getScreenLong(CORE.Android.app->config);
+    //AConfiguration_getDensity(platform.app->config);
+    //AConfiguration_getKeyboard(platform.app->config);
+    //AConfiguration_getScreenSize(platform.app->config);
+    //AConfiguration_getScreenLong(platform.app->config);
 
     // Initialize App command system
     // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()...
-    CORE.Android.app->onAppCmd = AndroidCommandCallback;
+    platform.app->onAppCmd = AndroidCommandCallback;
 
     // Initialize input events system
-    CORE.Android.app->onInputEvent = AndroidInputCallback;
+    platform.app->onInputEvent = AndroidInputCallback;
 
     // Initialize assets manager
-    InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath);
+    InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath);
 
     // Initialize base path for storage
-    CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath;
+    CORE.Storage.basePath = platform.app->activity->internalDataPath;
 
     TRACELOG(LOG_INFO, "ANDROID: App initialized successfully");
 
@@ -216,13 +232,13 @@ void InitWindow(int width, int height, const char *title)
     while (!CORE.Window.ready)
     {
         // Process events loop
-        while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0)
+        while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0)
         {
             // Process this event
-            if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source);
+            if (platform.source != NULL) platform.source->process(platform.app, platform.source);
 
             // NOTE: Never close window, native activity is controlled by the system!
-            //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true;
+            //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true;
         }
     }
 }
@@ -250,24 +266,24 @@ void CloseWindow(void)
 #endif
 
     // Close surface, context and display
-    if (CORE.Window.device != EGL_NO_DISPLAY)
+    if (platform.device != EGL_NO_DISPLAY)
     {
-        eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
 
-        if (CORE.Window.surface != EGL_NO_SURFACE)
+        if (platform.surface != EGL_NO_SURFACE)
         {
-            eglDestroySurface(CORE.Window.device, CORE.Window.surface);
-            CORE.Window.surface = EGL_NO_SURFACE;
+            eglDestroySurface(platform.device, platform.surface);
+            platform.surface = EGL_NO_SURFACE;
         }
 
-        if (CORE.Window.context != EGL_NO_CONTEXT)
+        if (platform.context != EGL_NO_CONTEXT)
         {
-            eglDestroyContext(CORE.Window.device, CORE.Window.context);
-            CORE.Window.context = EGL_NO_CONTEXT;
+            eglDestroyContext(platform.device, platform.context);
+            platform.context = EGL_NO_CONTEXT;
         }
 
-        eglTerminate(CORE.Window.device);
-        CORE.Window.device = EGL_NO_DISPLAY;
+        eglTerminate(platform.device);
+        platform.device = EGL_NO_DISPLAY;
     }
 
 #if defined(SUPPORT_EVENTS_AUTOMATION)
@@ -306,7 +322,7 @@ bool IsWindowMaximized(void)
 // Check if window has the focus
 bool IsWindowFocused(void)
 {
-    return CORE.Android.appEnabled;
+    return platform.appEnabled;
 }
 
 // Check if window has been resizedLastFrame
@@ -595,7 +611,7 @@ void OpenURL(const char *url)
     else
     {
         JNIEnv *env = NULL;
-        JavaVM *vm = CORE.Android.app->activity->vm;
+        JavaVM *vm = platform.app->activity->vm;
         (*vm)->AttachCurrentThread(vm, &env, NULL);
 
         jstring urlString = (*env)->NewStringUTF(env, url);
@@ -612,7 +628,7 @@ void OpenURL(const char *url)
         (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri);
         jclass activityClass = (*env)->FindClass(env, "android/app/Activity");
         jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V");
-        (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent);
+        (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent);
 
         (*vm)->DetachCurrentThread(vm);
     }
@@ -713,7 +729,7 @@ Vector2 GetTouchPosition(int index)
 // Swap back buffer with front buffer (screen drawing)
 void SwapScreenBuffer(void)
 {
-    eglSwapBuffers(CORE.Window.device, CORE.Window.surface);
+    eglSwapBuffers(platform.device, platform.surface);
 }
 
 // Register all input events
@@ -756,17 +772,17 @@ void PollInputEvents(void)
     int pollEvents = 0;
 
     // Poll Events (registered events)
-    // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled)
-    while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0)
+    // NOTE: Activity is paused if not enabled (platform.appEnabled)
+    while ((pollResult = ALooper_pollAll(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) >= 0)
     {
         // Process this event
-        if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source);
+        if (platform.source != NULL) platform.source->process(platform.app, platform.source);
 
         // NOTE: Never close window, native activity is controlled by the system!
-        if (CORE.Android.app->destroyRequested != 0)
+        if (platform.app->destroyRequested != 0)
         {
             //CORE.Window.shouldClose = true;
-            //ANativeActivity_finish(CORE.Android.app->activity);
+            //ANativeActivity_finish(platform.app->activity);
         }
     }
 }
@@ -829,15 +845,15 @@ static bool InitGraphicsDevice(int width, int height)
     EGLint numConfigs = 0;
 
     // Get an EGL device connection
-    CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (CORE.Window.device == EGL_NO_DISPLAY)
+    platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (platform.device == EGL_NO_DISPLAY)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
         return false;
     }
 
     // Initialize the EGL device connection
-    if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE)
+    if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
     {
         // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
@@ -845,14 +861,14 @@ static bool InitGraphicsDevice(int width, int height)
     }
 
     // Get an appropriate EGL framebuffer configuration
-    eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs);
+    eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs);
 
     // Set rendering API
     eglBindAPI(EGL_OPENGL_ES_API);
 
     // Create an EGL rendering context
-    CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs);
-    if (CORE.Window.context == EGL_NO_CONTEXT)
+    platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
+    if (platform.context == EGL_NO_CONTEXT)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
         return false;
@@ -864,7 +880,7 @@ static bool InitGraphicsDevice(int width, int height)
 
     // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
     // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
-    eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
+    eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
 
     // At this point we need to manage render size vs screen size
     // NOTE: This function use and modify global module variables:
@@ -873,15 +889,15 @@ static bool InitGraphicsDevice(int width, int height)
     //  -> CORE.Window.screenScale
     SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
 
-    ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
-    //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat);       // Force use of native display size
+    ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
+    //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat);       // Force use of native display size
 
-    CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL);
+    platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL);
 
     // There must be at least one frame displayed before the buffers are swapped
-    //eglSwapInterval(CORE.Window.device, 1);
+    //eglSwapInterval(platform.device, 1);
 
-    if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE)
+    if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface");
         return false;
@@ -933,11 +949,11 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
         {
             if (app->window != NULL)
             {
-                if (CORE.Android.contextRebindRequired)
+                if (platform.contextRebindRequired)
                 {
                     // Reset screen scaling to full display size
                     EGLint displayFormat = 0;
-                    eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
+                    eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
 
                     // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the
                     // context rebinding if the screen is scaled unless offsets are added. There's probably a more
@@ -948,15 +964,15 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
                         displayFormat);
 
                     // Recreate display surface and re-attach OpenGL context
-                    CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL);
-                    eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context);
+                    platform.surface = eglCreateWindowSurface(platform.device, platform.config, app->window, NULL);
+                    eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
 
-                    CORE.Android.contextRebindRequired = false;
+                    platform.contextRebindRequired = false;
                 }
                 else
                 {
-                    CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window);
-                    CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window);
+                    CORE.Window.display.width = ANativeWindow_getWidth(platform.app->window);
+                    CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window);
 
                     // Initialize graphics device (display device and OpenGL context)
                     InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height);
@@ -997,13 +1013,13 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
         } break;
         case APP_CMD_GAINED_FOCUS:
         {
-            CORE.Android.appEnabled = true;
+            platform.appEnabled = true;
             //ResumeMusicStream();
         } break;
         case APP_CMD_PAUSE: break;
         case APP_CMD_LOST_FOCUS:
         {
-            CORE.Android.appEnabled = false;
+            platform.appEnabled = false;
             //PauseMusicStream();
         } break;
         case APP_CMD_TERM_WINDOW:
@@ -1012,19 +1028,19 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
             // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming.
             // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
             // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :(
-            if (CORE.Window.device != EGL_NO_DISPLAY)
+            if (platform.device != EGL_NO_DISPLAY)
             {
-                eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+                eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
 
-                if (CORE.Window.surface != EGL_NO_SURFACE)
+                if (platform.surface != EGL_NO_SURFACE)
                 {
-                    eglDestroySurface(CORE.Window.device, CORE.Window.surface);
-                    CORE.Window.surface = EGL_NO_SURFACE;
+                    eglDestroySurface(platform.device, platform.surface);
+                    platform.surface = EGL_NO_SURFACE;
                 }
 
-                CORE.Android.contextRebindRequired = true;
+                platform.contextRebindRequired = true;
             }
-            // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY'
+            // If 'platform.device' is already set to 'EGL_NO_DISPLAY'
             // this means that the user has already called 'CloseWindow()'
 
         } break;
@@ -1033,8 +1049,8 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
         case APP_CMD_DESTROY: break;
         case APP_CMD_CONFIG_CHANGED:
         {
-            //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager);
-            //print_cur_config(CORE.Android.app);
+            //AConfiguration_fromAssetManager(platform.app->config, platform.app->activity->assetManager);
+            //print_cur_config(platform.app);
 
             // Check screen orientation here!
         } break;

+ 101 - 88
src/rcore_desktop.c

@@ -85,15 +85,28 @@
     #include "GLFW/glfw3native.h"       // Required for: glfwGetCocoaWindow()
 #endif
 
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// TODO: HACK: Added flag if not provided by GLFW when using external library
+// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev
+#if !defined(GLFW_MOUSE_PASSTHROUGH)
+    #define GLFW_MOUSE_PASSTHROUGH      0x0002000D
+#endif
+
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
-//...
+typedef struct {
+    GLFWwindow *handle;                 // GLFW window handle (graphic device)
+} PlatformData;
 
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-extern CoreData CORE;           // Global CORE state context
+extern CoreData CORE;                   // Global CORE state context
+
+static PlatformData platform = { 0 };   // Platform specific data
 
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
@@ -255,7 +268,7 @@ void CloseWindow(void)
 
     rlglClose();                // De-init rlgl
 
-    glfwDestroyWindow(CORE.Window.handle);
+    glfwDestroyWindow(platform.handle);
     glfwTerminate();
 
 #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
@@ -279,10 +292,10 @@ bool WindowShouldClose(void)
         // While window minimized, stop loop execution
         while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents();
 
-        CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle);
+        CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle);
 
         // Reset close status for next frame
-        glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE);
+        glfwSetWindowShouldClose(platform.handle, GLFW_FALSE);
 
         return CORE.Window.shouldClose;
     }
@@ -325,7 +338,7 @@ void ToggleFullscreen(void)
     if (!CORE.Window.fullscreen)
     {
         // Store previous window position (in case we exit fullscreen)
-        glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y);
+        glfwGetWindowPos(platform.handle, &CORE.Window.position.x, &CORE.Window.position.y);
 
         int monitorCount = 0;
         int monitorIndex = GetCurrentMonitor();
@@ -341,14 +354,14 @@ void ToggleFullscreen(void)
             CORE.Window.fullscreen = false;
             CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
 
-            glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
+            glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
         }
         else
         {
             CORE.Window.fullscreen = true;
             CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
 
-            glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
+            glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
         }
 
     }
@@ -357,7 +370,7 @@ void ToggleFullscreen(void)
         CORE.Window.fullscreen = false;
         CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
 
-        glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
+        glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
     }
 
     // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
@@ -368,9 +381,9 @@ void ToggleFullscreen(void)
 // Set window state: maximized, if resizable
 void MaximizeWindow(void)
 {
-    if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE)
+    if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
     {
-        glfwMaximizeWindow(CORE.Window.handle);
+        glfwMaximizeWindow(platform.handle);
         CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
     }
 }
@@ -379,16 +392,16 @@ void MaximizeWindow(void)
 void MinimizeWindow(void)
 {
     // NOTE: Following function launches callback that sets appropriate flag!
-    glfwIconifyWindow(CORE.Window.handle);
+    glfwIconifyWindow(platform.handle);
 }
 
 // Set window state: not minimized/maximized
 void RestoreWindow(void)
 {
-    if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE)
+    if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
     {
         // Restores the specified window if it was previously iconified (minimized) or maximized
-        glfwRestoreWindow(CORE.Window.handle);
+        glfwRestoreWindow(platform.handle);
         CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
         CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
     }
@@ -420,13 +433,13 @@ void ToggleBorderlessWindowed(void)
             {
                 // Store screen position and size
                 // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here
-                if (!wasOnFullscreen) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y);
+                if (!wasOnFullscreen) glfwGetWindowPos(platform.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y);
                 CORE.Window.previousScreen = CORE.Window.screen;
 
                 // Set undecorated and topmost modes and flags
-                glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE);
+                glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
                 CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
-                glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE);
+                glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE);
                 CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
 
                 // Get monitor position and size
@@ -437,29 +450,29 @@ void ToggleBorderlessWindowed(void)
                 const int monitorHeight = mode->height;
 
                 // Set screen position and size
-                glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY);
-                glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight);
+                glfwSetWindowPos(platform.handle, monitorPosX, monitorPosY);
+                glfwSetWindowSize(platform.handle, monitorWidth, monitorHeight);
 
                 // Refocus window
-                glfwFocusWindow(CORE.Window.handle);
+                glfwFocusWindow(platform.handle);
 
                 CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
             }
             else
             {
                 // Remove topmost and undecorated modes and flags
-                glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE);
+                glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE);
                 CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
-                glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE);
+                glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
                 CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
 
                 // Return previous screen size and position
                 // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly
-                glfwSetWindowSize(CORE.Window.handle,  CORE.Window.previousScreen.width, CORE.Window.previousScreen.height);
-                glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y);
+                glfwSetWindowSize(platform.handle,  CORE.Window.previousScreen.width, CORE.Window.previousScreen.height);
+                glfwSetWindowPos(platform.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y);
 
                 // Refocus window
-                glfwFocusWindow(CORE.Window.handle);
+                glfwFocusWindow(platform.handle);
 
                 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
             }
@@ -498,21 +511,21 @@ void SetWindowState(unsigned int flags)
     // State change: FLAG_WINDOW_RESIZABLE
     if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE);
+        glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
         CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
     }
 
     // State change: FLAG_WINDOW_UNDECORATED
     if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE);
+        glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
         CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
     }
 
     // State change: FLAG_WINDOW_HIDDEN
     if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
     {
-        glfwHideWindow(CORE.Window.handle);
+        glfwHideWindow(platform.handle);
         CORE.Window.flags |= FLAG_WINDOW_HIDDEN;
     }
 
@@ -533,14 +546,14 @@ void SetWindowState(unsigned int flags)
     // State change: FLAG_WINDOW_UNFOCUSED
     if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE);
+        glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE);
         CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
     }
 
     // State change: FLAG_WINDOW_TOPMOST
     if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE);
+        glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE);
         CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
     }
 
@@ -567,7 +580,7 @@ void SetWindowState(unsigned int flags)
     // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
     if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
+        glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
         CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH;
     }
 
@@ -613,14 +626,14 @@ void ClearWindowState(unsigned int flags)
     // State change: FLAG_WINDOW_RESIZABLE
     if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE);
+        glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
         CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
     }
 
     // State change: FLAG_WINDOW_HIDDEN
     if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
     {
-        glfwShowWindow(CORE.Window.handle);
+        glfwShowWindow(platform.handle);
         CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
     }
 
@@ -639,21 +652,21 @@ void ClearWindowState(unsigned int flags)
     // State change: FLAG_WINDOW_UNDECORATED
     if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE);
+        glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
         CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
     }
 
     // State change: FLAG_WINDOW_UNFOCUSED
     if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE);
+        glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE);
         CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
     }
 
     // State change: FLAG_WINDOW_TOPMOST
     if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE);
+        glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE);
         CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
     }
 
@@ -680,7 +693,7 @@ void ClearWindowState(unsigned int flags)
     // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
     if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
     {
-        glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
+        glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
         CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH;
     }
 
@@ -705,7 +718,7 @@ void SetWindowIcon(Image image)
     if (image.data == NULL)
     {
         // Revert to the default window icon, pass in an empty image array
-        glfwSetWindowIcon(CORE.Window.handle, 0, NULL);
+        glfwSetWindowIcon(platform.handle, 0, NULL);
     }
     else
     {
@@ -719,7 +732,7 @@ void SetWindowIcon(Image image)
 
             // NOTE 1: We only support one image icon
             // NOTE 2: The specified image data is copied before this function returns
-            glfwSetWindowIcon(CORE.Window.handle, 1, icon);
+            glfwSetWindowIcon(platform.handle, 1, icon);
         }
         else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
     }
@@ -734,7 +747,7 @@ void SetWindowIcons(Image *images, int count)
     if ((images == NULL) || (count <= 0))
     {
         // Revert to the default window icon, pass in an empty image array
-        glfwSetWindowIcon(CORE.Window.handle, 0, NULL);
+        glfwSetWindowIcon(platform.handle, 0, NULL);
     }
     else
     {
@@ -754,7 +767,7 @@ void SetWindowIcons(Image *images, int count)
             else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
         }
         // NOTE: Images data is copied internally before this function returns
-        glfwSetWindowIcon(CORE.Window.handle, valid, icons);
+        glfwSetWindowIcon(platform.handle, valid, icons);
 
         RL_FREE(icons);
     }
@@ -764,13 +777,13 @@ void SetWindowIcons(Image *images, int count)
 void SetWindowTitle(const char *title)
 {
     CORE.Window.title = title;
-    glfwSetWindowTitle(CORE.Window.handle, title);
+    glfwSetWindowTitle(platform.handle, title);
 }
 
 // Set window position on screen (windowed mode)
 void SetWindowPosition(int x, int y)
 {
-    glfwSetWindowPos(CORE.Window.handle, x, y);
+    glfwSetWindowPos(platform.handle, x, y);
 }
 
 // Set monitor for the current window
@@ -786,7 +799,7 @@ void SetWindowMonitor(int monitor)
             TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
 
             const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
-            glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate);
+            glfwSetWindowMonitor(platform.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate);
         }
         else
         {
@@ -801,12 +814,12 @@ void SetWindowMonitor(int monitor)
             glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight);
 
             // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it
-            if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY);
+            if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(platform.handle, monitorWorkareaX, monitorWorkareaY);
             else
             {
                 const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2);
                 const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2);
-                glfwSetWindowPos(CORE.Window.handle, x, y);
+                glfwSetWindowPos(platform.handle, x, y);
             }
         }
     }
@@ -822,7 +835,7 @@ void SetWindowMinSize(int width, int height)
     int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height;
     int maxWidth  = (CORE.Window.screenMax.width  == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width;
     int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height;
-    glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight);
+    glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight);
 }
 
 // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
@@ -834,13 +847,13 @@ void SetWindowMaxSize(int width, int height)
     int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height;
     int maxWidth  = (CORE.Window.screenMax.width  == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width;
     int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height;
-    glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight);
+    glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight);
 }
 
 // Set window dimensions
 void SetWindowSize(int width, int height)
 {
-    glfwSetWindowSize(CORE.Window.handle, width, height);
+    glfwSetWindowSize(platform.handle, width, height);
 }
 
 // Set window opacity, value opacity is between 0.0 and 1.0
@@ -848,13 +861,13 @@ void SetWindowOpacity(float opacity)
 {
     if (opacity >= 1.0f) opacity = 1.0f;
     else if (opacity <= 0.0f) opacity = 0.0f;
-    glfwSetWindowOpacity(CORE.Window.handle, opacity);
+    glfwSetWindowOpacity(platform.handle, opacity);
 }
 
 // Set window focused
 void SetWindowFocused(void)
 {
-    glfwFocusWindow(CORE.Window.handle);
+    glfwFocusWindow(platform.handle);
 }
 
 // Get native window handle
@@ -862,19 +875,19 @@ void *GetWindowHandle(void)
 {
 #if defined(_WIN32)
     // NOTE: Returned handle is: void *HWND (windows.h)
-    return glfwGetWin32Window(CORE.Window.handle);
+    return glfwGetWin32Window(platform.handle);
 #endif
 #if defined(__linux__)
     // NOTE: Returned handle is: unsigned long Window (X.h)
     // typedef unsigned long XID;
     // typedef XID Window;
-    //unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle);
+    //unsigned long id = (unsigned long)glfwGetX11Window(platform.handle);
     //return NULL;    // TODO: Find a way to return value... cast to void *?
-    return (void *)CORE.Window.handle;
+    return (void *)platform.handle;
 #endif
 #if defined(__APPLE__)
     // NOTE: Returned handle is: (objc_object *)
-    return (void *)glfwGetCocoaWindow(CORE.Window.handle);
+    return (void *)glfwGetCocoaWindow(platform.handle);
 #endif
 
     return NULL;
@@ -903,7 +916,7 @@ int GetCurrentMonitor(void)
         if (IsWindowFullscreen())
         {
             // Get the handle of the monitor that the specified window is in full screen on
-            monitor = glfwGetWindowMonitor(CORE.Window.handle);
+            monitor = glfwGetWindowMonitor(platform.handle);
 
             for (int i = 0; i < monitorCount; i++)
             {
@@ -919,7 +932,7 @@ int GetCurrentMonitor(void)
             int x = 0;
             int y = 0;
 
-            glfwGetWindowPos(CORE.Window.handle, &x, &y);
+            glfwGetWindowPos(platform.handle, &x, &y);
 
             for (int i = 0; i < monitorCount; i++)
             {
@@ -1070,7 +1083,7 @@ Vector2 GetWindowPosition(void)
     int x = 0;
     int y = 0;
     
-    glfwGetWindowPos(CORE.Window.handle, &x, &y);
+    glfwGetWindowPos(platform.handle, &x, &y);
     
     return (Vector2){ (float)x, (float)y };
 }
@@ -1109,34 +1122,34 @@ Vector2 GetWindowScaleDPI(void)
 // Set clipboard text content
 void SetClipboardText(const char *text)
 {
-    glfwSetClipboardString(CORE.Window.handle, text);
+    glfwSetClipboardString(platform.handle, text);
 }
 
 // Get clipboard text content
 // NOTE: returned string is allocated and freed by GLFW
 const char *GetClipboardText(void)
 {
-    return glfwGetClipboardString(CORE.Window.handle);
+    return glfwGetClipboardString(platform.handle);
 }
 
 // Show mouse cursor
 void ShowCursor(void)
 {
-    glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+    glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
     CORE.Input.Mouse.cursorHidden = false;
 }
 
 // Hides mouse cursor
 void HideCursor(void)
 {
-    glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
+    glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
     CORE.Input.Mouse.cursorHidden = true;
 }
 
 // Enables cursor (unlock cursor)
 void EnableCursor(void)
 {
-    glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+    glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
 
     // Set cursor position in the middle
     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
@@ -1147,7 +1160,7 @@ void EnableCursor(void)
 // Disables cursor (lock cursor)
 void DisableCursor(void)
 {
-    glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+    glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
 
     // Set cursor position in the middle
     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
@@ -1276,7 +1289,7 @@ void SetMousePosition(int x, int y)
     CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
 
     // NOTE: emscripten not implemented
-    glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
+    glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
 }
 
 // Get mouse wheel movement Y
@@ -1294,11 +1307,11 @@ float GetMouseWheelMove(void)
 void SetMouseCursor(int cursor)
 {
     CORE.Input.Mouse.cursor = cursor;
-    if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL);
+    if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(platform.handle, NULL);
     else
     {
         // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values
-        glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor));
+        glfwSetCursor(platform.handle, glfwCreateStandardCursor(0x00036000 + cursor));
     }
 }
 
@@ -1331,7 +1344,7 @@ Vector2 GetTouchPosition(int index)
 // Swap back buffer with front buffer (screen drawing)
 void SwapScreenBuffer(void)
 {
-    glfwSwapBuffers(CORE.Window.handle);
+    glfwSwapBuffers(platform.handle);
 }
 
 // Register all input events
@@ -1691,10 +1704,10 @@ static bool InitGraphicsDevice(int width, int height)
         // HighDPI monitors are properly considered in a following similar function: SetupViewport()
         SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
 
-        CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
+        platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
 
         // NOTE: Full-screen change, not working properly...
-        //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
+        //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
     }
     else
     {
@@ -1705,16 +1718,16 @@ static bool InitGraphicsDevice(int width, int height)
         }
 
         // No-fullscreen window creation
-        CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
+        platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
 
-        if (CORE.Window.handle)
+        if (platform.handle)
         {
             CORE.Window.render.width = CORE.Window.screen.width;
             CORE.Window.render.height = CORE.Window.screen.height;
         }
     }
 
-    if (!CORE.Window.handle)
+    if (!platform.handle)
     {
         glfwTerminate();
         TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
@@ -1722,23 +1735,23 @@ static bool InitGraphicsDevice(int width, int height)
     }
 
     // Set window callback events
-    glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback);      // NOTE: Resizing not allowed by default!
-    glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback);
-    glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback);
-    glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback);
-    glfwSetDropCallback(CORE.Window.handle, WindowDropCallback);
+    glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback);      // NOTE: Resizing not allowed by default!
+    glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback);
+    glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
+    glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
+    glfwSetDropCallback(platform.handle, WindowDropCallback);
 
     // Set input callback events
-    glfwSetKeyCallback(CORE.Window.handle, KeyCallback);
-    glfwSetCharCallback(CORE.Window.handle, CharCallback);
-    glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback);
-    glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback);   // Track mouse position changes
-    glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback);
-    glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback);
+    glfwSetKeyCallback(platform.handle, KeyCallback);
+    glfwSetCharCallback(platform.handle, CharCallback);
+    glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
+    glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback);   // Track mouse position changes
+    glfwSetScrollCallback(platform.handle, MouseScrollCallback);
+    glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
 
-    glfwMakeContextCurrent(CORE.Window.handle);
+    glfwMakeContextCurrent(platform.handle);
 
-    glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE);    // Enable lock keys modifiers (CAPS, NUM)
+    glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE);    // Enable lock keys modifiers (CAPS, NUM)
 
     glfwSwapInterval(0);        // No V-Sync by default
 
@@ -1760,7 +1773,7 @@ static bool InitGraphicsDevice(int width, int height)
         // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling.
         // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
 #if !defined(__APPLE__)
-        glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight);
+        glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight);
 
         // Screen scaling matrix is required in case desired screen area is different from display area
         CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
@@ -1913,7 +1926,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
     }
 
     // Check the exit key to set close window
-    if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE);
+    if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
 
 #if defined(SUPPORT_SCREEN_CAPTURE)
     if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS))

+ 239 - 159
src/rcore_drm.c

@@ -17,8 +17,10 @@
 *       - TRACELOG() function is located in raylib [utils] module
 *
 *   CONFIGURATION:
-*       #define RCORE_DRM_CUSTOM_FLAG
-*           Custom flag for rcore on PLATFORM_DRM -not used-
+*       #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only)
+*           Reconfigure standard input to receive key inputs, works with SSH connection.
+*           WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other
+*           running processes orblocking the device if not restored properly. Use with care.
 *
 *   DEPENDENCIES:
 *       gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
@@ -47,15 +49,93 @@
 
 #include "rcore.h"
 
+#include <fcntl.h>   // POSIX file control definitions - open(), creat(), fcntl()
+#include <unistd.h>  // POSIX standard function definitions - read(), close(), STDIN_FILENO
+#include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
+#include <pthread.h> // POSIX threads management (inputs reading)
+#include <dirent.h>  // POSIX directory browsing
+
+#include <sys/ioctl.h>      // Required for: ioctl() - UNIX System call for device-specific input/output operations
+#include <linux/kd.h>       // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
+#include <linux/input.h>    // Linux: Keycodes constants definition (KEY_A, ...)
+#include <linux/joystick.h> // Linux: Joystick support library
+
+#include <gbm.h>         // Generic Buffer Management (native platform for EGL on DRM)
+#include <xf86drm.h>     // Direct Rendering Manager user-level library interface
+#include <xf86drmMode.h> // Direct Rendering Manager mode setting (KMS) interface
+
+#include "EGL/egl.h"    // Native platform windowing system interface
+#include "EGL/eglext.h" // EGL extensions
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define USE_LAST_TOUCH_DEVICE       // When multiple touchscreens are connected, only use the one with the highest event<N> number
+
+#define DEFAULT_GAMEPAD_DEV    "/dev/input/js"      // Gamepad input (base dev for all gamepads: js0, js1, ...)
+#define DEFAULT_EVDEV_PATH       "/dev/input/"      // Path to the linux input events
+
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
-//...
+
+typedef struct {
+    pthread_t threadId;                 // Event reading thread id
+    
+    int fd;                             // File descriptor to the device it is assigned to
+    int eventNum;                       // Number of 'event<N>' device
+    Rectangle absRange;                 // Range of values for absolute pointing devices (touchscreens)
+    int touchSlot;                      // Hold the touch slot number of the currently being sent multitouch block
+    bool isMouse;                       // True if device supports relative X Y movements
+    bool isTouch;                       // True if device supports absolute X Y movements and has BTN_TOUCH
+    bool isMultitouch;                  // True if device supports multiple absolute movevents and has BTN_TOUCH
+    bool isKeyboard;                    // True if device has letter keycodes
+    bool isGamepad;                     // True if device has gamepad buttons
+} InputEventWorker;
+
+typedef struct {
+    // Display data
+    int fd;                             // File descriptor for /dev/dri/...
+    drmModeConnector *connector;        // Direct Rendering Manager (DRM) mode connector
+    drmModeCrtc *crtc;                  // CRT Controller
+    int modeIndex;                      // Index of the used mode of connector->modes
+    struct gbm_device *gbmDevice;       // GBM device
+    struct gbm_surface *gbmSurface;     // GBM surface
+    struct gbm_bo *prevBO;              // Previous GBM buffer object (during frame swapping)
+    uint32_t prevFB;                    // Previous GBM framebufer (during frame swapping)
+
+    EGLDisplay device;                  // Native display device (physical screen connection)
+    EGLSurface surface;                 // Surface to draw on, framebuffers (connected to context)
+    EGLContext context;                 // Graphic context, mode in which drawing can be done
+    EGLConfig config;                   // Graphic config
+
+    // Input data
+    InputEventWorker eventWorker[10];   // List of worker threads for every monitored "/dev/input/event<N>"
+    
+    // Keyboard data
+    int defaultKeyboardMode;            // Default keyboard mode
+    bool eventKeyboardMode;             // Keyboard in event mode
+    int defaultFileFlags;               // Default IO file flags
+    struct termios defaultSettings;     // Default keyboard settings
+    int keyboardFd;                     // File descriptor for the evdev keyboard
+
+    // Mouse data
+    Vector2 eventWheelMove;             // Registers the event mouse wheel variation
+    // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update
+    char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab
+
+    // Gamepad data
+    pthread_t gamepadThreadId;          // Gamepad reading thread id
+    int gamepadStreamFd[MAX_GAMEPADS];  // Gamepad device file descriptor
+
+} PlatformData;
 
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-extern CoreData CORE;           // Global CORE state context
+extern CoreData CORE;                   // Global CORE state context
+
+static PlatformData platform = { 0 };   // Platform specific data
 
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
@@ -227,67 +307,67 @@ void CloseWindow(void)
     timeEndPeriod(1);           // Restore time period
 #endif
 
-    if (CORE.Window.prevFB)
+    if (platform.prevFB)
     {
-        drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
-        CORE.Window.prevFB = 0;
+        drmModeRmFB(platform.fd, platform.prevFB);
+        platform.prevFB = 0;
     }
 
-    if (CORE.Window.prevBO)
+    if (platform.prevBO)
     {
-        gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
-        CORE.Window.prevBO = NULL;
+        gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
+        platform.prevBO = NULL;
     }
 
-    if (CORE.Window.gbmSurface)
+    if (platform.gbmSurface)
     {
-        gbm_surface_destroy(CORE.Window.gbmSurface);
-        CORE.Window.gbmSurface = NULL;
+        gbm_surface_destroy(platform.gbmSurface);
+        platform.gbmSurface = NULL;
     }
 
-    if (CORE.Window.gbmDevice)
+    if (platform.gbmDevice)
     {
-        gbm_device_destroy(CORE.Window.gbmDevice);
-        CORE.Window.gbmDevice = NULL;
+        gbm_device_destroy(platform.gbmDevice);
+        platform.gbmDevice = NULL;
     }
 
-    if (CORE.Window.crtc)
+    if (platform.crtc)
     {
-        if (CORE.Window.connector)
+        if (platform.connector)
         {
-            drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id,
-                CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode);
-            drmModeFreeConnector(CORE.Window.connector);
-            CORE.Window.connector = NULL;
+            drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id,
+                platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode);
+            drmModeFreeConnector(platform.connector);
+            platform.connector = NULL;
         }
 
-        drmModeFreeCrtc(CORE.Window.crtc);
-        CORE.Window.crtc = NULL;
+        drmModeFreeCrtc(platform.crtc);
+        platform.crtc = NULL;
     }
 
-    if (CORE.Window.fd != -1)
+    if (platform.fd != -1)
     {
-        close(CORE.Window.fd);
-        CORE.Window.fd = -1;
+        close(platform.fd);
+        platform.fd = -1;
     }
 
     // Close surface, context and display
-    if (CORE.Window.device != EGL_NO_DISPLAY)
+    if (platform.device != EGL_NO_DISPLAY)
     {
-        if (CORE.Window.surface != EGL_NO_SURFACE)
+        if (platform.surface != EGL_NO_SURFACE)
         {
-            eglDestroySurface(CORE.Window.device, CORE.Window.surface);
-            CORE.Window.surface = EGL_NO_SURFACE;
+            eglDestroySurface(platform.device, platform.surface);
+            platform.surface = EGL_NO_SURFACE;
         }
 
-        if (CORE.Window.context != EGL_NO_CONTEXT)
+        if (platform.context != EGL_NO_CONTEXT)
         {
-            eglDestroyContext(CORE.Window.device, CORE.Window.context);
-            CORE.Window.context = EGL_NO_CONTEXT;
+            eglDestroyContext(platform.device, platform.context);
+            platform.context = EGL_NO_CONTEXT;
         }
 
-        eglTerminate(CORE.Window.device);
-        CORE.Window.device = EGL_NO_DISPLAY;
+        eglTerminate(platform.device);
+        platform.device = EGL_NO_DISPLAY;
     }
 
     // Wait for mouse and gamepad threads to finish before closing
@@ -297,21 +377,21 @@ void CloseWindow(void)
     CORE.Window.shouldClose = true;   // Added to force threads to exit when the close window is called
 
     // Close the evdev keyboard
-    if (CORE.Input.Keyboard.fd != -1)
+    if (platform.keyboardFd != -1)
     {
-        close(CORE.Input.Keyboard.fd);
-        CORE.Input.Keyboard.fd = -1;
+        close(platform.keyboardFd);
+        platform.keyboardFd = -1;
     }
 
-    for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
+    for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
     {
-        if (CORE.Input.eventWorker[i].threadId)
+        if (platform.eventWorker[i].threadId)
         {
-            pthread_join(CORE.Input.eventWorker[i].threadId, NULL);
+            pthread_join(platform.eventWorker[i].threadId, NULL);
         }
     }
 
-    if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL);
+    if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL);
 
 #if defined(SUPPORT_EVENTS_AUTOMATION)
     RL_FREE(events);
@@ -524,9 +604,9 @@ int GetMonitorRefreshRate(int monitor)
 {
     int refresh = 0;
     
-    if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0))
+    if ((platform.connector) && (platform.modeIndex >= 0))
     {
-        refresh = CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh;
+        refresh = platform.connector->modes[platform.modeIndex].vrefresh;
     }
     
     return refresh;
@@ -659,7 +739,7 @@ const char *GetGamepadName(int gamepad)
 
     if (CORE.Input.Gamepad.ready[gamepad])
     {
-        ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]);
+        ioctl(platform.gamepadStreamFd[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]);
         name = CORE.Input.Gamepad.name[gamepad];
     }
 
@@ -670,7 +750,7 @@ const char *GetGamepadName(int gamepad)
 int GetGamepadAxisCount(int gamepad)
 {
     int axisCount = 0;
-    if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount);
+    if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount);
     CORE.Input.Gamepad.axisCount = axisCount;
     
     return CORE.Input.Gamepad.axisCount;
@@ -756,31 +836,31 @@ Vector2 GetTouchPosition(int index)
 // Swap back buffer with front buffer (screen drawing)
 void SwapScreenBuffer(void)
 {
-    eglSwapBuffers(CORE.Window.device, CORE.Window.surface);
+    eglSwapBuffers(platform.device, platform.surface);
 
-    if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
+    if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
 
-    struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface);
+    struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface);
     if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer");
 
     uint32_t fb = 0;
-    int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb);
+    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);
     if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result);
 
-    result = drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0, &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]);
+    result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]);
     if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result);
 
-    if (CORE.Window.prevFB)
+    if (platform.prevFB)
     {
-        result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
+        result = drmModeRmFB(platform.fd, platform.prevFB);
         if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result);
     }
 
-    CORE.Window.prevFB = fb;
+    platform.prevFB = fb;
 
-    if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
+    if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
 
-    CORE.Window.prevBO = bo;
+    platform.prevBO = bo;
 }
 
 // Register all input events
@@ -814,12 +894,12 @@ void PollInputEvents(void)
 
     // Register previous mouse states
     CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
-    CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove;
-    CORE.Input.Mouse.eventWheelMove = (Vector2){ 0.0f, 0.0f };
+    CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove;
+    platform.eventWheelMove = (Vector2){ 0.0f, 0.0f };
     for (int i = 0; i < MAX_MOUSE_BUTTONS; i++)
     {
         CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
-        CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i];
+        CORE.Input.Mouse.currentButtonState[i] = platform.currentButtonStateEvdev[i];
     }
 
     // Register gamepads buttons events
@@ -844,7 +924,7 @@ void PollInputEvents(void)
     // 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 (!CORE.Input.Keyboard.evtMode) ProcessKeyboard();
+    if (!platform.eventKeyboardMode) ProcessKeyboard();
 
     // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread()
     // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread()
@@ -877,41 +957,41 @@ static bool InitGraphicsDevice(int width, int height)
     CORE.Window.fullscreen = true;
     CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
 
-    CORE.Window.fd = -1;
-    CORE.Window.connector = NULL;
-    CORE.Window.modeIndex = -1;
-    CORE.Window.crtc = NULL;
-    CORE.Window.gbmDevice = NULL;
-    CORE.Window.gbmSurface = NULL;
-    CORE.Window.prevBO = NULL;
-    CORE.Window.prevFB = 0;
+    platform.fd = -1;
+    platform.connector = NULL;
+    platform.modeIndex = -1;
+    platform.crtc = NULL;
+    platform.gbmDevice = NULL;
+    platform.gbmSurface = NULL;
+    platform.prevBO = NULL;
+    platform.prevFB = 0;
 
 #if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
-    CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
+    platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
 #else
     TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card");
-    CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card",  O_RDWR); // VideoCore VI (Raspberry Pi 4)
+    platform.fd = open("/dev/dri/by-path/platform-gpu-card",  O_RDWR); // VideoCore VI (Raspberry Pi 4)
 
-    if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL))
+    if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL))
     {
         TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1");
-        CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded
+        platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded
     }
 
-    if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL))
+    if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL))
     {
         TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0");
-        CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
+        platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
     }
 #endif
 
-    if (CORE.Window.fd == -1)
+    if (platform.fd == -1)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card");
         return false;
     }
 
-    drmModeRes *res = drmModeGetResources(CORE.Window.fd);
+    drmModeRes *res = drmModeGetResources(platform.fd);
     if (!res)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources");
@@ -924,13 +1004,13 @@ static bool InitGraphicsDevice(int width, int height)
     {
         TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i);
         
-        drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]);
+        drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]);
         TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes);
         
         if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id))
         {
             TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected");
-            CORE.Window.connector = con;
+            platform.connector = con;
             break;
         }
         else
@@ -940,14 +1020,14 @@ static bool InitGraphicsDevice(int width, int height)
         }
     }
 
-    if (!CORE.Window.connector)
+    if (!platform.connector)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found");
         drmModeFreeResources(res);
         return false;
     }
 
-    drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id);
+    drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id);
     if (!enc)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder");
@@ -955,8 +1035,8 @@ static bool InitGraphicsDevice(int width, int height)
         return false;
     }
 
-    CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id);
-    if (!CORE.Window.crtc)
+    platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id);
+    if (!platform.crtc)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc");
         drmModeFreeEncoder(enc);
@@ -969,9 +1049,9 @@ static bool InitGraphicsDevice(int width, int height)
     {
         TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode...");
 
-        CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode);
+        platform.modeIndex = FindMatchingConnectorMode(platform.connector, &platform.crtc->mode);
 
-        if (CORE.Window.modeIndex < 0)
+        if (platform.modeIndex < 0)
         {
             TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found");
             drmModeFreeEncoder(enc);
@@ -987,19 +1067,19 @@ static bool InitGraphicsDevice(int width, int height)
     const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60;
 
     // Try to find an exact matching mode
-    CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
+    platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
 
     // If nothing found, try to find a nearly matching mode
-    if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
+    if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
 
     // If nothing found, try to find an exactly matching mode including interlaced
-    if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
+    if (platform.modeIndex < 0) platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
 
     // If nothing found, try to find a nearly matching mode including interlaced
-    if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
+    if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
 
     // If nothing found, there is no suitable mode
-    if (CORE.Window.modeIndex < 0)
+    if (platform.modeIndex < 0)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode");
         drmModeFreeEncoder(enc);
@@ -1007,13 +1087,13 @@ static bool InitGraphicsDevice(int width, int height)
         return false;
     }
 
-    CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay;
-    CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay;
+    CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay;
+    CORE.Window.display.height = platform.connector->modes[platform.modeIndex].vdisplay;
 
-    TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name,
-        CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay,
-        (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
-        CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh);
+    TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name,
+        platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay,
+        (platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
+        platform.connector->modes[platform.modeIndex].vrefresh);
 
     // Use the width and height of the surface for render
     CORE.Window.render.width = CORE.Window.screen.width;
@@ -1025,16 +1105,16 @@ static bool InitGraphicsDevice(int width, int height)
     drmModeFreeResources(res);
     res = NULL;
 
-    CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd);
-    if (!CORE.Window.gbmDevice)
+    platform.gbmDevice = gbm_create_device(platform.fd);
+    if (!platform.gbmDevice)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device");
         return false;
     }
 
-    CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay,
-        CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
-    if (!CORE.Window.gbmSurface)
+    platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay,
+        platform.connector->modes[platform.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+    if (!platform.gbmSurface)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface");
         return false;
@@ -1073,22 +1153,22 @@ static bool InitGraphicsDevice(int width, int height)
     EGLint numConfigs = 0;
 
     // Get an EGL device connection
-    CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice);
-    if (CORE.Window.device == EGL_NO_DISPLAY)
+    platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice);
+    if (platform.device == EGL_NO_DISPLAY)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
         return false;
     }
 
     // Initialize the EGL device connection
-    if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE)
+    if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
     {
         // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
         return false;
     }
 
-    if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs))
+    if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs))
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
         return false;
@@ -1104,7 +1184,7 @@ static bool InitGraphicsDevice(int width, int height)
     }
 
     EGLint matchingNumConfigs = 0;
-    if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
+    if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
         free(configs);
@@ -1118,7 +1198,7 @@ static bool InitGraphicsDevice(int width, int height)
     for (EGLint i = 0; i < matchingNumConfigs; ++i)
     {
         EGLint id = 0;
-        if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
+        if (!eglGetConfigAttrib(platform.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
         {
             TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError());
             continue;
@@ -1127,7 +1207,7 @@ static bool InitGraphicsDevice(int width, int height)
         if (GBM_FORMAT_ARGB8888 == id)
         {
             TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i);
-            CORE.Window.config = configs[i];
+            platform.config = configs[i];
             found = 1;
             break;
         }
@@ -1145,8 +1225,8 @@ static bool InitGraphicsDevice(int width, int height)
     eglBindAPI(EGL_OPENGL_ES_API);
 
     // Create an EGL rendering context
-    CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs);
-    if (CORE.Window.context == EGL_NO_CONTEXT)
+    platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
+    if (platform.context == EGL_NO_CONTEXT)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
         return false;
@@ -1154,8 +1234,8 @@ static bool InitGraphicsDevice(int width, int height)
 
     // Create an EGL window surface
     //---------------------------------------------------------------------------------
-    CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL);
-    if (EGL_NO_SURFACE == CORE.Window.surface)
+    platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL);
+    if (EGL_NO_SURFACE == platform.surface)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
         return false;
@@ -1169,9 +1249,9 @@ static bool InitGraphicsDevice(int width, int height)
     SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
 
     // There must be at least one frame displayed before the buffers are swapped
-    //eglSwapInterval(CORE.Window.device, 1);
+    //eglSwapInterval(platform.device, 1);
 
-    if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE)
+    if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface");
         return false;
@@ -1214,11 +1294,11 @@ static void InitKeyboard(void)
     // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
 
     // Save terminal keyboard settings
-    tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings);
+    tcgetattr(STDIN_FILENO, &platform.defaultSettings);
 
     // Reconfigure terminal with new settings
     struct termios keyboardNewSettings = { 0 };
-    keyboardNewSettings = CORE.Input.Keyboard.defaultSettings;
+    keyboardNewSettings = platform.defaultSettings;
 
     // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
     // NOTE: ISIG controls if ^C and ^Z generate break signals or not
@@ -1231,11 +1311,11 @@ static void InitKeyboard(void)
     tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings);
 
     // Save old keyboard mode to restore it at the end
-    CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0);          // F_GETFL: Get the file access mode and the file status flags
-    fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified
+    platform.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0);          // F_GETFL: Get the file access mode and the file status flags
+    fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified
 
     // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno)
-    int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode);
+    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");
@@ -1257,11 +1337,11 @@ static void InitKeyboard(void)
 static void RestoreKeyboard(void)
 {
     // Reset to default keyboard settings
-    tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings);
+    tcsetattr(STDIN_FILENO, TCSANOW, &platform.defaultSettings);
 
     // Reconfigure keyboard to default mode
-    fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags);
-    ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode);
+    fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags);
+    ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode);
 }
 
 #if defined(SUPPORT_SSH_KEYBOARD_RPI)
@@ -1389,7 +1469,7 @@ static void InitEvdevInput(void)
     struct dirent *entity = NULL;
 
     // Initialise keyboard file descriptor
-    CORE.Input.Keyboard.fd = -1;
+    platform.keyboardFd = -1;
 
     // Reset variables
     for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
@@ -1451,9 +1531,9 @@ static void ConfigureEvdevDevice(char *device)
     // Open the device and allocate worker
     //-------------------------------------------------------------------------------------------------------
     // Find a free spot in the workers array
-    for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
+    for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
     {
-        if (CORE.Input.eventWorker[i].threadId == 0)
+        if (platform.eventWorker[i].threadId == 0)
         {
             freeWorkerId = i;
             break;
@@ -1463,7 +1543,7 @@ static void ConfigureEvdevDevice(char *device)
     // Select the free worker from array
     if (freeWorkerId >= 0)
     {
-        worker = &(CORE.Input.eventWorker[freeWorkerId]);       // Grab a pointer to the worker
+        worker = &(platform.eventWorker[freeWorkerId]);       // Grab a pointer to the worker
         memset(worker, 0, sizeof(InputEventWorker));  // Clear the worker
     }
     else
@@ -1574,13 +1654,13 @@ static void ConfigureEvdevDevice(char *device)
 
     // Decide what to do with the device
     //-------------------------------------------------------------------------------------------------------
-    if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1))
+    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);
-        CORE.Input.Keyboard.fd = worker->fd;
+        platform.keyboardFd = worker->fd;
     }
     else if (worker->isTouch || worker->isMouse)
     {
@@ -1604,21 +1684,21 @@ static void ConfigureEvdevDevice(char *device)
         // Find touchscreen with the highest index
         int maxTouchNumber = -1;
 
-        for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
+        for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
         {
-            if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum;
+            if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = platform.eventWorker[i].eventNum;
         }
 
         // Find touchscreens with lower indexes
-        for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
+        for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
         {
-            if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber))
+            if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum < maxTouchNumber))
             {
-                if (CORE.Input.eventWorker[i].threadId != 0)
+                if (platform.eventWorker[i].threadId != 0)
                 {
                     TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i);
-                    pthread_cancel(CORE.Input.eventWorker[i].threadId);
-                    close(CORE.Input.eventWorker[i].fd);
+                    pthread_cancel(platform.eventWorker[i].threadId);
+                    close(platform.eventWorker[i].fd);
                 }
             }
         }
@@ -1652,7 +1732,7 @@ static void PollKeyboardEvents(void)
         243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0
     };
 
-    int fd = CORE.Input.Keyboard.fd;
+    int fd = platform.keyboardFd;
     if (fd == -1) return;
 
     struct input_event event = { 0 };
@@ -1666,7 +1746,7 @@ static void PollKeyboardEvents(void)
         {
 #if defined(SUPPORT_SSH_KEYBOARD_RPI)
             // Change keyboard mode to events
-            CORE.Input.Keyboard.evtMode = true;
+            platform.eventKeyboardMode = true;
 #endif
             // Keyboard button parsing
             if ((event.code >= 1) && (event.code <= 255))     //Keyboard keys appear for codes 1 to 255
@@ -1739,7 +1819,7 @@ static void *EventThread(void *arg)
                     gestureUpdate = true;
                 }
 
-                if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value;
+                if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value;
             }
 
             // Absolute movement parsing
@@ -1790,11 +1870,11 @@ static void *EventThread(void *arg)
                 // Touchscreen tap
                 if (event.code == ABS_PRESSURE)
                 {
-                    int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
+                    int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
 
                     if (!event.value && previousMouseLeftButtonState)
                     {
-                        CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
+                        platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
 
                         touchAction = 0;    // TOUCH_ACTION_UP
                         gestureUpdate = true;
@@ -1802,7 +1882,7 @@ static void *EventThread(void *arg)
 
                     if (event.value && !previousMouseLeftButtonState)
                     {
-                        CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
+                        platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
 
                         touchAction = 1;    // TOUCH_ACTION_DOWN
                         gestureUpdate = true;
@@ -1817,19 +1897,19 @@ static void *EventThread(void *arg)
                 // Mouse button parsing
                 if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
                 {
-                    CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
+                    platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
 
                     if (event.value > 0) touchAction = 1;   // TOUCH_ACTION_DOWN
                     else touchAction = 0;       // TOUCH_ACTION_UP
                     gestureUpdate = true;
                 }
 
-                if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
-                if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value;
-                if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value;
-                if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value;
-                if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value;
-                if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value;
+                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
@@ -1885,7 +1965,7 @@ static void InitGamepad(void)
     {
         sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i);
 
-        if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0)
+        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");
@@ -1897,7 +1977,7 @@ static void InitGamepad(void)
             // NOTE: Only create one thread
             if (i == 0)
             {
-                int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL);
+                int error = pthread_create(&platform.gamepadThreadId, NULL, &GamepadThread, NULL);
 
                 if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread");
                 else  TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully");
@@ -1927,7 +2007,7 @@ static void *GamepadThread(void *arg)
     {
         for (int i = 0; i < MAX_GAMEPADS; i++)
         {
-            if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
+            if (read(platform.gamepadStreamFd[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
             {
                 gamepadEvent.type &= ~JS_EVENT_INIT;     // Ignore synthetic events
 
@@ -1977,7 +2057,7 @@ static int FindMatchingConnectorMode(const drmModeConnector *connector, const dr
         TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
             connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
 
-        if (0 == BINCMP(&CORE.Window.crtc->mode, &CORE.Window.connector->modes[i])) return i;
+        if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i;
     }
 
     return -1;
@@ -1992,9 +2072,9 @@ static int FindExactConnectorMode(const drmModeConnector *connector, uint width,
 
     if (NULL == connector) return -1;
 
-    for (int i = 0; i < CORE.Window.connector->count_modes; i++)
+    for (int i = 0; i < platform.connector->count_modes; i++)
     {
-        const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
+        const drmModeModeInfo *const mode = &platform.connector->modes[i];
 
         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");
 
@@ -2015,9 +2095,9 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
     if (NULL == connector) return -1;
 
     int nearestIndex = -1;
-    for (int i = 0; i < CORE.Window.connector->count_modes; i++)
+    for (int i = 0; i < platform.connector->count_modes; i++)
     {
-        const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
+        const drmModeModeInfo *const mode = &platform.connector->modes[i];
 
         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");
@@ -2044,9 +2124,9 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
         const int heightDiff = abs(mode->vdisplay - height);
         const int fpsDiff = abs(mode->vrefresh - fps);
 
-        const int nearestWidthDiff = abs(CORE.Window.connector->modes[nearestIndex].hdisplay - width);
-        const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height);
-        const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps);
+        const int nearestWidthDiff = abs(platform.connector->modes[nearestIndex].hdisplay - width);
+        const int nearestHeightDiff = abs(platform.connector->modes[nearestIndex].vdisplay - height);
+        const int nearestFpsDiff = abs(platform.connector->modes[nearestIndex].vrefresh - fps);
 
         if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) {
             nearestIndex = i;

+ 47 - 28
src/rcore_web.c

@@ -47,23 +47,42 @@
 
 #include "rcore.h"
 
-#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL)
-// #define GLFW_INCLUDE_ES3            // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?)
-#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management
+#define GLFW_INCLUDE_ES2                // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL)
+// #define GLFW_INCLUDE_ES3               // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?)
+#include "GLFW/glfw3.h"                 // GLFW3: Windows, OpenGL context and Input management
+
+#include <emscripten/emscripten.h>      // Emscripten functionality for C
+#include <emscripten/html5.h>           // Emscripten HTML5 library
+
 #include <sys/time.h>   // Required for: timespec, nanosleep(), select() - POSIX
 
-#include <emscripten/emscripten.h> // Emscripten functionality for C
-#include <emscripten/html5.h>      // Emscripten HTML5 library
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// TODO: HACK: Added flag if not provided by GLFW when using external library
+// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev
+#if !defined(GLFW_MOUSE_PASSTHROUGH)
+    #define GLFW_MOUSE_PASSTHROUGH      0x0002000D
+#endif
+
+#if (_POSIX_C_SOURCE < 199309L)
+    #undef _POSIX_C_SOURCE
+    #define _POSIX_C_SOURCE 199309L     // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
+#endif
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
-//...
+typedef struct {
+    GLFWwindow *handle;                 // GLFW window handle (graphic device)
+} PlatformData;
 
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-extern CoreData CORE;           // Global CORE state context
+extern CoreData CORE;                   // Global CORE state context
+
+static PlatformData platform = { 0 };   // Platform specific data
 
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
@@ -263,7 +282,7 @@ void CloseWindow(void)
 
     rlglClose(); // De-init rlgl
 
-    glfwDestroyWindow(CORE.Window.handle);
+    glfwDestroyWindow(platform.handle);
     glfwTerminate();
 
 #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
@@ -481,7 +500,7 @@ void SetWindowMaxSize(int width, int height)
 // Set window dimensions
 void SetWindowSize(int width, int height)
 {
-    glfwSetWindowSize(CORE.Window.handle, width, height);
+    glfwSetWindowSize(platform.handle, width, height);
 }
 
 // Set window opacity, value opacity is between 0.0 and 1.0
@@ -759,7 +778,7 @@ void SetMousePosition(int x, int y)
     CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
 
     // NOTE: emscripten not implemented
-    glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
+    glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
 }
 
 // Get mouse wheel movement Y
@@ -806,7 +825,7 @@ Vector2 GetTouchPosition(int index)
 // Swap back buffer with front buffer (screen drawing)
 void SwapScreenBuffer(void)
 {
-    glfwSwapBuffers(CORE.Window.handle);
+    glfwSwapBuffers(platform.handle);
 }
 
 // Register all input events
@@ -1110,24 +1129,24 @@ static bool InitGraphicsDevice(int width, int height)
         // HighDPI monitors are properly considered in a following similar function: SetupViewport()
         SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
 
-        CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
+        platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
 
         // NOTE: Full-screen change, not working properly...
-        // glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
+        // glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
     }
     else
     {
         // No-fullscreen window creation
-        CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
+        platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
 
-        if (CORE.Window.handle)
+        if (platform.handle)
         {
             CORE.Window.render.width = CORE.Window.screen.width;
             CORE.Window.render.height = CORE.Window.screen.height;
         }
     }
 
-    if (!CORE.Window.handle)
+    if (!platform.handle)
     {
         glfwTerminate();
         TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
@@ -1138,20 +1157,20 @@ static bool InitGraphicsDevice(int width, int height)
     emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " ");
 
     // Set window callback events
-    glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default!
-    glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback);
-    glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback);
-    glfwSetDropCallback(CORE.Window.handle, WindowDropCallback);
+    glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default!
+    glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
+    glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
+    glfwSetDropCallback(platform.handle, WindowDropCallback);
 
     // Set input callback events
-    glfwSetKeyCallback(CORE.Window.handle, KeyCallback);
-    glfwSetCharCallback(CORE.Window.handle, CharCallback);
-    glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback);
-    glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes
-    glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback);
-    glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback);
+    glfwSetKeyCallback(platform.handle, KeyCallback);
+    glfwSetCharCallback(platform.handle, CharCallback);
+    glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
+    glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes
+    glfwSetScrollCallback(platform.handle, MouseScrollCallback);
+    glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
 
-    glfwMakeContextCurrent(CORE.Window.handle);
+    glfwMakeContextCurrent(platform.handle);
 
     // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
     // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need
@@ -1293,7 +1312,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
     }
 
     // Check the exit key to set close window
-    if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE);
+    if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
 
 #if defined(SUPPORT_SCREEN_CAPTURE)
     if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS))

+ 1 - 1
src/utils.h

@@ -32,7 +32,7 @@
     #include <android/asset_manager.h>      // Required for: AAssetManager
 #endif
 
-#if defined(SUPPORT_TRACELOG)
+#if defined(SUPPORT_TRACELOG) && !defined(TRACELOG)
     #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__)
 
     #if defined(SUPPORT_TRACELOG_DEBUG)