Browse Source

WARNING: REDESIGN: `InitPlatform()` to initialize all platform data #3313

`InitGraphicsDevice()` could be confusing because the function actually initialized many things: window, graphics, inputs, callbacks, timming, storage... restructured it.
Ray 1 year ago
parent
commit
b34c2ecbcb
6 changed files with 505 additions and 547 deletions
  1. 4 4
      src/rcore.c
  2. 148 131
      src/rcore_android.c
  3. 64 80
      src/rcore_desktop.c
  4. 171 181
      src/rcore_drm.c
  5. 30 41
      src/rcore_template.c
  6. 88 110
      src/rcore_web.c

+ 4 - 4
src/rcore.c

@@ -2365,10 +2365,10 @@ int GetTouchPointCount(void)
 // Initialize hi-resolution timer
 void InitTimer(void)
 {
-// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
-// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
-// High resolutions can also prevent the CPU power management system from entering power-saving modes.
-// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.
+    // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
+    // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
+    // High resolutions can also prevent the CPU power management system from entering power-saving modes.
+    // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.
 #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
     timeBeginPeriod(1);                 // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
 #endif

+ 148 - 131
src/rcore_android.c

@@ -82,7 +82,8 @@ static PlatformData platform = { 0 };   // Platform specific data
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
-static bool InitGraphicsDevice(int width, int height); // Initialize graphics device
+static int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
+static void ClosePlatform(void);        // Close platform
 
 static void AndroidCommandCallback(struct android_app *app, int32_t cmd);           // Process Android activity lifecycle commands
 static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event);   // Process Android inputs
@@ -172,88 +173,23 @@ void InitWindow(int width, int height, const char *title)
     TRACELOG(LOG_INFO, "    > raudio:.... not loaded (optional)");
 #endif
 
-    // NOTE: Keep internal pointer to input title string (no copy)
+    // Initialize window data
+    CORE.Window.screen.width = width;
+    CORE.Window.screen.height = height;
+    CORE.Window.eventWaiting = false;
+    CORE.Window.screenScale = MatrixIdentity();     // No draw scaling required by default
     if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
 
     // Initialize global input state
-    memset(&CORE.Input, 0, sizeof(CORE.Input));
+    memset(&CORE.Input, 0, sizeof(CORE.Input));     // Reset CORE.Input structure to 0
     CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
     CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
     CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
-    CORE.Input.Gamepad.lastButtonPressed = 0;       // GAMEPAD_BUTTON_UNKNOWN
-    CORE.Window.eventWaiting = false;
-
-
-    // Platform specific init window
-    //--------------------------------------------------------------
-    CORE.Window.screen.width = width;
-    CORE.Window.screen.height = height;
-    CORE.Window.currentFbo.width = width;
-    CORE.Window.currentFbo.height = height;
-
-    // Set desired windows flags before initializing anything
-    ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0);  //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
-
-    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");
-
-    // TODO: Automatic orientation doesn't seem to work
-    if (width <= height)
-    {
-        AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT);
-        TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait");
-    }
-    else
-    {
-        AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND);
-        TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape");
-    }
-
-    //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()...
-    platform.app->onAppCmd = AndroidCommandCallback;
-
-    // Initialize input events system
-    platform.app->onInputEvent = AndroidInputCallback;
-
-    // Initialize assets manager
-    InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath);
-
-    // Initialize base path for storage
-    CORE.Storage.basePath = platform.app->activity->internalDataPath;
-
-    // Set some default window flags
-    CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;       // false
-    CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;    // false
-    CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;     // true
-    CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;    // false
+    CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
 
-    TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully");
-
-    // Android ALooper_pollAll() variables
-    int pollResult = 0;
-    int pollEvents = 0;
-
-    // Wait for window to be initialized (display and context)
-    while (!CORE.Window.ready)
-    {
-        // Process events loop
-        while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0)
-        {
-            // Process this event
-            if (platform.source != NULL) platform.source->process(platform.app, platform.source);
-
-            // NOTE: Never close window, native activity is controlled by the system!
-            //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true;
-        }
-    }
+    // Initialize platform
+    //--------------------------------------------------------------   
+    InitPlatform();
     //--------------------------------------------------------------
 }
 
@@ -279,28 +215,9 @@ void CloseWindow(void)
     timeEndPeriod(1);           // Restore time period
 #endif
 
-    // Platform specific close window
-    //--------------------------------------------------------------
-    // Close surface, context and display
-    if (platform.device != EGL_NO_DISPLAY)
-    {
-        eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
-        if (platform.surface != EGL_NO_SURFACE)
-        {
-            eglDestroySurface(platform.device, platform.surface);
-            platform.surface = EGL_NO_SURFACE;
-        }
-
-        if (platform.context != EGL_NO_CONTEXT)
-        {
-            eglDestroyContext(platform.device, platform.context);
-            platform.context = EGL_NO_CONTEXT;
-        }
-
-        eglTerminate(platform.device);
-        platform.device = EGL_NO_DISPLAY;
-    }
+    // De-initialize platform
+    //--------------------------------------------------------------   
+    ClosePlatform();
     //--------------------------------------------------------------
 
 #if defined(SUPPORT_EVENTS_AUTOMATION)
@@ -690,25 +607,108 @@ void PollInputEvents(void)
 // Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 
