minesweeper

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

rcore_desktop_glfw.c (79876B)


      1 /**********************************************************************************************
      2 *
      3 *   rcore_desktop - Functions to manage window, graphics device and inputs
      4 *
      5 *   PLATFORM: DESKTOP: GLFW
      6 *       - Windows (Win32, Win64)
      7 *       - Linux (X11/Wayland desktop mode)
      8 *       - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
      9 *       - OSX/macOS (x64, arm64)
     10 *
     11 *   LIMITATIONS:
     12 *       - Limitation 01
     13 *       - Limitation 02
     14 *
     15 *   POSSIBLE IMPROVEMENTS:
     16 *       - Improvement 01
     17 *       - Improvement 02
     18 *
     19 *   ADDITIONAL NOTES:
     20 *       - TRACELOG() function is located in raylib [utils] module
     21 *
     22 *   CONFIGURATION:
     23 *       #define RCORE_PLATFORM_CUSTOM_FLAG
     24 *           Custom flag for rcore on target platform -not used-
     25 *
     26 *   DEPENDENCIES:
     27 *       - rglfw: Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...)
     28 *       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
     29 *
     30 *
     31 *   LICENSE: zlib/libpng
     32 *
     33 *   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
     34 *
     35 *   This software is provided "as-is", without any express or implied warranty. In no event
     36 *   will the authors be held liable for any damages arising from the use of this software.
     37 *
     38 *   Permission is granted to anyone to use this software for any purpose, including commercial
     39 *   applications, and to alter it and redistribute it freely, subject to the following restrictions:
     40 *
     41 *     1. The origin of this software must not be misrepresented; you must not claim that you
     42 *     wrote the original software. If you use this software in a product, an acknowledgment
     43 *     in the product documentation would be appreciated but is not required.
     44 *
     45 *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
     46 *     as being the original software.
     47 *
     48 *     3. This notice may not be removed or altered from any source distribution.
     49 *
     50 **********************************************************************************************/
     51 
     52 #define GLFW_INCLUDE_NONE       // Disable the standard OpenGL header inclusion on GLFW3
     53                                 // NOTE: Already provided by rlgl implementation (on glad.h)
     54 #include "GLFW/glfw3.h"         // GLFW3 library: Windows, OpenGL context and Input management
     55                                 // NOTE: GLFW3 already includes gl.h (OpenGL) headers
     56 
     57 // Support retrieving native window handlers
     58 #if defined(_WIN32)
     59     typedef void *PVOID;
     60     typedef PVOID HANDLE;
     61     #include "../external/win32_clipboard.h"
     62     typedef HANDLE HWND;
     63     #define GLFW_EXPOSE_NATIVE_WIN32
     64     #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h
     65     #include "GLFW/glfw3native.h"
     66 
     67     #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
     68         // NOTE: Those functions require linking with winmm library
     69         //#pragma warning(disable: 4273)
     70         __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
     71         //#pragma warning(default: 4273) 
     72     #endif
     73 #endif
     74 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
     75     #include <sys/time.h>               // Required for: timespec, nanosleep(), select() - POSIX
     76 
     77     //#define GLFW_EXPOSE_NATIVE_X11      // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
     78     //#define GLFW_EXPOSE_NATIVE_WAYLAND
     79     #include "GLFW/glfw3native.h"       // Required for: glfwGetX11Window()
     80 #endif
     81 #if defined(__APPLE__)
     82     #include <unistd.h>                 // Required for: usleep()
     83 
     84     //#define GLFW_EXPOSE_NATIVE_COCOA    // WARNING: Fails due to type redefinition
     85     void *glfwGetCocoaWindow(GLFWwindow* handle);
     86     #include "GLFW/glfw3native.h"       // Required for: glfwGetCocoaWindow()
     87 #endif
     88 
     89 //----------------------------------------------------------------------------------
     90 // Types and Structures Definition
     91 //----------------------------------------------------------------------------------
     92 typedef struct {
     93     GLFWwindow *handle;                 // GLFW window handle (graphic device)
     94 } PlatformData;
     95 
     96 //----------------------------------------------------------------------------------
     97 // Global Variables Definition
     98 //----------------------------------------------------------------------------------
     99 extern CoreData CORE;                   // Global CORE state context
    100 
    101 static PlatformData platform = { 0 };   // Platform specific data
    102 
    103 //----------------------------------------------------------------------------------
    104 // Module Internal Functions Declaration
    105 //----------------------------------------------------------------------------------
    106 int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
    107 void ClosePlatform(void);        // Close platform
    108 
    109 // Error callback event
    110 static void ErrorCallback(int error, const char *description);                             // GLFW3 Error Callback, runs on GLFW3 error
    111 
    112 // Window callbacks events
    113 static void WindowSizeCallback(GLFWwindow *window, int width, int height);                 // GLFW3 WindowSize Callback, runs when window is resized
    114 static void WindowPosCallback(GLFWwindow* window, int x, int y);                     // GLFW3 WindowPos Callback, runs when window is moved
    115 static void WindowIconifyCallback(GLFWwindow *window, int iconified);                      // GLFW3 WindowIconify Callback, runs when window is minimized/restored
    116 static void WindowMaximizeCallback(GLFWwindow* window, int maximized);                     // GLFW3 Window Maximize Callback, runs when window is maximized
    117 static void WindowFocusCallback(GLFWwindow *window, int focused);                          // GLFW3 WindowFocus Callback, runs when window get/lose focus
    118 static void WindowDropCallback(GLFWwindow *window, int count, const char **paths);         // GLFW3 Window Drop Callback, runs when drop files into window
    119 static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley);    // GLFW3 Window Content Scale Callback, runs when window changes scale
    120 
    121 // Input callbacks events
    122 static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);  // GLFW3 Keyboard Callback, runs on key pressed
    123 static void CharCallback(GLFWwindow *window, unsigned int codepoint);                      // GLFW3 Char Callback, runs on key pressed (get codepoint value)
    124 static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);     // GLFW3 Mouse Button Callback, runs on mouse button pressed
    125 static void MouseCursorPosCallback(GLFWwindow *window, double x, double y);                // GLFW3 Cursor Position Callback, runs on mouse move
    126 static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset);       // GLFW3 Scrolling Callback, runs on mouse wheel
    127 static void CursorEnterCallback(GLFWwindow *window, int enter);                            // GLFW3 Cursor Enter Callback, cursor enters client area
    128 static void JoystickCallback(int jid, int event);                                           // GLFW3 Joystick Connected/Disconnected Callback
    129 
    130 //----------------------------------------------------------------------------------
    131 // Module Functions Declaration
    132 //----------------------------------------------------------------------------------
    133 // NOTE: Functions declaration is provided by raylib.h
    134 
    135 //----------------------------------------------------------------------------------
    136 // Module Functions Definition: Window and Graphics Device
    137 //----------------------------------------------------------------------------------
    138 
    139 // Check if application should close
    140 // NOTE: By default, if KEY_ESCAPE pressed or window close icon clicked
    141 bool WindowShouldClose(void)
    142 {
    143     if (CORE.Window.ready) return CORE.Window.shouldClose;
    144     else return true;
    145 }
    146 
    147 // Toggle fullscreen mode
    148 void ToggleFullscreen(void)
    149 {
    150     if (!CORE.Window.fullscreen)
    151     {
    152         // Store previous window position (in case we exit fullscreen)
    153         CORE.Window.previousPosition = CORE.Window.position;
    154 
    155         int monitorCount = 0;
    156         int monitorIndex = GetCurrentMonitor();
    157         GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    158 
    159         // Use current monitor, so we correctly get the display the window is on
    160         GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL;
    161 
    162         if (monitor == NULL)
    163         {
    164             TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor");
    165 
    166             CORE.Window.fullscreen = false;
    167             CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
    168 
    169             glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
    170         }
    171         else
    172         {
    173             CORE.Window.fullscreen = true;
    174             CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
    175 
    176             glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
    177         }
    178 
    179     }
    180     else
    181     {
    182         CORE.Window.fullscreen = false;
    183         CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
    184 
    185         glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
    186 
    187         // we update the window position right away
    188         CORE.Window.position.x = CORE.Window.previousPosition.x;
    189         CORE.Window.position.y = CORE.Window.previousPosition.y;
    190     }
    191 
    192     // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
    193     // NOTE: V-Sync can be enabled by graphic driver configuration
    194     if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1);
    195 }
    196 
    197 // Toggle borderless windowed mode
    198 void ToggleBorderlessWindowed(void)
    199 {
    200     // Leave fullscreen before attempting to set borderless windowed mode
    201     bool wasOnFullscreen = false;
    202     if (CORE.Window.fullscreen)
    203     {
    204         // fullscreen already saves the previous position so it does not need to be set here again
    205         ToggleFullscreen();
    206         wasOnFullscreen = true;
    207     }
    208 
    209     const int monitor = GetCurrentMonitor();
    210     int monitorCount;
    211     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    212 
    213     if ((monitor >= 0) && (monitor < monitorCount))
    214     {
    215         const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
    216 
    217         if (mode)
    218         {
    219             if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE))
    220             {
    221                 // Store screen position and size
    222                 // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here
    223                 if (!wasOnFullscreen) CORE.Window.previousPosition = CORE.Window.position;
    224                 CORE.Window.previousScreen = CORE.Window.screen;
    225 
    226                 // Set undecorated and topmost modes and flags
    227                 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
    228                 CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
    229                 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE);
    230                 CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
    231 
    232                 // Get monitor position and size
    233                 int monitorPosX = 0;
    234                 int monitorPosY = 0;
    235                 glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY);
    236                 const int monitorWidth = mode->width;
    237                 const int monitorHeight = mode->height;
    238 
    239                 // Set screen position and size
    240                 glfwSetWindowPos(platform.handle, monitorPosX, monitorPosY);
    241                 glfwSetWindowSize(platform.handle, monitorWidth, monitorHeight);
    242 
    243                 // Refocus window
    244                 glfwFocusWindow(platform.handle);
    245 
    246                 CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
    247             }
    248             else
    249             {
    250                 // Remove topmost and undecorated modes and flags
    251                 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE);
    252                 CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
    253                 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
    254                 CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
    255 
    256                 // Return previous screen size and position
    257                 // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly
    258                 glfwSetWindowSize(platform.handle,  CORE.Window.previousScreen.width, CORE.Window.previousScreen.height);
    259                 glfwSetWindowPos(platform.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y);
    260 
    261                 // Refocus window
    262                 glfwFocusWindow(platform.handle);
    263 
    264                 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
    265 
    266                 CORE.Window.position.x = CORE.Window.previousPosition.x;
    267                 CORE.Window.position.y = CORE.Window.previousPosition.y;
    268             }
    269         }
    270         else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
    271     }
    272     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
    273 }
    274 
    275 // Set window state: maximized, if resizable
    276 void MaximizeWindow(void)
    277 {
    278     if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
    279     {
    280         glfwMaximizeWindow(platform.handle);
    281         CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
    282     }
    283 }
    284 
    285 // Set window state: minimized
    286 void MinimizeWindow(void)
    287 {
    288     // NOTE: Following function launches callback that sets appropriate flag!
    289     glfwIconifyWindow(platform.handle);
    290 }
    291 
    292 // Set window state: not minimized/maximized
    293 void RestoreWindow(void)
    294 {
    295     if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
    296     {
    297         // Restores the specified window if it was previously iconified (minimized) or maximized
    298         glfwRestoreWindow(platform.handle);
    299         CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
    300         CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
    301     }
    302 }
    303 
    304 // Set window configuration state using flags
    305 void SetWindowState(unsigned int flags)
    306 {
    307     // Check previous state and requested state to apply required changes
    308     // NOTE: In most cases the functions already change the flags internally
    309 
    310     // State change: FLAG_VSYNC_HINT
    311     if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0))
    312     {
    313         glfwSwapInterval(1);
    314         CORE.Window.flags |= FLAG_VSYNC_HINT;
    315     }
    316 
    317     // State change: FLAG_BORDERLESS_WINDOWED_MODE
    318     // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running
    319     if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0))
    320     {
    321         ToggleBorderlessWindowed();     // NOTE: Window state flag updated inside function
    322     }
    323 
    324     // State change: FLAG_FULLSCREEN_MODE
    325     if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE))
    326     {
    327         ToggleFullscreen();     // NOTE: Window state flag updated inside function
    328     }
    329 
    330     // State change: FLAG_WINDOW_RESIZABLE
    331     if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
    332     {
    333         glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
    334         CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
    335     }
    336 
    337     // State change: FLAG_WINDOW_UNDECORATED
    338     if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED))
    339     {
    340         glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
    341         CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
    342     }
    343 
    344     // State change: FLAG_WINDOW_HIDDEN
    345     if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
    346     {
    347         glfwHideWindow(platform.handle);
    348         CORE.Window.flags |= FLAG_WINDOW_HIDDEN;
    349     }
    350 
    351     // State change: FLAG_WINDOW_MINIMIZED
    352     if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
    353     {
    354         //GLFW_ICONIFIED
    355         MinimizeWindow();       // NOTE: Window state flag updated inside function
    356     }
    357 
    358     // State change: FLAG_WINDOW_MAXIMIZED
    359     if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
    360     {
    361         //GLFW_MAXIMIZED
    362         MaximizeWindow();       // NOTE: Window state flag updated inside function
    363     }
    364 
    365     // State change: FLAG_WINDOW_UNFOCUSED
    366     if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
    367     {
    368         glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE);
    369         CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
    370     }
    371 
    372     // State change: FLAG_WINDOW_TOPMOST
    373     if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
    374     {
    375         glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE);
    376         CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
    377     }
    378 
    379     // State change: FLAG_WINDOW_ALWAYS_RUN
    380     if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
    381     {
    382         CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
    383     }
    384 
    385     // The following states can not be changed after window creation
    386 
    387     // State change: FLAG_WINDOW_TRANSPARENT
    388     if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
    389     {
    390         TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
    391     }
    392 
    393     // State change: FLAG_WINDOW_HIGHDPI
    394     if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
    395     {
    396         TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
    397     }
    398 
    399     // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
    400     if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
    401     {
    402         glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
    403         CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH;
    404     }
    405 
    406     // State change: FLAG_MSAA_4X_HINT
    407     if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
    408     {
    409         TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
    410     }
    411 
    412     // State change: FLAG_INTERLACED_HINT
    413     if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
    414     {
    415         TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
    416     }
    417 }
    418 
    419 // Clear window configuration state flags
    420 void ClearWindowState(unsigned int flags)
    421 {
    422     // Check previous state and requested state to apply required changes
    423     // NOTE: In most cases the functions already change the flags internally
    424 
    425     // State change: FLAG_VSYNC_HINT
    426     if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0))
    427     {
    428         glfwSwapInterval(0);
    429         CORE.Window.flags &= ~FLAG_VSYNC_HINT;
    430     }
    431 
    432     // State change: FLAG_BORDERLESS_WINDOWED_MODE
    433     // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running
    434     if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0))
    435     {
    436         ToggleBorderlessWindowed();     // NOTE: Window state flag updated inside function
    437     }
    438 
    439     // State change: FLAG_FULLSCREEN_MODE
    440     if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0))
    441     {
    442         ToggleFullscreen();     // NOTE: Window state flag updated inside function
    443     }
    444 
    445     // State change: FLAG_WINDOW_RESIZABLE
    446     if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
    447     {
    448         glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
    449         CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
    450     }
    451 
    452     // State change: FLAG_WINDOW_HIDDEN
    453     if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
    454     {
    455         glfwShowWindow(platform.handle);
    456         CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
    457     }
    458 
    459     // State change: FLAG_WINDOW_MINIMIZED
    460     if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
    461     {
    462         RestoreWindow();       // NOTE: Window state flag updated inside function
    463     }
    464 
    465     // State change: FLAG_WINDOW_MAXIMIZED
    466     if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
    467     {
    468         RestoreWindow();       // NOTE: Window state flag updated inside function
    469     }
    470 
    471     // State change: FLAG_WINDOW_UNDECORATED
    472     if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0))
    473     {
    474         glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
    475         CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
    476     }
    477 
    478     // State change: FLAG_WINDOW_UNFOCUSED
    479     if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
    480     {
    481         glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE);
    482         CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
    483     }
    484 
    485     // State change: FLAG_WINDOW_TOPMOST
    486     if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
    487     {
    488         glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE);
    489         CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
    490     }
    491 
    492     // State change: FLAG_WINDOW_ALWAYS_RUN
    493     if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
    494     {
    495         CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
    496     }
    497 
    498     // The following states can not be changed after window creation
    499 
    500     // State change: FLAG_WINDOW_TRANSPARENT
    501     if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
    502     {
    503         TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
    504     }
    505 
    506     // State change: FLAG_WINDOW_HIGHDPI
    507     if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
    508     {
    509         TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
    510     }
    511 
    512     // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
    513     if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
    514     {
    515         glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
    516         CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH;
    517     }
    518 
    519     // State change: FLAG_MSAA_4X_HINT
    520     if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
    521     {
    522         TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
    523     }
    524 
    525     // State change: FLAG_INTERLACED_HINT
    526     if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
    527     {
    528         TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
    529     }
    530 }
    531 
    532 // Set icon for window
    533 // NOTE 1: Image must be in RGBA format, 8bit per channel
    534 // NOTE 2: Image is scaled by the OS for all required sizes
    535 void SetWindowIcon(Image image)
    536 {
    537     if (image.data == NULL)
    538     {
    539         // Revert to the default window icon, pass in an empty image array
    540         glfwSetWindowIcon(platform.handle, 0, NULL);
    541     }
    542     else
    543     {
    544         if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
    545         {
    546             GLFWimage icon[1] = { 0 };
    547 
    548             icon[0].width = image.width;
    549             icon[0].height = image.height;
    550             icon[0].pixels = (unsigned char *)image.data;
    551 
    552             // NOTE 1: We only support one image icon
    553             // NOTE 2: The specified image data is copied before this function returns
    554             glfwSetWindowIcon(platform.handle, 1, icon);
    555         }
    556         else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
    557     }
    558 }
    559 
    560 // Set icon for window, multiple images
    561 // NOTE 1: Images must be in RGBA format, 8bit per channel
    562 // NOTE 2: The multiple images are used depending on provided sizes
    563 // Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16
    564 void SetWindowIcons(Image *images, int count)
    565 {
    566     if ((images == NULL) || (count <= 0))
    567     {
    568         // Revert to the default window icon, pass in an empty image array
    569         glfwSetWindowIcon(platform.handle, 0, NULL);
    570     }
    571     else
    572     {
    573         int valid = 0;
    574         GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage));
    575 
    576         for (int i = 0; i < count; i++)
    577         {
    578             if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
    579             {
    580                 icons[valid].width = images[i].width;
    581                 icons[valid].height = images[i].height;
    582                 icons[valid].pixels = (unsigned char *)images[i].data;
    583 
    584                 valid++;
    585             }
    586             else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
    587         }
    588         // NOTE: Images data is copied internally before this function returns
    589         glfwSetWindowIcon(platform.handle, valid, icons);
    590 
    591         RL_FREE(icons);
    592     }
    593 }
    594 
    595 // Set title for window
    596 void SetWindowTitle(const char *title)
    597 {
    598     CORE.Window.title = title;
    599     glfwSetWindowTitle(platform.handle, title);
    600 }
    601 
    602 // Set window position on screen (windowed mode)
    603 void SetWindowPosition(int x, int y)
    604 {
    605     // Update CORE.Window.position as well
    606     CORE.Window.position.x = x;
    607     CORE.Window.position.y = y;
    608     glfwSetWindowPos(platform.handle, x, y);
    609 }
    610 
    611 // Set monitor for the current window
    612 void SetWindowMonitor(int monitor)
    613 {
    614     int monitorCount = 0;
    615     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    616 
    617     if ((monitor >= 0) && (monitor < monitorCount))
    618     {
    619         if (CORE.Window.fullscreen)
    620         {
    621             TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
    622 
    623             const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
    624             glfwSetWindowMonitor(platform.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate);
    625         }
    626         else
    627         {
    628             TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
    629 
    630             // Here the render width has to be used again in case high dpi flag is enabled
    631             const int screenWidth = CORE.Window.render.width;
    632             const int screenHeight = CORE.Window.render.height;
    633             int monitorWorkareaX = 0;
    634             int monitorWorkareaY = 0;
    635             int monitorWorkareaWidth = 0;
    636             int monitorWorkareaHeight = 0;
    637             glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight);
    638 
    639             // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it
    640             if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(platform.handle, monitorWorkareaX, monitorWorkareaY);
    641             else
    642             {
    643                 const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2);
    644                 const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2);
    645                 glfwSetWindowPos(platform.handle, x, y);
    646             }
    647         }
    648     }
    649     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
    650 }
    651 
    652 // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
    653 void SetWindowMinSize(int width, int height)
    654 {
    655     CORE.Window.screenMin.width = width;
    656     CORE.Window.screenMin.height = height;
    657 
    658     int minWidth  = (CORE.Window.screenMin.width  == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width;
    659     int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height;
    660     int maxWidth  = (CORE.Window.screenMax.width  == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width;
    661     int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height;
    662 
    663     glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight);
    664 }
    665 
    666 // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
    667 void SetWindowMaxSize(int width, int height)
    668 {
    669     CORE.Window.screenMax.width = width;
    670     CORE.Window.screenMax.height = height;
    671 
    672     int minWidth  = (CORE.Window.screenMin.width  == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width;
    673     int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height;
    674     int maxWidth  = (CORE.Window.screenMax.width  == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width;
    675     int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height;
    676 
    677     glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight);
    678 }
    679 
    680 // Set window dimensions
    681 void SetWindowSize(int width, int height)
    682 {
    683     CORE.Window.screen.width = width;
    684     CORE.Window.screen.height = height;
    685 
    686     glfwSetWindowSize(platform.handle, width, height);
    687 }
    688 
    689 // Set window opacity, value opacity is between 0.0 and 1.0
    690 void SetWindowOpacity(float opacity)
    691 {
    692     if (opacity >= 1.0f) opacity = 1.0f;
    693     else if (opacity <= 0.0f) opacity = 0.0f;
    694     glfwSetWindowOpacity(platform.handle, opacity);
    695 }
    696 
    697 // Set window focused
    698 void SetWindowFocused(void)
    699 {
    700     glfwFocusWindow(platform.handle);
    701 }
    702 
    703 // Get native window handle
    704 void *GetWindowHandle(void)
    705 {
    706 #if defined(_WIN32)
    707     // NOTE: Returned handle is: void *HWND (windows.h)
    708     return glfwGetWin32Window(platform.handle);
    709 #endif
    710 #if defined(__linux__)
    711     // NOTE: Returned handle is: unsigned long Window (X.h)
    712     // typedef unsigned long XID;
    713     // typedef XID Window;
    714     //unsigned long id = (unsigned long)glfwGetX11Window(platform.handle);
    715     //return NULL;    // TODO: Find a way to return value... cast to void *?
    716     return (void *)platform.handle;
    717 #endif
    718 #if defined(__APPLE__)
    719     // NOTE: Returned handle is: (objc_object *)
    720     return (void *)glfwGetCocoaWindow(platform.handle);
    721 #endif
    722 
    723     return NULL;
    724 }
    725 
    726 // Get number of monitors
    727 int GetMonitorCount(void)
    728 {
    729     int monitorCount = 0;
    730 
    731     glfwGetMonitors(&monitorCount);
    732 
    733     return monitorCount;
    734 }
    735 
    736 // Get number of monitors
    737 int GetCurrentMonitor(void)
    738 {
    739     int index = 0;
    740     int monitorCount = 0;
    741     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    742     GLFWmonitor *monitor = NULL;
    743 
    744     if (monitorCount >= 1)
    745     {
    746         if (IsWindowFullscreen())
    747         {
    748             // Get the handle of the monitor that the specified window is in full screen on
    749             monitor = glfwGetWindowMonitor(platform.handle);
    750 
    751             for (int i = 0; i < monitorCount; i++)
    752             {
    753                 if (monitors[i] == monitor)
    754                 {
    755                     index = i;
    756                     break;
    757                 }
    758             }
    759         }
    760         else
    761         {
    762             // In case the window is between two monitors, we use below logic
    763             // to try to detect the "current monitor" for that window, note that
    764             // this is probably an overengineered solution for a very side case
    765             // trying to match SDL behaviour
    766 
    767             int closestDist = 0x7FFFFFFF;
    768 
    769             // Window center position
    770             int wcx = 0;
    771             int wcy = 0;
    772 
    773             glfwGetWindowPos(platform.handle, &wcx, &wcy);
    774             wcx += (int)CORE.Window.screen.width/2;
    775             wcy += (int)CORE.Window.screen.height/2;
    776 
    777             for (int i = 0; i < monitorCount; i++)
    778             {
    779                 // Monitor top-left position
    780                 int mx = 0;
    781                 int my = 0;
    782 
    783                 monitor = monitors[i];
    784                 glfwGetMonitorPos(monitor, &mx, &my);
    785                 const GLFWvidmode *mode = glfwGetVideoMode(monitor);
    786 
    787                 if (mode)
    788                 {
    789                     const int right = mx + mode->width - 1;
    790                     const int bottom = my + mode->height - 1;
    791 
    792                     if ((wcx >= mx) &&
    793                         (wcx <= right) &&
    794                         (wcy >= my) &&
    795                         (wcy <= bottom))
    796                     {
    797                         index = i;
    798                         break;
    799                     }
    800 
    801                     int xclosest = wcx;
    802                     if (wcx < mx) xclosest = mx;
    803                     else if (wcx > right) xclosest = right;
    804 
    805                     int yclosest = wcy;
    806                     if (wcy < my) yclosest = my;
    807                     else if (wcy > bottom) yclosest = bottom;
    808 
    809                     int dx = wcx - xclosest;
    810                     int dy = wcy - yclosest;
    811                     int dist = (dx*dx) + (dy*dy);
    812                     if (dist < closestDist)
    813                     {
    814                         index = i;
    815                         closestDist = dist;
    816                     }
    817                 }
    818                 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
    819             }
    820         }
    821     }
    822 
    823     return index;
    824 }
    825 
    826 // Get selected monitor position
    827 Vector2 GetMonitorPosition(int monitor)
    828 {
    829     int monitorCount = 0;
    830     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    831 
    832     if ((monitor >= 0) && (monitor < monitorCount))
    833     {
    834         int x, y;
    835         glfwGetMonitorPos(monitors[monitor], &x, &y);
    836 
    837         return (Vector2){ (float)x, (float)y };
    838     }
    839     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
    840     return (Vector2){ 0, 0 };
    841 }
    842 
    843 // Get selected monitor width (currently used by monitor)
    844 int GetMonitorWidth(int monitor)
    845 {
    846     int width = 0;
    847     int monitorCount = 0;
    848     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    849 
    850     if ((monitor >= 0) && (monitor < monitorCount))
    851     {
    852         const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
    853 
    854         if (mode) width = mode->width;
    855         else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
    856     }
    857     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
    858 
    859     return width;
    860 }
    861 
    862 // Get selected monitor height (currently used by monitor)
    863 int GetMonitorHeight(int monitor)
    864 {
    865     int height = 0;
    866     int monitorCount = 0;
    867     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    868 
    869     if ((monitor >= 0) && (monitor < monitorCount))
    870     {
    871         const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
    872 
    873         if (mode) height = mode->height;
    874         else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
    875     }
    876     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
    877 
    878     return height;
    879 }
    880 
    881 // Get selected monitor physical width in millimetres
    882 int GetMonitorPhysicalWidth(int monitor)
    883 {
    884     int width = 0;
    885     int monitorCount = 0;
    886     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    887 
    888     if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], &width, NULL);
    889     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
    890 
    891     return width;
    892 }
    893 
    894 // Get selected monitor physical height in millimetres
    895 int GetMonitorPhysicalHeight(int monitor)
    896 {
    897     int height = 0;
    898     int monitorCount = 0;
    899     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    900 
    901     if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &height);
    902     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
    903 
    904     return height;
    905 }
    906 
    907 // Get selected monitor refresh rate
    908 int GetMonitorRefreshRate(int monitor)
    909 {
    910     int refresh = 0;
    911     int monitorCount = 0;
    912     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    913 
    914     if ((monitor >= 0) && (monitor < monitorCount))
    915     {
    916         const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]);
    917         refresh = vidmode->refreshRate;
    918     }
    919     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
    920 
    921     return refresh;
    922 }
    923 
    924 // Get the human-readable, UTF-8 encoded name of the selected monitor
    925 const char *GetMonitorName(int monitor)
    926 {
    927     int monitorCount = 0;
    928     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
    929 
    930     if ((monitor >= 0) && (monitor < monitorCount))
    931     {
    932         return glfwGetMonitorName(monitors[monitor]);
    933     }
    934     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
    935     return "";
    936 }
    937 
    938 // Get window position XY on monitor
    939 Vector2 GetWindowPosition(void)
    940 {
    941     int x = 0;
    942     int y = 0;
    943 
    944     glfwGetWindowPos(platform.handle, &x, &y);
    945 
    946     return (Vector2){ (float)x, (float)y };
    947 }
    948 
    949 // Get window scale DPI factor for current monitor
    950 Vector2 GetWindowScaleDPI(void)
    951 {
    952     Vector2 scale = {0};
    953     glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y);
    954     return scale;
    955 }
    956 
    957 // Set clipboard text content
    958 void SetClipboardText(const char *text)
    959 {
    960     glfwSetClipboardString(platform.handle, text);
    961 }
    962 
    963 // Get clipboard text content
    964 // NOTE: returned string is allocated and freed by GLFW
    965 const char *GetClipboardText(void)
    966 {
    967     return glfwGetClipboardString(platform.handle);
    968 }
    969 
    970 #if defined(SUPPORT_CLIPBOARD_IMAGE)
    971 // Get clipboard image
    972 Image GetClipboardImage(void)
    973 {
    974     Image image = {0};
    975     unsigned long long int dataSize = 0;
    976     void* fileData = NULL;
    977 
    978 #ifdef _WIN32
    979     int width, height;
    980     fileData  = (void*)Win32GetClipboardImageData(&width, &height, &dataSize);
    981 #else
    982     TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_GLFW doesn't implement `GetClipboardImage` for this OS");
    983 #endif
    984 
    985     if (fileData == NULL)
    986     {
    987         TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data.");
    988     }
    989     else
    990     {
    991         image = LoadImageFromMemory(".bmp", fileData, (int)dataSize);
    992     }
    993     return image;
    994 }
    995 #endif // SUPPORT_CLIPBOARD_IMAGE
    996 
    997 // Show mouse cursor
    998 void ShowCursor(void)
    999 {
   1000     glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
   1001     CORE.Input.Mouse.cursorHidden = false;
   1002 }
   1003 
   1004 // Hides mouse cursor
   1005 void HideCursor(void)
   1006 {
   1007     glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
   1008     CORE.Input.Mouse.cursorHidden = true;
   1009 }
   1010 
   1011 // Enables cursor (unlock cursor)
   1012 void EnableCursor(void)
   1013 {
   1014     glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
   1015 
   1016     // Set cursor position in the middle
   1017     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
   1018 
   1019     if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
   1020 
   1021     CORE.Input.Mouse.cursorHidden = false;
   1022 }
   1023 
   1024 // Disables cursor (lock cursor)
   1025 void DisableCursor(void)
   1026 {
   1027     glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
   1028 
   1029     // Set cursor position in the middle
   1030     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
   1031 
   1032     if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
   1033 
   1034     CORE.Input.Mouse.cursorHidden = true;
   1035 }
   1036 
   1037 // Swap back buffer with front buffer (screen drawing)
   1038 void SwapScreenBuffer(void)
   1039 {
   1040     glfwSwapBuffers(platform.handle);
   1041 }
   1042 
   1043 //----------------------------------------------------------------------------------
   1044 // Module Functions Definition: Misc
   1045 //----------------------------------------------------------------------------------
   1046 
   1047 // Get elapsed time measure in seconds since InitTimer()
   1048 double GetTime(void)
   1049 {
   1050     double time = glfwGetTime();   // Elapsed time since glfwInit()
   1051     return time;
   1052 }
   1053 
   1054 // Open URL with default system browser (if available)
   1055 // NOTE: This function is only safe to use if you control the URL given.
   1056 // A user could craft a malicious string performing another action.
   1057 // Only call this function yourself not with user input or make sure to check the string yourself.
   1058 // Ref: https://github.com/raysan5/raylib/issues/686
   1059 void OpenURL(const char *url)
   1060 {
   1061     // Security check to (partially) avoid malicious code
   1062     if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
   1063     else
   1064     {
   1065         char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
   1066 #if defined(_WIN32)
   1067         sprintf(cmd, "explorer \"%s\"", url);
   1068 #endif
   1069 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
   1070         sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
   1071 #endif
   1072 #if defined(__APPLE__)
   1073         sprintf(cmd, "open '%s'", url);
   1074 #endif
   1075         int result = system(cmd);
   1076         if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
   1077         RL_FREE(cmd);
   1078     }
   1079 }
   1080 
   1081 //----------------------------------------------------------------------------------
   1082 // Module Functions Definition: Inputs
   1083 //----------------------------------------------------------------------------------
   1084 
   1085 // Set internal gamepad mappings
   1086 int SetGamepadMappings(const char *mappings)
   1087 {
   1088     return glfwUpdateGamepadMappings(mappings);
   1089 }
   1090 
   1091 // Set gamepad vibration
   1092 void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
   1093 {
   1094     TRACELOG(LOG_WARNING, "GamepadSetVibration() not available on target platform");
   1095 }
   1096 
   1097 // Set mouse position XY
   1098 void SetMousePosition(int x, int y)
   1099 {
   1100     CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
   1101     CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
   1102 
   1103     // NOTE: emscripten not implemented
   1104     glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
   1105 }
   1106 
   1107 // Set mouse cursor
   1108 void SetMouseCursor(int cursor)
   1109 {
   1110     CORE.Input.Mouse.cursor = cursor;
   1111     if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(platform.handle, NULL);
   1112     else
   1113     {
   1114         // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values
   1115         glfwSetCursor(platform.handle, glfwCreateStandardCursor(0x00036000 + cursor));
   1116     }
   1117 }
   1118 
   1119 // Get physical key name.
   1120 const char *GetKeyName(int key)
   1121 {
   1122     return glfwGetKeyName(key, glfwGetKeyScancode(key));
   1123 }
   1124 
   1125 // Register all input events
   1126 void PollInputEvents(void)
   1127 {
   1128 #if defined(SUPPORT_GESTURES_SYSTEM)
   1129     // NOTE: Gestures update must be called every frame to reset gestures correctly
   1130     // because ProcessGestureEvent() is just called on an event, not every frame
   1131     UpdateGestures();
   1132 #endif
   1133 
   1134     // Reset keys/chars pressed registered
   1135     CORE.Input.Keyboard.keyPressedQueueCount = 0;
   1136     CORE.Input.Keyboard.charPressedQueueCount = 0;
   1137 
   1138     // Reset last gamepad button/axis registered state
   1139     CORE.Input.Gamepad.lastButtonPressed = 0;       // GAMEPAD_BUTTON_UNKNOWN
   1140     //CORE.Input.Gamepad.axisCount = 0;
   1141 
   1142     // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback)
   1143 
   1144     // Register previous keys states
   1145     for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
   1146     {
   1147         CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
   1148         CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
   1149     }
   1150 
   1151     // Register previous mouse states
   1152     for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
   1153 
   1154     // Register previous mouse wheel state
   1155     CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
   1156     CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
   1157 
   1158     // Register previous mouse position
   1159     CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
   1160 
   1161     // Register previous touch states
   1162     for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
   1163 
   1164     // Reset touch positions
   1165     //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
   1166 
   1167     // Map touch position to mouse position for convenience
   1168     // WARNING: If the target desktop device supports touch screen, this behaviour should be reviewed!
   1169     // TODO: GLFW does not support multi-touch input just yet
   1170     // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
   1171     // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
   1172     CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
   1173 
   1174     // Check if gamepads are ready
   1175     // NOTE: We do it here in case of disconnection
   1176     for (int i = 0; i < MAX_GAMEPADS; i++)
   1177     {
   1178         if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true;
   1179         else CORE.Input.Gamepad.ready[i] = false;
   1180     }
   1181 
   1182     // Register gamepads buttons events
   1183     for (int i = 0; i < MAX_GAMEPADS; i++)
   1184     {
   1185         if (CORE.Input.Gamepad.ready[i])     // Check if gamepad is available
   1186         {
   1187             // Register previous gamepad states
   1188             for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
   1189 
   1190             // Get current gamepad state
   1191             // NOTE: There is no callback available, so we get it manually
   1192             GLFWgamepadstate state = { 0 };
   1193             glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller
   1194 
   1195             const unsigned char *buttons = state.buttons;
   1196 
   1197             for (int k = 0; (buttons != NULL) && (k < MAX_GAMEPAD_BUTTONS); k++)
   1198             {
   1199                 int button = -1;        // GamepadButton enum values assigned
   1200 
   1201                 switch (k)
   1202                 {
   1203                     case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
   1204                     case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
   1205                     case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
   1206                     case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
   1207 
   1208                     case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
   1209                     case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
   1210 
   1211                     case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
   1212                     case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
   1213                     case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
   1214 
   1215                     case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
   1216                     case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
   1217                     case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
   1218                     case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
   1219 
   1220                     case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
   1221                     case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
   1222                     default: break;
   1223                 }
   1224 
   1225                 if (button != -1)   // Check for valid button
   1226                 {
   1227                     if (buttons[k] == GLFW_PRESS)
   1228                     {
   1229                         CORE.Input.Gamepad.currentButtonState[i][button] = 1;
   1230                         CORE.Input.Gamepad.lastButtonPressed = button;
   1231                     }
   1232                     else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
   1233                 }
   1234             }
   1235 
   1236             // Get current axis state
   1237             const float *axes = state.axes;
   1238 
   1239             for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1); k++)
   1240             {
   1241                 CORE.Input.Gamepad.axisState[i][k] = axes[k];
   1242             }
   1243 
   1244             // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis)
   1245             CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f);
   1246             CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f);
   1247 
   1248             CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1;
   1249         }
   1250     }
   1251 
   1252     CORE.Window.resizedLastFrame = false;
   1253 
   1254     if (CORE.Window.eventWaiting) glfwWaitEvents();     // Wait for in input events before continue (drawing is paused)
   1255     else glfwPollEvents();      // Poll input events: keyboard/mouse/window events (callbacks) -> Update keys state
   1256 
   1257     // While window minimized, stop loop execution
   1258     while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents();
   1259 
   1260     CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle);
   1261 
   1262     // Reset close status for next frame
   1263     glfwSetWindowShouldClose(platform.handle, GLFW_FALSE);
   1264 }
   1265 
   1266 //----------------------------------------------------------------------------------
   1267 // Module Internal Functions Definition
   1268 //----------------------------------------------------------------------------------
   1269 
   1270 static void SetDimensionsFromMonitor(GLFWmonitor *monitor)
   1271 {
   1272   const GLFWvidmode *mode = glfwGetVideoMode(monitor);
   1273 
   1274   // Default display resolution to that of the current mode
   1275   CORE.Window.display.width = mode->width;
   1276   CORE.Window.display.height = mode->height;
   1277 
   1278   // Set screen width/height to the display width/height if they are 0
   1279   if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
   1280   if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
   1281 }
   1282 
   1283 // Initialize platform: graphics, inputs and more
   1284 int InitPlatform(void)
   1285 {
   1286     glfwSetErrorCallback(ErrorCallback);
   1287 /*
   1288     // TODO: Setup GLFW custom allocators to match raylib ones
   1289     const GLFWallocator allocator = {
   1290         .allocate = MemAlloc,
   1291         .deallocate = MemFree,
   1292         .reallocate = MemRealloc,
   1293         .user = NULL
   1294     };
   1295 
   1296     glfwInitAllocator(&allocator);
   1297 */
   1298 
   1299 #if defined(__APPLE__)
   1300     glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
   1301 #endif
   1302     // Initialize GLFW internal global state
   1303     int result = glfwInit();
   1304     if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; }
   1305 
   1306     // Initialize graphic device: display/window and graphic context
   1307     //----------------------------------------------------------------------------
   1308     glfwDefaultWindowHints();                       // Set default windows hints
   1309     //glfwWindowHint(GLFW_RED_BITS, 8);             // Framebuffer red color component bits
   1310     //glfwWindowHint(GLFW_GREEN_BITS, 8);           // Framebuffer green color component bits
   1311     //glfwWindowHint(GLFW_BLUE_BITS, 8);            // Framebuffer blue color component bits
   1312     //glfwWindowHint(GLFW_ALPHA_BITS, 8);           // Framebuffer alpha color component bits
   1313     //glfwWindowHint(GLFW_DEPTH_BITS, 24);          // Depthbuffer bits
   1314     //glfwWindowHint(GLFW_REFRESH_RATE, 0);         // Refresh rate for fullscreen window
   1315     //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
   1316     //glfwWindowHint(GLFW_AUX_BUFFERS, 0);          // Number of auxiliar buffers
   1317 
   1318     // Disable GlFW auto iconify behaviour
   1319     // Auto Iconify automatically minimizes (iconifies) the window if the window loses focus
   1320     // additionally auto iconify restores the hardware resolution of the monitor if the window that loses focus is a fullscreen window
   1321     glfwWindowHint(GLFW_AUTO_ICONIFY, 0);
   1322 
   1323     // Check window creation flags
   1324     if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
   1325 
   1326     if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
   1327     else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);     // Window initially hidden
   1328 
   1329     if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
   1330     else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE);   // Decorated window
   1331 
   1332     if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
   1333     else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);  // Avoid window being resizable
   1334 
   1335     // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
   1336     if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
   1337 
   1338     // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
   1339     if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
   1340 
   1341     if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
   1342     else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
   1343 
   1344     if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
   1345     else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
   1346 
   1347     // NOTE: Some GLFW flags are not supported on HTML5
   1348     if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);     // Transparent framebuffer
   1349     else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE);  // Opaque framebuffer
   1350 
   1351     if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
   1352     {
   1353         // Resize window content area based on the monitor content scale.
   1354         // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11.
   1355         // On platforms like macOS the resolution of the framebuffer is changed independently of the window size.
   1356         glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);   // Scale content area based on the monitor content scale where window is placed on
   1357 #if defined(__APPLE__)
   1358         glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
   1359 #endif
   1360     }
   1361     else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
   1362 
   1363     // Mouse passthrough
   1364     if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
   1365     else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
   1366 
   1367     if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
   1368     {
   1369         // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
   1370         TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
   1371         glfwWindowHint(GLFW_SAMPLES, 4);   // Tries to enable multisampling x4 (MSAA), default is 0
   1372     }
   1373 
   1374     // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version
   1375     // with backward compatibility to older OpenGL versions.
   1376     // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context.
   1377 
   1378     // Check selection OpenGL version
   1379     if (rlGetVersion() == RL_OPENGL_21)
   1380     {
   1381         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);          // Choose OpenGL major version (just hint)
   1382         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);          // Choose OpenGL minor version (just hint)
   1383     }
   1384     else if (rlGetVersion() == RL_OPENGL_33)
   1385     {
   1386         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);          // Choose OpenGL major version (just hint)
   1387         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);          // Choose OpenGL minor version (just hint)
   1388         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
   1389                                                                        // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
   1390 #if defined(__APPLE__)
   1391         glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);  // OSX Requires forward compatibility
   1392 #else
   1393         glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above!
   1394 #endif
   1395         //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
   1396     }
   1397     else if (rlGetVersion() == RL_OPENGL_43)
   1398     {
   1399         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);          // Choose OpenGL major version (just hint)
   1400         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);          // Choose OpenGL minor version (just hint)
   1401         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
   1402         glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
   1403 #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
   1404         glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);   // Enable OpenGL Debug Context
   1405 #endif
   1406     }
   1407     else if (rlGetVersion() == RL_OPENGL_ES_20)                 // Request OpenGL ES 2.0 context
   1408     {
   1409         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
   1410         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
   1411         glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
   1412         glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
   1413     }
   1414     else if (rlGetVersion() == RL_OPENGL_ES_30)                 // Request OpenGL ES 3.0 context
   1415     {
   1416         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
   1417         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
   1418         glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
   1419         glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
   1420     }
   1421 
   1422     // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions.
   1423     // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn.
   1424     // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience.
   1425     // REF: https://github.com/raysan5/raylib/issues/1554
   1426     glfwSetJoystickCallback(NULL);
   1427 
   1428     GLFWmonitor *monitor = NULL;
   1429     if (CORE.Window.fullscreen)
   1430     {
   1431         // According to glfwCreateWindow(), if the user does not have a choice, fullscreen applications
   1432         // should default to the primary monitor.
   1433 
   1434         monitor = glfwGetPrimaryMonitor();
   1435         if (!monitor)
   1436         {
   1437           TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
   1438           return -1;
   1439         }
   1440 
   1441         SetDimensionsFromMonitor(monitor);
   1442 
   1443         // Remember center for switching from fullscreen to window
   1444         if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
   1445         {
   1446             // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
   1447             // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
   1448             CORE.Window.position.x = CORE.Window.display.width/4;
   1449             CORE.Window.position.y = CORE.Window.display.height/4;
   1450         }
   1451         else
   1452         {
   1453             CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
   1454             CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
   1455         }
   1456 
   1457         if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
   1458         if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
   1459 
   1460         // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor
   1461         int count = 0;
   1462         const GLFWvidmode *modes = glfwGetVideoModes(monitor, &count);
   1463 
   1464         // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height
   1465         for (int i = 0; i < count; i++)
   1466         {
   1467             if ((unsigned int)modes[i].width >= CORE.Window.screen.width)
   1468             {
   1469                 if ((unsigned int)modes[i].height >= CORE.Window.screen.height)
   1470                 {
   1471                     CORE.Window.display.width = modes[i].width;
   1472                     CORE.Window.display.height = modes[i].height;
   1473                     break;
   1474                 }
   1475             }
   1476         }
   1477 
   1478         TRACELOG(LOG_INFO, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
   1479 
   1480         // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
   1481         // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
   1482         // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
   1483         // by the sides to fit all monitor space...
   1484 
   1485         // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
   1486         // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
   1487         // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
   1488         // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
   1489         // HighDPI monitors are properly considered in a following similar function: SetupViewport()
   1490         SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
   1491 
   1492         platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL);
   1493 
   1494         // NOTE: Full-screen change, not working properly...
   1495         //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
   1496     }
   1497     else
   1498     {
   1499         // No-fullscreen window creation
   1500         bool requestWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0);
   1501 
   1502         // Default to at least one pixel in size, as creation with a zero dimension is not allowed.
   1503         int creationWidth = CORE.Window.screen.width != 0 ? CORE.Window.screen.width : 1;
   1504         int creationHeight = CORE.Window.screen.height != 0 ? CORE.Window.screen.height : 1;
   1505 
   1506         platform.handle = glfwCreateWindow(creationWidth, creationHeight, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
   1507 
   1508         // After the window was created, determine the monitor that the window manager assigned.
   1509         // Derive display sizes, and, if possible, window size in case it was zero at beginning.
   1510 
   1511         int monitorCount = 0;
   1512         int monitorIndex = GetCurrentMonitor();
   1513         GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
   1514 
   1515         if (monitorIndex < monitorCount)
   1516         {
   1517             monitor = monitors[monitorIndex];
   1518             SetDimensionsFromMonitor(monitor);
   1519 
   1520             if (requestWindowedFullscreen) glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height);
   1521         }
   1522         else
   1523         {
   1524             // The monitor for the window-manager-created window can not be determined, so it can not be centered.
   1525             glfwTerminate();
   1526             TRACELOG(LOG_WARNING, "GLFW: Failed to determine Monitor to center Window");
   1527             return -1;
   1528         }
   1529 
   1530         if (platform.handle)
   1531         {
   1532             CORE.Window.render.width = CORE.Window.screen.width;
   1533             CORE.Window.render.height = CORE.Window.screen.height;
   1534         }
   1535     }
   1536 
   1537     if (!platform.handle)
   1538     {
   1539         glfwTerminate();
   1540         TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
   1541         return -1;
   1542     }
   1543 
   1544     glfwMakeContextCurrent(platform.handle);
   1545     result = glfwGetError(NULL);
   1546 
   1547     // Check context activation
   1548     if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR))
   1549     {
   1550         CORE.Window.ready = true;
   1551 
   1552         glfwSwapInterval(0);        // No V-Sync by default
   1553 
   1554         // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
   1555         // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need
   1556         // to be activated on web platforms since VSync is enforced there.
   1557         if (CORE.Window.flags & FLAG_VSYNC_HINT)
   1558         {
   1559             // WARNING: It seems to hit a critical render path in Intel HD Graphics
   1560             glfwSwapInterval(1);
   1561             TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC");
   1562         }
   1563 
   1564         int fbWidth = CORE.Window.screen.width;
   1565         int fbHeight = CORE.Window.screen.height;
   1566 
   1567         if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
   1568         {
   1569             // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling.
   1570             // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
   1571     #if !defined(__APPLE__)
   1572             glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight);
   1573 
   1574             // Screen scaling matrix is required in case desired screen area is different from display area
   1575             CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
   1576 
   1577             // Mouse input scaling for the new screen size
   1578             SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight);
   1579     #endif
   1580         }
   1581 
   1582         CORE.Window.render.width = fbWidth;
   1583         CORE.Window.render.height = fbHeight;
   1584         CORE.Window.currentFbo.width = fbWidth;
   1585         CORE.Window.currentFbo.height = fbHeight;
   1586 
   1587         TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
   1588         TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
   1589         TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
   1590         TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
   1591         TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
   1592     }
   1593     else
   1594     {
   1595         TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
   1596         return -1;
   1597     }
   1598 
   1599     if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
   1600 
   1601     // If graphic device is no properly initialized, we end program
   1602     if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
   1603     else
   1604     {
   1605         // Try to center window on screen but avoiding window-bar outside of screen
   1606         int monitorX = 0;
   1607         int monitorY = 0;
   1608         int monitorWidth = 0;
   1609         int monitorHeight = 0;
   1610         glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight);
   1611 
   1612         // Here CORE.Window.render.width/height should be used instead of CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled.
   1613         int posX = monitorX + (monitorWidth - (int)CORE.Window.render.width)/2;
   1614         int posY = monitorY + (monitorHeight - (int)CORE.Window.render.height)/2;
   1615         if (posX < monitorX) posX = monitorX;
   1616         if (posY < monitorY) posY = monitorY;
   1617         SetWindowPosition(posX, posY);
   1618 
   1619         // Update CORE.Window.position here so it is correct from the start
   1620         CORE.Window.position.x = posX;
   1621         CORE.Window.position.y = posY;
   1622     }
   1623 
   1624     // Load OpenGL extensions
   1625     // NOTE: GL procedures address loader is required to load extensions
   1626     rlLoadExtensions(glfwGetProcAddress);
   1627     //----------------------------------------------------------------------------
   1628 
   1629     // Initialize input events callbacks
   1630     //----------------------------------------------------------------------------
   1631     // Set window callback events
   1632     glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback);      // NOTE: Resizing not allowed by default!
   1633     glfwSetWindowPosCallback(platform.handle, WindowPosCallback);
   1634     glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback);
   1635     glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
   1636     glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
   1637     glfwSetDropCallback(platform.handle, WindowDropCallback);
   1638 
   1639     if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
   1640     {
   1641        glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
   1642     }
   1643 
   1644     // Set input callback events
   1645     glfwSetKeyCallback(platform.handle, KeyCallback);
   1646     glfwSetCharCallback(platform.handle, CharCallback);
   1647     glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
   1648     glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback);   // Track mouse position changes
   1649     glfwSetScrollCallback(platform.handle, MouseScrollCallback);
   1650     glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
   1651     glfwSetJoystickCallback(JoystickCallback);
   1652 
   1653     glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE);    // Enable lock keys modifiers (CAPS, NUM)
   1654 
   1655     // Retrieve gamepad names
   1656     for (int i = 0; i < MAX_GAMEPADS; i++)
   1657     {
   1658         if (glfwJoystickPresent(i)) strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i));
   1659     }
   1660     //----------------------------------------------------------------------------
   1661 
   1662     // Initialize timming system
   1663     //----------------------------------------------------------------------------
   1664     InitTimer();
   1665     //----------------------------------------------------------------------------
   1666 
   1667     // Initialize storage system
   1668     //----------------------------------------------------------------------------
   1669     CORE.Storage.basePath = GetWorkingDirectory();
   1670     //----------------------------------------------------------------------------
   1671 
   1672 #if defined(__NetBSD__)
   1673     // Workaround for NetBSD
   1674     char *glfwPlatform = "X11";
   1675 #else
   1676     char *glfwPlatform = "";
   1677     switch (glfwGetPlatform())
   1678     {
   1679         case GLFW_PLATFORM_WIN32: glfwPlatform = "Win32"; break;
   1680         case GLFW_PLATFORM_COCOA: glfwPlatform = "Cocoa"; break;
   1681         case GLFW_PLATFORM_WAYLAND: glfwPlatform = "Wayland"; break;
   1682         case GLFW_PLATFORM_X11: glfwPlatform = "X11"; break;
   1683         case GLFW_PLATFORM_NULL: glfwPlatform = "Null"; break;
   1684         default: break;
   1685     }
   1686 #endif
   1687 
   1688     TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW - %s): Initialized successfully", glfwPlatform);
   1689 
   1690     return 0;
   1691 }
   1692 
   1693 // Close platform
   1694 void ClosePlatform(void)
   1695 {
   1696     glfwDestroyWindow(platform.handle);
   1697     glfwTerminate();
   1698 
   1699 #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
   1700     timeEndPeriod(1);           // Restore time period
   1701 #endif
   1702 }
   1703 
   1704 // GLFW3 Error Callback, runs on GLFW3 error
   1705 static void ErrorCallback(int error, const char *description)
   1706 {
   1707     TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
   1708 }
   1709 
   1710 // GLFW3 WindowSize Callback, runs when window is resizedLastFrame
   1711 // NOTE: Window resizing not allowed by default
   1712 static void WindowSizeCallback(GLFWwindow *window, int width, int height)
   1713 {
   1714     // Reset viewport and projection matrix for new size
   1715     SetupViewport(width, height);
   1716 
   1717     CORE.Window.currentFbo.width = width;
   1718     CORE.Window.currentFbo.height = height;
   1719     CORE.Window.resizedLastFrame = true;
   1720 
   1721     if (IsWindowFullscreen()) return;
   1722 
   1723     // Set current screen size
   1724 
   1725     CORE.Window.screen.width = width;
   1726     CORE.Window.screen.height = height;
   1727 
   1728     // NOTE: Postprocessing texture is not scaled to new size
   1729 }
   1730 static void WindowPosCallback(GLFWwindow* window, int x, int y)
   1731 {
   1732     // Set current window position
   1733     CORE.Window.position.x = x;
   1734     CORE.Window.position.y = y;
   1735 }
   1736 static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
   1737 {
   1738     CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
   1739 }
   1740 
   1741 // GLFW3 WindowIconify Callback, runs when window is minimized/restored
   1742 static void WindowIconifyCallback(GLFWwindow *window, int iconified)
   1743 {
   1744     if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;  // The window was iconified
   1745     else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;           // The window was restored
   1746 }
   1747 
   1748 // GLFW3 WindowMaximize Callback, runs when window is maximized/restored
   1749 static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
   1750 {
   1751     if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;  // The window was maximized
   1752     else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;           // The window was restored
   1753 }
   1754 
   1755 // GLFW3 WindowFocus Callback, runs when window get/lose focus
   1756 static void WindowFocusCallback(GLFWwindow *window, int focused)
   1757 {
   1758     if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;   // The window was focused
   1759     else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;            // The window lost focus
   1760 }
   1761 
   1762 // GLFW3 Window Drop Callback, runs when drop files into window
   1763 static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
   1764 {
   1765     if (count > 0)
   1766     {
   1767         // In case previous dropped filepaths have not been freed, we free them
   1768         if (CORE.Window.dropFileCount > 0)
   1769         {
   1770             for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]);
   1771 
   1772             RL_FREE(CORE.Window.dropFilepaths);
   1773 
   1774             CORE.Window.dropFileCount = 0;
   1775             CORE.Window.dropFilepaths = NULL;
   1776         }
   1777 
   1778         // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy
   1779         CORE.Window.dropFileCount = count;
   1780         CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *));
   1781 
   1782         for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
   1783         {
   1784             CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
   1785             strcpy(CORE.Window.dropFilepaths[i], paths[i]);
   1786         }
   1787     }
   1788 }
   1789 
   1790 // GLFW3 Keyboard Callback, runs on key pressed
   1791 static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
   1792 {
   1793     if (key < 0) return;    // Security check, macOS fn key generates -1
   1794 
   1795     // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
   1796     // to work properly with our implementation (IsKeyDown/IsKeyUp checks)
   1797     if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
   1798     else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
   1799     else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
   1800 
   1801     // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys
   1802     if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) ||
   1803         ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1;
   1804 
   1805     // Check if there is space available in the key queue
   1806     if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS))
   1807     {
   1808         // Add character to the queue
   1809         CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
   1810         CORE.Input.Keyboard.keyPressedQueueCount++;
   1811     }
   1812 
   1813     // Check the exit key to set close window
   1814     if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
   1815 }
   1816 
   1817 // GLFW3 Char Callback, get unicode codepoint value
   1818 static void CharCallback(GLFWwindow *window, unsigned int codepoint)
   1819 {
   1820     //TRACELOG(LOG_DEBUG, "Char Callback: Codepoint: %i", codepoint);
   1821 
   1822     // NOTE: Registers any key down considering OS keyboard layout but
   1823     // does not detect action events, those should be managed by user...
   1824     // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
   1825     // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
   1826 
   1827     // Check if there is space available in the queue
   1828     if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
   1829     {
   1830         // Add character to the queue
   1831         CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint;
   1832         CORE.Input.Keyboard.charPressedQueueCount++;
   1833     }
   1834 }
   1835 
   1836 // GLFW3 Mouse Button Callback, runs on mouse button pressed
   1837 static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
   1838 {
   1839     // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
   1840     // but future releases may add more actions (i.e. GLFW_REPEAT)
   1841     CORE.Input.Mouse.currentButtonState[button] = action;
   1842     CORE.Input.Touch.currentTouchState[button] = action;
   1843 
   1844 #if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
   1845     // Process mouse events as touches to be able to use mouse-gestures
   1846     GestureEvent gestureEvent = { 0 };
   1847 
   1848     // Register touch actions
   1849     if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
   1850     else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP;
   1851 
   1852     // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback()
   1853 
   1854     // Assign a pointer ID
   1855     gestureEvent.pointId[0] = 0;
   1856 
   1857     // Register touch points count
   1858     gestureEvent.pointCount = 1;
   1859 
   1860     // Register touch points position, only one point registered
   1861     gestureEvent.position[0] = GetMousePosition();
   1862 
   1863     // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
   1864     gestureEvent.position[0].x /= (float)GetScreenWidth();
   1865     gestureEvent.position[0].y /= (float)GetScreenHeight();
   1866 
   1867     // Gesture data is sent to gestures-system for processing
   1868     ProcessGestureEvent(gestureEvent);
   1869 #endif
   1870 }
   1871 
   1872 // GLFW3 Cursor Position Callback, runs on mouse move
   1873 static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
   1874 {
   1875     CORE.Input.Mouse.currentPosition.x = (float)x;
   1876     CORE.Input.Mouse.currentPosition.y = (float)y;
   1877     CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
   1878 
   1879 #if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
   1880     // Process mouse events as touches to be able to use mouse-gestures
   1881     GestureEvent gestureEvent = { 0 };
   1882 
   1883     gestureEvent.touchAction = TOUCH_ACTION_MOVE;
   1884 
   1885     // Assign a pointer ID
   1886     gestureEvent.pointId[0] = 0;
   1887 
   1888     // Register touch points count
   1889     gestureEvent.pointCount = 1;
   1890 
   1891     // Register touch points position, only one point registered
   1892     gestureEvent.position[0] = CORE.Input.Touch.position[0];
   1893 
   1894     // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
   1895     gestureEvent.position[0].x /= (float)GetScreenWidth();
   1896     gestureEvent.position[0].y /= (float)GetScreenHeight();
   1897 
   1898     // Gesture data is sent to gestures-system for processing
   1899     ProcessGestureEvent(gestureEvent);
   1900 #endif
   1901 }
   1902 
   1903 // GLFW3 Scrolling Callback, runs on mouse wheel
   1904 static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
   1905 {
   1906     CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset };
   1907 }
   1908 
   1909 // GLFW3 CursorEnter Callback, when cursor enters the window
   1910 static void CursorEnterCallback(GLFWwindow *window, int enter)
   1911 {
   1912     if (enter) CORE.Input.Mouse.cursorOnScreen = true;
   1913     else CORE.Input.Mouse.cursorOnScreen = false;
   1914 }
   1915 
   1916 // GLFW3 Joystick Connected/Disconnected Callback
   1917 static void JoystickCallback(int jid, int event)
   1918 {
   1919     if (event == GLFW_CONNECTED)
   1920     {
   1921         strcpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid));
   1922     }
   1923     else if (event == GLFW_DISCONNECTED)
   1924     {
   1925         memset(CORE.Input.Gamepad.name[jid], 0, 64);
   1926     }
   1927 }
   1928 
   1929 #ifdef _WIN32
   1930 #   define WIN32_CLIPBOARD_IMPLEMENTATION
   1931 #   include "../external/win32_clipboard.h"
   1932 #endif
   1933 // EOF