+// Initialize platform: graphics, inputs and more
+static int InitPlatform(void)
+{
+    CORE.Window.currentFbo.width = CORE.Window.screen.width;
+    CORE.Window.currentFbo.height = CORE.Window.screen.width;
+
+    // Set desired windows flags before initializing anything
+    ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0);  //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
+
+    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");
+
+    // TODO: Automatic orientation doesn't seem to work
+    if (width <= height)
+    {
+        AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT);
+        TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait");
+    }
+    else
+    {
+        AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND);
+        TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape");
+    }
+
+    //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()...
+    platform.app->onAppCmd = AndroidCommandCallback;
+
+    // Initialize input events system
+    platform.app->onInputEvent = AndroidInputCallback;
+
+    // Initialize assets manager
+    InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath);
+
+    // Initialize base path for storage
+    CORE.Storage.basePath = platform.app->activity->internalDataPath;
+
+    // Set some default window flags
+    CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;       // false
+    CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;    // false
+    CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;     // true
+    CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;    // false
+
+    TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully");
+
+    // Android ALooper_pollAll() variables
+    int pollResult = 0;
+    int pollEvents = 0;
+
+    // Wait for window to be initialized (display and context)
+    while (!CORE.Window.ready)
+    {
+        // Process events loop
+        while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0)
+        {
+            // Process this event
+            if (platform.source != NULL) platform.source->process(platform.app, platform.source);
+
+            // NOTE: Never close window, native activity is controlled by the system!
+            //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true;
+        }
+    }
+}
+
+// Close platform
+static void ClosePlatform(void)
+{
+    // Close surface, context and display
+    if (platform.device != EGL_NO_DISPLAY)
+    {
+        eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+        if (platform.surface != EGL_NO_SURFACE)
+        {
+            eglDestroySurface(platform.device, platform.surface);
+            platform.surface = EGL_NO_SURFACE;
+        }
+
+        if (platform.context != EGL_NO_CONTEXT)
+        {
+            eglDestroyContext(platform.device, platform.context);
+            platform.context = EGL_NO_CONTEXT;
+        }
+
+        eglTerminate(platform.device);
+        platform.device = EGL_NO_DISPLAY;
+    }
+}
+
 // Initialize display device and framebuffer
 // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
 // If width or height are 0, default display size will be used for framebuffer size
 // NOTE: returns false in case graphic device could not be created
-static bool InitGraphicsDevice(int width, int height)
+static bool InitGraphicsDevice(void)
 {
-    CORE.Window.screen.width = width;            // User desired width
-    CORE.Window.screen.height = height;          // User desired height
-    CORE.Window.screenScale = MatrixIdentity();  // No draw scaling required by default
-
-    // Set the screen minimum and maximum default values to 0
-    CORE.Window.screenMin.width  = 0;
-    CORE.Window.screenMin.height = 0;
-    CORE.Window.screenMax.width  = 0;
-    CORE.Window.screenMax.height = 0;
-
-    // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
-    // ...in top-down or left-right to match display aspect ratio (no weird scaling)
-
     CORE.Window.fullscreen = true;
     CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
 
@@ -748,7 +748,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (platform.device == EGL_NO_DISPLAY)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
-        return false;
+        return -1;
     }
 
     // Initialize the EGL device connection
@@ -756,7 +756,7 @@ static bool InitGraphicsDevice(int width, int height)
     {
         // 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;
+        return -1;
     }
 
     // Get an appropriate EGL framebuffer configuration
@@ -770,7 +770,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (platform.context == EGL_NO_CONTEXT)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
-        return false;
+        return -1;
     }
 
     // Create an EGL window surface
@@ -799,7 +799,7 @@ static bool InitGraphicsDevice(int width, int height)
     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;
+        return -1;
     }
     else
     {
@@ -819,19 +819,11 @@ static bool InitGraphicsDevice(int width, int height)
     // NOTE: GL procedures address loader is required to load extensions
     rlLoadExtensions(eglGetProcAddress);
 
-    // Initialize OpenGL context (states and resources)
-    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
-    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
-
-    // Setup default viewport
-    // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
-    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
-
     CORE.Window.ready = true;
 
     if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
 
-    return true;
+    return 0;
 }
 
 // ANDROID: Process activity lifecycle commands
@@ -874,24 +866,49 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
                     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);
+                    InitGraphicsDevice();
+                    
+                    // Initialize OpenGL context (states and resources)
+                    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
+                    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
+
+                    // Setup default viewport
+                    // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
+                    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
 
                     // Initialize hi-res timer
                     InitTimer();
 
-                    // Initialize random seed
-                    srand((unsigned int)time(NULL));
-
                 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
                     // Load default font
                     // WARNING: External function: Module required: rtext
                     LoadFontDefault();
+                    #if defined(SUPPORT_MODULE_RSHAPES)
+                    // Set font white rectangle for shapes drawing, so shapes and text can be batched together
+                    // WARNING: rshapes module is required, if not available, default internal white rectangle is used
                     Rectangle rec = GetFontDefault().recs[95];
-                    // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
+                    if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+                    {
+                        // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
+                        SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
+                    }
+                    else
+                    {
+                        // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding
+                        SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
+                    }
+                    #endif
+                #else
                     #if defined(SUPPORT_MODULE_RSHAPES)
-                    SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });  // WARNING: Module required: rshapes
+                    // Set default texture and rectangle to be used for shapes drawing
+                    // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
+                    Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
+                    SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f });    // WARNING: Module required: rshapes
                     #endif
                 #endif
+                
+                    // Initialize random seed
+                    SetRandomSeed((unsigned int)time(NULL));
 
                     // TODO: GPU assets reload in case of lost focus (lost context)
                     // NOTE: This problem has been solved just unbinding and rebinding context from display

+ 64 - 80
src/rcore_desktop.c

@@ -111,7 +111,8 @@ static PlatformData platform = { 0 };   // Platform specific data
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
-static bool InitGraphicsDevice(int width, int height); // Initialize graphics device
+static int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
+static void ClosePlatform(void);        // Close platform
 
 // Error callback event
 static void ErrorCallback(int error, const char *description);                             // GLFW3 Error Callback, runs on GLFW3 error
@@ -176,53 +177,31 @@ void InitWindow(int width, int height, const char *title)
     TRACELOG(LOG_INFO, "    > raudio:.... not loaded (optional)");
 #endif
 
-    // NOTE: Keep internal pointer to input title string (no copy)
+    // Initialize window data
+    CORE.Window.screen.width = width;
+    CORE.Window.screen.height = height;
+    CORE.Window.eventWaiting = false;
+    CORE.Window.screenScale = MatrixIdentity();     // No draw scaling required by default
     if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
 
     // Initialize global input state
-    memset(&CORE.Input, 0, sizeof(CORE.Input));     // Reset CORE structure to 0
+    memset(&CORE.Input, 0, sizeof(CORE.Input));     // Reset CORE.Input structure to 0
     CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
     CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
     CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
-    CORE.Input.Gamepad.lastButtonPressed = 0;       // GAMEPAD_BUTTON_UNKNOWN
-    CORE.Window.eventWaiting = false;
-
-
-    // Platform specific init window
-    //--------------------------------------------------------------
-    glfwSetErrorCallback(ErrorCallback);
-/*
-    // TODO: Setup GLFW custom allocators to match raylib ones
-    const GLFWallocator allocator = {
-        .allocate = MemAlloc,
-        .deallocate = MemFree,
-        .reallocate = MemRealloc,
-        .user = NULL
-    };
-
-    glfwInitAllocator(&allocator);
-*/
-
-    // Initialize graphics device
-    // NOTE: returns true if window and graphic device has been initialized successfully
-    // WARNING: Actually, all window initialization and input callbacks initialization is
-    // done inside InitGraphicsDevice(), this functionality should be changed!
-    CORE.Window.ready = InitGraphicsDevice(width, height);
-
-    // If graphic device is no properly initialized, we end program
-    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; }
-    else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
-
-    // Initialize hi-res timer
-    InitTimer();
+    CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
     
-    // Initialize base path for storage
-    CORE.Storage.basePath = GetWorkingDirectory();
+    // Initialize platform
+    //--------------------------------------------------------------   
+    InitPlatform();
     //--------------------------------------------------------------
+    
+    // Initialize rlgl default data (buffers and shaders)
+    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
+    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
 
-
-    // Initialize random seed
-    SetRandomSeed((unsigned int)time(NULL));
+    // Setup default viewport
+    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
 
 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
     // Load default font
@@ -266,6 +245,9 @@ void InitWindow(int width, int height, const char *title)
     CORE.Time.frameCounter = 0;
 #endif
 
+    // Initialize random seed
+    SetRandomSeed((unsigned int)time(NULL));
+
     TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully");
 }
 
@@ -287,14 +269,9 @@ void CloseWindow(void)
 
     rlglClose();                // De-init rlgl
 
-    // Platform specific close window
-    //--------------------------------------------------------------
-    glfwDestroyWindow(platform.handle);
-    glfwTerminate();
-
-#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
-    timeEndPeriod(1);           // Restore time period
-#endif
+    // De-initialize platform
+    //--------------------------------------------------------------   
+    ClosePlatform();
     //--------------------------------------------------------------
 
 #if defined(SUPPORT_EVENTS_AUTOMATION)
@@ -1379,34 +1356,28 @@ void PollInputEvents(void)
 // Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 
-// Initialize display device and framebuffer
-// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
-// If width or height are 0, default display size will be used for framebuffer size
-// NOTE: returns false in case graphic device could not be created
-static bool InitGraphicsDevice(int width, int height)
+// Initialize platform: graphics, inputs and more
+static int InitPlatform(void)
 {
-    CORE.Window.screen.width = width;            // User desired width
-    CORE.Window.screen.height = height;          // User desired height
-    CORE.Window.screenScale = MatrixIdentity();  // No draw scaling required by default
-
-    // Set the screen minimum and maximum default values to 0
-    CORE.Window.screenMin.width  = 0;
-    CORE.Window.screenMin.height = 0;
-    CORE.Window.screenMax.width  = 0;
-    CORE.Window.screenMax.height = 0;
+    glfwSetErrorCallback(ErrorCallback);
+/*
+    // TODO: Setup GLFW custom allocators to match raylib ones
+    const GLFWallocator allocator = {
+        .allocate = MemAlloc,
+        .deallocate = MemFree,
+        .reallocate = MemRealloc,
+        .user = NULL
+    };
 
-    // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
-    // ...in top-down or left-right to match display aspect ratio (no weird scaling)
+    glfwInitAllocator(&allocator);
+*/
 
 #if defined(__APPLE__)
     glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
 #endif
-
-    if (!glfwInit())
-    {
-        TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW");
-        return false;
-    }
+    // Initialize GLFW internal global state
+    int result = glfwInit();
+    if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; }
 
     glfwDefaultWindowHints();                       // Set default windows hints
     //glfwWindowHint(GLFW_RED_BITS, 8);             // Framebuffer red color component bits
@@ -1528,7 +1499,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (!monitor)
     {
         TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
-        return false;
+        return -1;
     }
 
     const GLFWvidmode *mode = glfwGetVideoMode(monitor);
@@ -1617,7 +1588,7 @@ static bool InitGraphicsDevice(int width, int height)
     {
         glfwTerminate();
         TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
-        return false;
+        return -1;
     }
 
     // Set window callback events
@@ -1683,22 +1654,35 @@ static bool InitGraphicsDevice(int width, int height)
 
     // Load OpenGL extensions
     // NOTE: GL procedures address loader is required to load extensions
-
     rlLoadExtensions(glfwGetProcAddress);
 
-    // Initialize OpenGL context (states and resources)
-    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
-    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
+    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+    
+    // If graphic device is no properly initialized, we end program
+    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
+    else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
 
-    // Setup default viewport
-    // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
-    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
+    // Initialize hi-res timer
+    InitTimer();
+    
+    // Initialize base path for storage
+    CORE.Storage.basePath = GetWorkingDirectory();
+    
+    return 0;
+}
 
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+// Close platform
+static void ClosePlatform(void)
+{
+    glfwDestroyWindow(platform.handle);
+    glfwTerminate();
 
-    return true;
+#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
+    timeEndPeriod(1);           // Restore time period
+#endif
 }
 
+
 // GLFW3 Error Callback, runs on GLFW3 error
 static void ErrorCallback(int error, const char *description)
 {

+ 171 - 181
src/rcore_drm.c

@@ -140,21 +140,22 @@ static PlatformData platform = { 0 };   // Platform specific data
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
-static bool InitGraphicsDevice(int width, int height);  // Initialize graphics device
+static int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
+static void ClosePlatform(void);        // Close platform
 
-static void InitKeyboard(void);                         // Initialize raw keyboard system
-static void RestoreKeyboard(void);                      // Restore keyboard system
+static void InitKeyboard(void);                 // Initialize raw keyboard system
+static void RestoreKeyboard(void);              // Restore keyboard system
 #if defined(SUPPORT_SSH_KEYBOARD_RPI)
-static void ProcessKeyboard(void);                      // Process keyboard events
+static void ProcessKeyboard(void);              // Process keyboard events
 #endif
 
-static void InitEvdevInput(void);                       // Initialize evdev inputs
-static void ConfigureEvdevDevice(char *device);         // Identifies a input device and configures it for use if appropriate
-static void PollKeyboardEvents(void);                   // Process evdev keyboard events
-static void *EventThread(void *arg);                    // Input device events reading thread
+static void InitEvdevInput(void);               // Initialize evdev inputs
+static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate
+static void PollKeyboardEvents(void);           // Process evdev keyboard events
+static void *EventThread(void *arg);            // Input device events reading thread
 
-static void InitGamepad(void);                          // Initialize raw gamepad input
-static void *GamepadThread(void *arg);                  // Mouse reading thread
+static void InitGamepad(void);                  // Initialize raw gamepad input
+static void *GamepadThread(void *arg);          // Mouse reading thread
 
 static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode);                               // Search matching DRM mode in connector's mode list
 static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced);      // Search exactly matching DRM connector mode in connector's list
@@ -204,49 +205,31 @@ void InitWindow(int width, int height, const char *title)
     TRACELOG(LOG_INFO, "    > raudio:.... not loaded (optional)");
 #endif
 
-    // NOTE: Keep internal pointer to input title string (no copy)
+    // Initialize window data
+    CORE.Window.screen.width = width;
+    CORE.Window.screen.height = height;
+    CORE.Window.eventWaiting = false;
+    CORE.Window.screenScale = MatrixIdentity();     // No draw scaling required by default
     if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
 
     // Initialize global input state
-    memset(&CORE.Input, 0, sizeof(CORE.Input));
+    memset(&CORE.Input, 0, sizeof(CORE.Input));     // Reset CORE.Input structure to 0
     CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
-    CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f};
+    CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
     CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
-    CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
-    CORE.Window.eventWaiting = false;
-
-
-    // Platform specific init window
-    //--------------------------------------------------------------
-    // Initialize graphics device (display device and OpenGL context)
-    // NOTE: returns true if window and graphic device has been initialized successfully
-    CORE.Window.ready = InitGraphicsDevice(width, height);
-
-    // If graphic device is no properly initialized, we end program
-    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; }
-    else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2);
-
-    // Set some default window flags
-    CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;       // false
-    CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;    // false
-    CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;     // true
-    CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;    // false
-
-    // Initialize hi-res timer
-    InitTimer();
-
-    // Initialize base path for storage
-    CORE.Storage.basePath = GetWorkingDirectory();
-
-    // Initialize raw input system
-    InitEvdevInput(); // Evdev inputs initialization
-    InitGamepad();    // Gamepad init
-    InitKeyboard();   // Keyboard init (stdin)
+    CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
+    
+    // Initialize platform
+    //--------------------------------------------------------------   
+    InitPlatform();
     //--------------------------------------------------------------
+    
+    // Initialize rlgl default data (buffers and shaders)
+    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
+    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
 
-
-    // Initialize random seed
-    SetRandomSeed((unsigned int)time(NULL));
+    // Setup default viewport
+    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
 
 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
     // Load default font
@@ -289,7 +272,10 @@ void InitWindow(int width, int height, const char *title)
     events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent));
     CORE.Time.frameCounter = 0;
 #endif
-
+    
+    // Initialize random seed
+    SetRandomSeed((unsigned int)time(NULL));
+    
     TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully");
 }
 
@@ -315,93 +301,9 @@ void CloseWindow(void)
     timeEndPeriod(1);           // Restore time period
 #endif
 
-    // Platform specific close window
-    //--------------------------------------------------------------
-    if (platform.prevFB)
-    {
-        drmModeRmFB(platform.fd, platform.prevFB);
-        platform.prevFB = 0;
-    }
-
-    if (platform.prevBO)
-    {
-        gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
-        platform.prevBO = NULL;
-    }
-
-    if (platform.gbmSurface)
-    {
-        gbm_surface_destroy(platform.gbmSurface);
-        platform.gbmSurface = NULL;
-    }
-
-    if (platform.gbmDevice)
-    {
-        gbm_device_destroy(platform.gbmDevice);
-        platform.gbmDevice = NULL;
-    }
-
-    if (platform.crtc)
-    {
-        if (platform.connector)
-        {
-            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(platform.crtc);
-        platform.crtc = NULL;
-    }
-
-    if (platform.fd != -1)
-    {
-        close(platform.fd);
-        platform.fd = -1;
-    }
-
-    // Close surface, context and display
-    if (platform.device != EGL_NO_DISPLAY)
-    {
-        if (platform.surface != EGL_NO_SURFACE)
-        {
-            eglDestroySurface(platform.device, platform.surface);
-            platform.surface = EGL_NO_SURFACE;
-        }
-
-        if (platform.context != EGL_NO_CONTEXT)
-        {
-            eglDestroyContext(platform.device, platform.context);
-            platform.context = EGL_NO_CONTEXT;
-        }
-
-        eglTerminate(platform.device);
-        platform.device = EGL_NO_DISPLAY;
-    }
-
-    // Wait for mouse and gamepad threads to finish before closing
-    // NOTE: Those threads should already have finished at this point
-    // because they are controlled by CORE.Window.shouldClose variable
-
-    CORE.Window.shouldClose = true;   // Added to force threads to exit when the close window is called
-
-    // Close the evdev keyboard
-    if (platform.keyboardFd != -1)
-    {
-        close(platform.keyboardFd);
-        platform.keyboardFd = -1;
-    }
-
-    for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
-    {
-        if (platform.eventWorker[i].threadId)
-        {
-            pthread_join(platform.eventWorker[i].threadId, NULL);
-        }
-    }
-
-    if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL);
+    // De-initialize platform
+    //--------------------------------------------------------------   
+    ClosePlatform();
     //--------------------------------------------------------------
 
 #if defined(SUPPORT_EVENTS_AUTOMATION)
@@ -808,28 +710,9 @@ void PollInputEvents(void)
 // Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 
-// Initialize display device and framebuffer
-// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
-// If width or height are 0, default display size will be used for framebuffer size
-// NOTE: returns false in case graphic device could not be created
-static bool InitGraphicsDevice(int width, int height)
+// Initialize platform: graphics, inputs and more
+static int InitPlatform(void)
 {
-    CORE.Window.screen.width = width;            // User desired width
-    CORE.Window.screen.height = height;          // User desired height
-    CORE.Window.screenScale = MatrixIdentity();  // No draw scaling required by default
-
-    // Set the window minimum and maximum default values to 0
-    CORE.Window.screenMin.width  = 0;
-    CORE.Window.screenMin.height = 0;
-    CORE.Window.screenMax.width  = 0;
-    CORE.Window.screenMax.height = 0;
-
-    // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
-    // ...in top-down or left-right to match display aspect ratio (no weird scaling)
-
-    CORE.Window.fullscreen = true;
-    CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
-
     platform.fd = -1;
     platform.connector = NULL;
     platform.modeIndex = -1;
@@ -838,6 +721,9 @@ static bool InitGraphicsDevice(int width, int height)
     platform.gbmSurface = NULL;
     platform.prevBO = NULL;
     platform.prevFB = 0;
+    
+    CORE.Window.fullscreen = true;
+    CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
 
 #if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
     platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
@@ -861,14 +747,14 @@ static bool InitGraphicsDevice(int width, int height)
     if (platform.fd == -1)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card");
-        return false;
+        return -1;
     }
 
     drmModeRes *res = drmModeGetResources(platform.fd);
     if (!res)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources");
-        return false;
+        return -1;
     }
 
     TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors);
@@ -897,7 +783,7 @@ static bool InitGraphicsDevice(int width, int height)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found");
         drmModeFreeResources(res);
-        return false;
+        return -1;
     }
 
     drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id);
@@ -905,7 +791,7 @@ static bool InitGraphicsDevice(int width, int height)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder");
         drmModeFreeResources(res);
-        return false;
+        return -1;
     }
 
     platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id);
@@ -914,7 +800,7 @@ static bool InitGraphicsDevice(int width, int height)
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc");
         drmModeFreeEncoder(enc);
         drmModeFreeResources(res);
-        return false;
+        return -1;
     }
 
     // If InitWindow should use the current mode find it in the connector's mode list
@@ -929,7 +815,7 @@ static bool InitGraphicsDevice(int width, int height)
             TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found");
             drmModeFreeEncoder(enc);
             drmModeFreeResources(res);
-            return false;
+            return -1;
         }
 
         CORE.Window.screen.width = CORE.Window.display.width;
@@ -957,7 +843,7 @@ static bool InitGraphicsDevice(int width, int height)
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode");
         drmModeFreeEncoder(enc);
         drmModeFreeResources(res);
-        return false;
+        return -1;
     }
 
     CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay;
@@ -982,7 +868,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (!platform.gbmDevice)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device");
-        return false;
+        return -1;
     }
 
     platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay,
@@ -990,7 +876,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (!platform.gbmSurface)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface");
-        return false;
+        return -1;
     }
 
     EGLint samples = 0;
@@ -1030,7 +916,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (platform.device == EGL_NO_DISPLAY)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
-        return false;
+        return -1;
     }
 
     // Initialize the EGL device connection
@@ -1038,13 +924,13 @@ static bool InitGraphicsDevice(int width, int height)
     {
         // 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;
+        return -1;
     }
 
     if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs))
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
-        return false;
+        return -1;
     }
 
     TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs);
@@ -1053,7 +939,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (!configs)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
-        return false;
+        return -1;
     }
 
     EGLint matchingNumConfigs = 0;
@@ -1061,7 +947,7 @@ static bool InitGraphicsDevice(int width, int height)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
         free(configs);
-        return false;
+        return -1;
     }
 
     TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs);
@@ -1091,7 +977,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (!found)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config");
-        return false;
+        return -1;
     }
 
     // Set rendering API
@@ -1102,7 +988,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (platform.context == EGL_NO_CONTEXT)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
-        return false;
+        return -1;
     }
 
     // Create an EGL window surface
@@ -1111,7 +997,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (EGL_NO_SURFACE == platform.surface)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
-        return false;
+        return -1;
     }
 
     // At this point we need to manage render size vs screen size
@@ -1127,7 +1013,7 @@ static bool InitGraphicsDevice(int width, int height)
     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;
+        return -1;
     }
     else
     {
@@ -1147,19 +1033,123 @@ static bool InitGraphicsDevice(int width, int height)
     // NOTE: GL procedures address loader is required to load extensions
     rlLoadExtensions(eglGetProcAddress);
 
-    // Initialize OpenGL context (states and resources)
-    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
-    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
+    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+    
+        // If graphic device is no properly initialized, we end program
+    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
+    else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2);
 
-    // Setup default viewport
-    // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
-    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
+    // Set some default window flags
+    CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;       // false
+    CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;    // false
+    CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;     // true
+    CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;    // false
 
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+    // Initialize hi-res timer
+    InitTimer();
 
-    return true;
+    // Initialize base path for storage
+    CORE.Storage.basePath = GetWorkingDirectory();
+
+    // Initialize raw input system
+    InitEvdevInput(); // Evdev inputs initialization
+    InitGamepad();    // Gamepad init
+    InitKeyboard();   // Keyboard init (stdin)
+    
+    return 0;
 }
 
+// Close platform
+static void ClosePlatform(void)
+{
+    if (platform.prevFB)
+    {
+        drmModeRmFB(platform.fd, platform.prevFB);
+        platform.prevFB = 0;
+    }
+
+    if (platform.prevBO)
+    {
+        gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
+        platform.prevBO = NULL;
+    }
+
+    if (platform.gbmSurface)
+    {
+        gbm_surface_destroy(platform.gbmSurface);
+        platform.gbmSurface = NULL;
+    }
+
+    if (platform.gbmDevice)
+    {
+        gbm_device_destroy(platform.gbmDevice);
+        platform.gbmDevice = NULL;
+    }
+
+    if (platform.crtc)
+    {
+        if (platform.connector)
+        {
+            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(platform.crtc);
+        platform.crtc = NULL;
+    }
+
+    if (platform.fd != -1)
+    {
+        close(platform.fd);
+        platform.fd = -1;
+    }
+
+    // Close surface, context and display
+    if (platform.device != EGL_NO_DISPLAY)
+    {
+        if (platform.surface != EGL_NO_SURFACE)
+        {
+            eglDestroySurface(platform.device, platform.surface);
+            platform.surface = EGL_NO_SURFACE;
+        }
+
+        if (platform.context != EGL_NO_CONTEXT)
+        {
+            eglDestroyContext(platform.device, platform.context);
+            platform.context = EGL_NO_CONTEXT;
+        }
+
+        eglTerminate(platform.device);
+        platform.device = EGL_NO_DISPLAY;
+    }
+
+    // Wait for mouse and gamepad threads to finish before closing
+    // NOTE: Those threads should already have finished at this point
+    // because they are controlled by CORE.Window.shouldClose variable
+
+    CORE.Window.shouldClose = true;   // Added to force threads to exit when the close window is called
+
+    // Close the evdev keyboard
+    if (platform.keyboardFd != -1)
+    {
+        close(platform.keyboardFd);
+        platform.keyboardFd = -1;
+    }
+
+    for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i)
+    {
+        if (platform.eventWorker[i].threadId)
+        {
+            pthread_join(platform.eventWorker[i].threadId, NULL);
+        }
+    }
+
+    if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL);
+}
+
+
 // Initialize Keyboard system (using standard input)
 static void InitKeyboard(void)
 {

+ 30 - 41
src/rcore_template.c

@@ -73,7 +73,8 @@ static PlatformData platform = { 0 };   // Platform specific data
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
-static bool InitGraphicsDevice(int width, int height); // Initialize graphics device
+static int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
+static bool InitGraphicsDevice(void);   // Initialize graphics device
 
 //----------------------------------------------------------------------------------
 // Module Functions Declaration
@@ -142,19 +143,15 @@ void InitWindow(int width, int height, const char *title)
     // NOTE: returns true if window and graphic device has been initialized successfully
     CORE.Window.ready = InitGraphicsDevice(width, height);
 
-    // If graphic device is no properly initialized, we end program
-    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; }
 
-    // Initialize hi-res timer
-    InitTimer();
     
-    // Initialize base path for storage
-    CORE.Storage.basePath = GetWorkingDirectory();
-    //--------------------------------------------------------------
-
+    // Initialize OpenGL context (states and resources)
+    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
+    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
 
-    // Initialize random seed
-    SetRandomSeed((unsigned int)time(NULL));
+    // Setup default viewport
+    // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
+    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
 
 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
     // Load default font
@@ -198,6 +195,9 @@ void InitWindow(int width, int height, const char *title)
     CORE.Time.frameCounter = 0;
 #endif
 
+    // Initialize random seed
+    SetRandomSeed((unsigned int)time(NULL));
+
     TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully");
 }
 
@@ -597,25 +597,9 @@ void PollInputEvents(void)
 // Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 
-// Initialize display device and framebuffer
-// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
-// If width or height are 0, default display size will be used for framebuffer size
-// NOTE: returns false in case graphic device could not be created
-static bool InitGraphicsDevice(int width, int height)
+// Initialize platform: graphics, inputs and more
+static int InitPlatform(void)
 {
-    CORE.Window.screen.width = width;            // User desired width
-    CORE.Window.screen.height = height;          // User desired height
-    CORE.Window.screenScale = MatrixIdentity();  // No draw scaling required by default
-
-    // Set the screen minimum and maximum default values to 0
-    CORE.Window.screenMin.width  = 0;
-    CORE.Window.screenMin.height = 0;
-    CORE.Window.screenMax.width  = 0;
-    CORE.Window.screenMax.height = 0;
-
-    // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
-    // ...in top-down or left-right to match display aspect ratio (no weird scaling)
-
     CORE.Window.fullscreen = true;
     CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
 
@@ -677,7 +661,7 @@ static bool InitGraphicsDevice(int width, int height)
     if (platform.context == EGL_NO_CONTEXT)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
-        return false;
+        return -1;
     }
 
     // Create an EGL window surface
@@ -706,7 +690,7 @@ static bool InitGraphicsDevice(int width, int height)
     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;
+        return -1;
     }
     else
     {
@@ -726,19 +710,24 @@ static bool InitGraphicsDevice(int width, int height)
     // NOTE: GL procedures address loader is required to load extensions
     rlLoadExtensions(eglGetProcAddress);
 
-    // Initialize OpenGL context (states and resources)
-    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
-    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
-
-    // Setup default viewport
-    // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
-    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
-
     CORE.Window.ready = true;
+    
+    // If graphic device is no properly initialized, we end program
+    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
+
+    // Initialize hi-res timer
+    InitTimer();
+    
+    // Initialize base path for storage
+    CORE.Storage.basePath = GetWorkingDirectory();
 
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+    return 0;
+}
 
-    return true;
+// Close platform
+static void ClosePlatform(void)
+{
+    // TODO: De-initialize graphics, inputs and more
 }
 
 // EOF

+ 88 - 110
src/rcore_web.c

@@ -87,17 +87,18 @@ static PlatformData platform = { 0 };   // Platform specific data
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
-static bool InitGraphicsDevice(int width, int height); // Initialize graphics device
+static int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
+static void ClosePlatform(void);        // Close platform
 
 // Error callback event
-static void ErrorCallback(int error, const char *description);                             // GLFW3 Error Callback, runs on GLFW3 error
+static void ErrorCallback(int error, const char *description);                      // GLFW3 Error Callback, runs on GLFW3 error
 
 // Window callbacks events
-static void WindowSizeCallback(GLFWwindow *window, int width, int height);         // GLFW3 WindowSize Callback, runs when window is resized
-static void WindowIconifyCallback(GLFWwindow *window, int iconified);              // GLFW3 WindowIconify Callback, runs when window is minimized/restored
-static void WindowMaximizeCallback(GLFWwindow *window, int maximized);             // GLFW3 Window Maximize Callback, runs when window is maximized
-static void WindowFocusCallback(GLFWwindow *window, int focused);                  // GLFW3 WindowFocus Callback, runs when window get/lose focus
-static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
+static void WindowSizeCallback(GLFWwindow *window, int width, int height);          // GLFW3 WindowSize Callback, runs when window is resized
+static void WindowIconifyCallback(GLFWwindow *window, int iconified);               // GLFW3 WindowIconify Callback, runs when window is minimized/restored
+static void WindowMaximizeCallback(GLFWwindow *window, int maximized);              // GLFW3 Window Maximize Callback, runs when window is maximized
+static void WindowFocusCallback(GLFWwindow *window, int focused);                   // GLFW3 WindowFocus Callback, runs when window get/lose focus
+static void WindowDropCallback(GLFWwindow *window, int count, const char **paths);  // GLFW3 Window Drop Callback, runs when drop files into window
 
 // Input callbacks events
 static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
@@ -107,11 +108,12 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y);
 static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset);      // GLFW3 Srolling Callback, runs on mouse wheel
 static void CursorEnterCallback(GLFWwindow *window, int enter);                           // GLFW3 Cursor Enter Callback, cursor enters client area
 
-// Emscripten callback events
+// Emscripten window callback events
 static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
 static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
 static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
 
+// Emscripten input callback events
 static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
 static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
 static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
@@ -160,67 +162,32 @@ void InitWindow(int width, int height, const char *title)
     TRACELOG(LOG_INFO, "    > raudio:.... not loaded (optional)");
 #endif
 
-    // NOTE: Keep internal pointer to input title string (no copy)
+    // Initialize window data
+    CORE.Window.screen.width = width;
+    CORE.Window.screen.height = height;
+    CORE.Window.eventWaiting = false;
+    CORE.Window.screenScale = MatrixIdentity();     // No draw scaling required by default
     if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
 
     // Initialize global input state
-    memset(&CORE.Input, 0, sizeof(CORE.Input));
+    memset(&CORE.Input, 0, sizeof(CORE.Input));     // Reset CORE.Input structure to 0
     CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
-    CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f};
+    CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
     CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
-    CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
-    CORE.Window.eventWaiting = false;
+    CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
 
-
-    // Platform specific init window
-    //--------------------------------------------------------------
-    // Initialize graphics device (display device and OpenGL context)
-    // NOTE: returns true if window and graphic device has been initialized successfully
-    CORE.Window.ready = InitGraphicsDevice(width, height);
-
-    // If graphic device is no properly initialized, we end program
-    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; }
-    else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
-
-    // Initialize hi-res timer
-    InitTimer();
-    
-    // Initialize base path for storage
-    CORE.Storage.basePath = GetWorkingDirectory();
-    
-    // Setup callback functions for the DOM events
-    emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback);
-
-    // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
-    // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
-    // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
-    // Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
-    emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
-
-    // Trigger this once to get initial window sizing
-    EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
-
-    // Support keyboard events -> Not used, GLFW.JS takes care of that
-    // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
-    // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
-
-    // Support mouse events
-    emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
-
-    // Support touch events
-    emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
-    emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
-    emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
-    emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
-
-    // Support gamepad events (not provided by GLFW3 on emscripten)
-    emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
-    emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
+    // Initialize platform
+    //--------------------------------------------------------------   
+    InitPlatform();
     //--------------------------------------------------------------
 
+    // Initialize OpenGL context (states and resources)
+    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
+    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
 
-    // Initialize random seed
-    SetRandomSeed((unsigned int)time(NULL));
+    // Setup default viewport
+    // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
+    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
 
 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
     // Load default font
@@ -264,6 +231,9 @@ void InitWindow(int width, int height, const char *title)
     CORE.Time.frameCounter = 0;
 #endif
 
+    // Initialize random seed
+    SetRandomSeed((unsigned int)time(NULL));
+
     TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully");
 }
 
@@ -285,10 +255,9 @@ void CloseWindow(void)
 
     rlglClose(); // De-init rlgl
 
-    // Platform specific close window
-    //--------------------------------------------------------------
-    glfwDestroyWindow(platform.handle);
-    glfwTerminate();
+    // De-initialize platform
+    //--------------------------------------------------------------   
+    ClosePlatform();
     //--------------------------------------------------------------
 
 #if defined(SUPPORT_EVENTS_AUTOMATION)
@@ -814,43 +783,14 @@ void PollInputEvents(void)
 // Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 
-// Initialize display device and framebuffer
-// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
-// If width or height are 0, default display size will be used for framebuffer size
-// NOTE: returns false in case graphic device could not be created
-static bool InitGraphicsDevice(int width, int height)
+// Initialize platform: graphics, inputs and more
+static int InitPlatform(void)
 {
-    CORE.Window.screen.width = width;           // User desired width
-    CORE.Window.screen.height = height;         // User desired height
-    CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default
-
-    // Set the screen minimum and maximum default values to 0
-    CORE.Window.screenMin.width  = 0;
-    CORE.Window.screenMin.height = 0;
-    CORE.Window.screenMax.width  = 0;
-    CORE.Window.screenMax.height = 0;
-
-    // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
-    // ...in top-down or left-right to match display aspect ratio (no weird scaling)
-
     glfwSetErrorCallback(ErrorCallback);
-/*
-    // TODO: Setup GLFW custom allocators to match raylib ones
-    const GLFWallocator allocator = {
-        .allocate = MemAlloc,
-        .deallocate = MemFree,
-        .reallocate = MemRealloc,
-        .user = NULL
-    };
-
-    glfwInitAllocator(&allocator);
-*/
 
-    if (!glfwInit())
-    {
-        TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW");
-        return false;
-    }
+    // Initialize GLFW internal global state
+    int result = glfwInit();
+    if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; }
 
     glfwDefaultWindowHints(); // Set default windows hints
     // glfwWindowHint(GLFW_RED_BITS, 8);             // Framebuffer red color component bits
@@ -1016,7 +956,7 @@ static bool InitGraphicsDevice(int width, int height)
     {
         glfwTerminate();
         TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
-        return false;
+        return -1;
     }
 
     // WARNING: glfwCreateWindow() title doesn't work with emscripten
@@ -1037,6 +977,12 @@ static bool InitGraphicsDevice(int width, int height)
     glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
 
     glfwMakeContextCurrent(platform.handle);
+    
+    // Load OpenGL extensions
+    // NOTE: GL procedures address loader is required to load extensions
+    rlLoadExtensions(glfwGetProcAddress);
+
+    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
 
     // 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
@@ -1055,22 +1001,54 @@ static bool InitGraphicsDevice(int width, int height)
     TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
     TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
     TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
+    
+    // If graphic device is no properly initialized, we end program
+    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
+    else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
 
-    // Load OpenGL extensions
-    // NOTE: GL procedures address loader is required to load extensions
-    rlLoadExtensions(glfwGetProcAddress);
+    // Initialize hi-res timer
+    InitTimer();
+    
+    // Initialize base path for storage
+    CORE.Storage.basePath = GetWorkingDirectory();
+    
+    // Setup callback functions for the DOM events
+    emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback);
 
-    // Initialize OpenGL context (states and resources)
-    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
-    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
+    // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
+    // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
+    // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
+    // Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
+    emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
 
-    // Setup default viewport
-    // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
-    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
+    // Trigger this once to get initial window sizing
+    EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
 
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+    // Support keyboard events -> Not used, GLFW.JS takes care of that
+    // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
+    // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
+
+    // Support mouse events
+    emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
+
+    // Support touch events
+    emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
+    emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
+    emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
+    emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
 
-    return true;
+    // Support gamepad events (not provided by GLFW3 on emscripten)
+    emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
+    emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
+    
+    return 0;
+}
+
+// Close platform
+static void ClosePlatform(void)
+{
+    glfwDestroyWindow(platform.handle);
+    glfwTerminate();
 }
 
 // GLFW3 Error Callback, runs on GLFW3 error