github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/rcore.c (about)

     1  /**********************************************************************************************
     2  *
     3  *   rcore - Basic functions to manage windows, OpenGL context and input on multiple platforms
     4  *
     5  *   PLATFORMS SUPPORTED:
     6  *       - PLATFORM_DESKTOP: Windows (Win32, Win64)
     7  *       - PLATFORM_DESKTOP: Linux (X11 desktop mode)
     8  *       - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
     9  *       - PLATFORM_DESKTOP: OSX/macOS
    10  *       - PLATFORM_ANDROID: Android (ARM, ARM64)
    11  *       - PLATFORM_RPI:     Raspberry Pi 0,1,2,3 (Raspbian, native mode)
    12  *       - PLATFORM_DRM:     Linux native mode, including Raspberry Pi 4 with V3D fkms driver
    13  *       - PLATFORM_WEB:     HTML5 with WebAssembly
    14  *
    15  *   CONFIGURATION:
    16  *
    17  *   #define PLATFORM_DESKTOP
    18  *       Windowing and input system configured for desktop platforms: Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly
    19  *       NOTE: Oculus Rift CV1 requires PLATFORM_DESKTOP for mirror rendering - View [rlgl] module to enable it
    20  *
    21  *   #define PLATFORM_ANDROID
    22  *       Windowing and input system configured for Android device, app activity managed internally in this module.
    23  *       NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL
    24  *
    25  *   #define PLATFORM_RPI (deprecated - RPI OS Buster only)
    26  *       Windowing and input system configured for Raspberry Pi in native mode (no XWindow required),
    27  *       graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
    28  *       WARNING: This platform is deprecated, since RPI OS Bullseye, the old Dispmanx libraries are not
    29  *       supported and you must be using PLATFORM_DRM
    30  *
    31  *   #define PLATFORM_DRM
    32  *       Windowing and input system configured for DRM native mode (RPI4 and other devices)
    33  *       graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
    34  *
    35  *   #define PLATFORM_WEB
    36  *       Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js
    37  *       using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code.
    38  *
    39  *   #define SUPPORT_DEFAULT_FONT (default)
    40  *       Default font is loaded on window initialization to be available for the user to render simple text.
    41  *       NOTE: If enabled, uses external module functions to load default raylib font (module: text)
    42  *
    43  *   #define SUPPORT_CAMERA_SYSTEM
    44  *       Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital
    45  *
    46  *   #define SUPPORT_GESTURES_SYSTEM
    47  *       Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
    48  *
    49  *   #define SUPPORT_MOUSE_GESTURES
    50  *       Mouse gestures are directly mapped like touches and processed by gestures system.
    51  *
    52  *   #define SUPPORT_TOUCH_AS_MOUSE
    53  *       Touch input and mouse input are shared. Mouse functions also return touch information.
    54  *
    55  *   #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only)
    56  *       Reconfigure standard input to receive key inputs, works with SSH connection.
    57  *       WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other running processes or
    58  *       blocking the device if not restored properly. Use with care.
    59  *
    60  *   #define SUPPORT_BUSY_WAIT_LOOP
    61  *       Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
    62  *
    63  *   #define SUPPORT_PARTIALBUSY_WAIT_LOOP
    64  *       Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end
    65  *
    66  *   #define SUPPORT_EVENTS_WAITING
    67  *       Wait for events passively (sleeping while no events) instead of polling them actively every frame
    68  *
    69  *   #define SUPPORT_SCREEN_CAPTURE
    70  *       Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
    71  *
    72  *   #define SUPPORT_GIF_RECORDING
    73  *       Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
    74  *
    75  *   #define SUPPORT_COMPRESSION_API
    76  *       Support CompressData() and DecompressData() functions, those functions use zlib implementation
    77  *       provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
    78  *       for linkage
    79  *
    80  *   #define SUPPORT_EVENTS_AUTOMATION
    81  *       Support automatic generated events, loading and recording of those events when required
    82  *
    83  *   DEPENDENCIES:
    84  *       rglfw    - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly)
    85  *       raymath  - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
    86  *       camera   - Multiple 3D camera modes (free, orbital, 1st person, 3rd person)
    87  *       gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
    88  *
    89  *
    90  *   LICENSE: zlib/libpng
    91  *
    92  *   Copyright (c) 2013-2022 Ramon Santamaria (@raysan5)
    93  *
    94  *   This software is provided "as-is", without any express or implied warranty. In no event
    95  *   will the authors be held liable for any damages arising from the use of this software.
    96  *
    97  *   Permission is granted to anyone to use this software for any purpose, including commercial
    98  *   applications, and to alter it and redistribute it freely, subject to the following restrictions:
    99  *
   100  *     1. The origin of this software must not be misrepresented; you must not claim that you
   101  *     wrote the original software. If you use this software in a product, an acknowledgment
   102  *     in the product documentation would be appreciated but is not required.
   103  *
   104  *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
   105  *     as being the original software.
   106  *
   107  *     3. This notice may not be removed or altered from any source distribution.
   108  *
   109  **********************************************************************************************/
   110  
   111  #include "raylib.h"                 // Declares module functions
   112  
   113  // Check if config flags have been externally provided on compilation line
   114  #if !defined(EXTERNAL_CONFIG_FLAGS)
   115      #include "config.h"             // Defines module configuration flags
   116  #endif
   117  
   118  #include "utils.h"                  // Required for: TRACELOG() macros
   119  
   120  #define RLGL_IMPLEMENTATION
   121  #include "rlgl.h"                   // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
   122  
   123  #define RAYMATH_IMPLEMENTATION      // Define external out-of-line implementation
   124  #include "raymath.h"                // Vector3, Quaternion and Matrix functionality
   125  
   126  #if defined(SUPPORT_GESTURES_SYSTEM)
   127      #define GESTURES_IMPLEMENTATION
   128      #include "rgestures.h"           // Gestures detection functionality
   129  #endif
   130  
   131  #if defined(SUPPORT_CAMERA_SYSTEM)
   132      #define CAMERA_IMPLEMENTATION
   133      #include "rcamera.h"             // Camera system functionality
   134  #endif
   135  
   136  #if defined(SUPPORT_GIF_RECORDING)
   137      #define MSF_GIF_MALLOC(contextPointer, newSize) RL_MALLOC(newSize)
   138      #define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) RL_REALLOC(oldMemory, newSize)
   139      #define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) RL_FREE(oldMemory)
   140  
   141      #define MSF_GIF_IMPL
   142      #include "external/msf_gif.h"   // GIF recording functionality
   143  #endif
   144  
   145  #if defined(SUPPORT_COMPRESSION_API)
   146      #define SINFL_IMPLEMENTATION
   147      #define SINFL_NO_SIMD
   148      #include "external/sinfl.h"     // Deflate (RFC 1951) decompressor
   149  
   150      #define SDEFL_IMPLEMENTATION
   151      #include "external/sdefl.h"     // Deflate (RFC 1951) compressor
   152  #endif
   153  
   154  #if (defined(__linux__) || defined(PLATFORM_WEB)) && _POSIX_C_SOURCE < 199309L
   155      #undef _POSIX_C_SOURCE
   156      #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
   157  #endif
   158  
   159  // Platform specific defines to handle GetApplicationDirectory()
   160  #if defined (PLATFORM_DESKTOP)
   161      #if defined(_WIN32)
   162          #ifndef MAX_PATH
   163              #define MAX_PATH 1025
   164          #endif
   165      __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize);
   166      __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize);
   167      __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default);
   168      #elif defined(__linux__)
   169          #include <unistd.h>
   170      #elif defined(__APPLE__)
   171          #include <sys/syslimits.h>
   172          #include <mach-o/dyld.h>
   173      #endif // OSs
   174  #endif // PLATFORM_DESKTOP
   175  
   176  #include <stdlib.h>                 // Required for: srand(), rand(), atexit()
   177  #include <stdio.h>                  // Required for: sprintf() [Used in OpenURL()]
   178  #include <string.h>                 // Required for: strrchr(), strcmp(), strlen(), memset()
   179  #include <time.h>                   // Required for: time() [Used in InitTimer()]
   180  #include <math.h>                   // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
   181  
   182  #define _CRT_INTERNAL_NONSTDC_NAMES  1
   183  #include <sys/stat.h>               // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()]
   184  
   185  #if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
   186      #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
   187  #endif
   188  
   189  #if defined(PLATFORM_DESKTOP) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
   190      #define DIRENT_MALLOC RL_MALLOC
   191      #define DIRENT_FREE RL_FREE
   192  
   193      #include "external/dirent.h"    // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
   194  #else
   195      #include <dirent.h>             // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
   196  #endif
   197  
   198  #if defined(_WIN32)
   199      #include <direct.h>             // Required for: _getch(), _chdir()
   200      #define GETCWD _getcwd          // NOTE: MSDN recommends not to use getcwd(), chdir()
   201      #define CHDIR _chdir
   202      #include <io.h>                 // Required for: _access() [Used in FileExists()]
   203  #else
   204      #include <unistd.h>             // Required for: getch(), chdir() (POSIX), access()
   205      #define GETCWD getcwd
   206      #define CHDIR chdir
   207  #endif
   208  
   209  #if defined(PLATFORM_DESKTOP)
   210      #define GLFW_INCLUDE_NONE       // Disable the standard OpenGL header inclusion on GLFW3
   211                                      // NOTE: Already provided by rlgl implementation (on glad.h)
   212      #include "GLFW/glfw3.h"         // GLFW3 library: Windows, OpenGL context and Input management
   213                                      // NOTE: GLFW3 already includes gl.h (OpenGL) headers
   214  
   215      // Support retrieving native window handlers
   216      #if defined(_WIN32)
   217          typedef void *PVOID;
   218          typedef PVOID HANDLE;
   219          typedef HANDLE HWND;
   220          #define GLFW_EXPOSE_NATIVE_WIN32
   221          #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h
   222          #include "GLFW/glfw3native.h"
   223  
   224          #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
   225              // NOTE: Those functions require linking with winmm library
   226              unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
   227              unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
   228          #endif
   229      #endif
   230      #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
   231          #include <sys/time.h>               // Required for: timespec, nanosleep(), select() - POSIX
   232  
   233          //#define GLFW_EXPOSE_NATIVE_X11      // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
   234          //#define GLFW_EXPOSE_NATIVE_WAYLAND
   235          //#define GLFW_EXPOSE_NATIVE_MIR
   236          #include "GLFW/glfw3native.h"       // Required for: glfwGetX11Window()
   237      #endif
   238      #if defined(__APPLE__)
   239          #include <unistd.h>                 // Required for: usleep()
   240  
   241          //#define GLFW_EXPOSE_NATIVE_COCOA    // WARNING: Fails due to type redefinition
   242          #include "GLFW/glfw3native.h"       // Required for: glfwGetCocoaWindow()
   243      #endif
   244  
   245      // TODO: HACK: Added flag if not provided by GLFW when using external library
   246      // Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev
   247      #if !defined(GLFW_MOUSE_PASSTHROUGH)
   248          #define GLFW_MOUSE_PASSTHROUGH      0x0002000D
   249      #endif
   250  #endif
   251  
   252  #if defined(PLATFORM_ANDROID)
   253      //#include <android/sensor.h>           // Required for: Android sensors functions (accelerometer, gyroscope, light...)
   254      #include <android/window.h>             // Required for: AWINDOW_FLAG_FULLSCREEN definition and others
   255      #include <android_native_app_glue.h>    // Required for: android_app struct and activity management
   256      #include <jni.h>                        // Required for: JNIEnv and JavaVM [Used in OpenURL()]
   257  
   258      #include <EGL/egl.h>                    // Native platform windowing system interface
   259      //#include <GLES2/gl2.h>                // OpenGL ES 2.0 library (not required in this module, only in rlgl)
   260  #endif
   261  
   262  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   263      #include <fcntl.h>                  // POSIX file control definitions - open(), creat(), fcntl()
   264      #include <unistd.h>                 // POSIX standard function definitions - read(), close(), STDIN_FILENO
   265      #include <termios.h>                // POSIX terminal control definitions - tcgetattr(), tcsetattr()
   266      #include <pthread.h>                // POSIX threads management (inputs reading)
   267      #include <dirent.h>                 // POSIX directory browsing
   268  
   269      #include <sys/ioctl.h>              // Required for: ioctl() - UNIX System call for device-specific input/output operations
   270      #include <linux/kd.h>               // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
   271      #include <linux/input.h>            // Linux: Keycodes constants definition (KEY_A, ...)
   272      #include <linux/joystick.h>         // Linux: Joystick support library
   273  
   274  #if defined(PLATFORM_RPI)
   275      #include "bcm_host.h"               // Raspberry Pi VideoCore IV access functions
   276  #endif
   277  #if defined(PLATFORM_DRM)
   278      #include <gbm.h>                    // Generic Buffer Management (native platform for EGL on DRM)
   279      #include <xf86drm.h>                // Direct Rendering Manager user-level library interface
   280      #include <xf86drmMode.h>            // Direct Rendering Manager mode setting (KMS) interface
   281  #endif
   282  
   283      #include "EGL/egl.h"                // Native platform windowing system interface
   284      #include "EGL/eglext.h"             // EGL extensions
   285      //#include "GLES2/gl2.h"            // OpenGL ES 2.0 library (not required in this module, only in rlgl)
   286  #endif
   287  
   288  #if defined(PLATFORM_WEB)
   289      #define GLFW_INCLUDE_ES2            // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL)
   290      #include "GLFW/glfw3.h"             // GLFW3: Windows, OpenGL context and Input management
   291      #include <sys/time.h>               // Required for: timespec, nanosleep(), select() - POSIX
   292  
   293      #include <emscripten/emscripten.h>  // Emscripten functionality for C
   294      #include <emscripten/html5.h>       // Emscripten HTML5 library
   295  #endif
   296  
   297  //----------------------------------------------------------------------------------
   298  // Defines and Macros
   299  //----------------------------------------------------------------------------------
   300  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   301      #define USE_LAST_TOUCH_DEVICE       // When multiple touchscreens are connected, only use the one with the highest event<N> number
   302  
   303      #define DEFAULT_GAMEPAD_DEV    "/dev/input/js"  // Gamepad input (base dev for all gamepads: js0, js1, ...)
   304      #define DEFAULT_EVDEV_PATH       "/dev/input/"  // Path to the linux input events
   305  #endif
   306  
   307  #ifndef MAX_FILEPATH_CAPACITY
   308      #define MAX_FILEPATH_CAPACITY       8192        // Maximum capacity for filepath
   309  #endif
   310  #ifndef MAX_FILEPATH_LENGTH
   311      #define MAX_FILEPATH_LENGTH         4096        // Maximum length for filepaths (Linux PATH_MAX default value)
   312  #endif
   313  
   314  #ifndef MAX_KEYBOARD_KEYS
   315      #define MAX_KEYBOARD_KEYS            512        // Maximum number of keyboard keys supported
   316  #endif
   317  #ifndef MAX_MOUSE_BUTTONS
   318      #define MAX_MOUSE_BUTTONS              8        // Maximum number of mouse buttons supported
   319  #endif
   320  #ifndef MAX_GAMEPADS
   321      #define MAX_GAMEPADS                   4        // Maximum number of gamepads supported
   322  #endif
   323  #ifndef MAX_GAMEPAD_AXIS
   324      #define MAX_GAMEPAD_AXIS               8        // Maximum number of axis supported (per gamepad)
   325  #endif
   326  #ifndef MAX_GAMEPAD_BUTTONS
   327      #define MAX_GAMEPAD_BUTTONS           32        // Maximum number of buttons supported (per gamepad)
   328  #endif
   329  #ifndef MAX_TOUCH_POINTS
   330      #define MAX_TOUCH_POINTS               8        // Maximum number of touch points supported
   331  #endif
   332  #ifndef MAX_KEY_PRESSED_QUEUE
   333      #define MAX_KEY_PRESSED_QUEUE         16        // Maximum number of keys in the key input queue
   334  #endif
   335  #ifndef MAX_CHAR_PRESSED_QUEUE
   336      #define MAX_CHAR_PRESSED_QUEUE        16        // Maximum number of characters in the char input queue
   337  #endif
   338  
   339  #ifndef MAX_DECOMPRESSION_SIZE
   340      #define MAX_DECOMPRESSION_SIZE        64        // Maximum size allocated for decompression in MB
   341  #endif
   342  
   343  // Flags operation macros
   344  #define FLAG_SET(n, f) ((n) |= (f))
   345  #define FLAG_CLEAR(n, f) ((n) &= ~(f))
   346  #define FLAG_TOGGLE(n, f) ((n) ^= (f))
   347  #define FLAG_CHECK(n, f) ((n) & (f))
   348  
   349  //----------------------------------------------------------------------------------
   350  // Types and Structures Definition
   351  //----------------------------------------------------------------------------------
   352  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   353  typedef struct {
   354      pthread_t threadId;             // Event reading thread id
   355      int fd;                         // File descriptor to the device it is assigned to
   356      int eventNum;                   // Number of 'event<N>' device
   357      Rectangle absRange;             // Range of values for absolute pointing devices (touchscreens)
   358      int touchSlot;                  // Hold the touch slot number of the currently being sent multitouch block
   359      bool isMouse;                   // True if device supports relative X Y movements
   360      bool isTouch;                   // True if device supports absolute X Y movements and has BTN_TOUCH
   361      bool isMultitouch;              // True if device supports multiple absolute movevents and has BTN_TOUCH
   362      bool isKeyboard;                // True if device has letter keycodes
   363      bool isGamepad;                 // True if device has gamepad buttons
   364  } InputEventWorker;
   365  #endif
   366  
   367  typedef struct { int x; int y; } Point;
   368  typedef struct { unsigned int width; unsigned int height; } Size;
   369  
   370  // Core global state context data
   371  typedef struct CoreData {
   372      struct {
   373  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
   374          GLFWwindow *handle;                 // GLFW window handle (graphic device)
   375  #endif
   376  #if defined(PLATFORM_RPI)
   377          EGL_DISPMANX_WINDOW_T handle;       // Native window handle (graphic device)
   378  #endif
   379  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   380  #if defined(PLATFORM_DRM)
   381          int fd;                             // File descriptor for /dev/dri/...
   382          drmModeConnector *connector;        // Direct Rendering Manager (DRM) mode connector
   383          drmModeCrtc *crtc;                  // CRT Controller
   384          int modeIndex;                      // Index of the used mode of connector->modes
   385          struct gbm_device *gbmDevice;       // GBM device
   386          struct gbm_surface *gbmSurface;     // GBM surface
   387          struct gbm_bo *prevBO;              // Previous GBM buffer object (during frame swapping)
   388          uint32_t prevFB;                    // Previous GBM framebufer (during frame swapping)
   389  #endif  // PLATFORM_DRM
   390          EGLDisplay device;                  // Native display device (physical screen connection)
   391          EGLSurface surface;                 // Surface to draw on, framebuffers (connected to context)
   392          EGLContext context;                 // Graphic context, mode in which drawing can be done
   393          EGLConfig config;                   // Graphic config
   394  #endif
   395          const char *title;                  // Window text title const pointer
   396          unsigned int flags;                 // Configuration flags (bit based), keeps window state
   397          bool ready;                         // Check if window has been initialized successfully
   398          bool fullscreen;                    // Check if fullscreen mode is enabled
   399          bool shouldClose;                   // Check if window set for closing
   400          bool resizedLastFrame;              // Check if window has been resized last frame
   401          bool eventWaiting;                  // Wait for events before ending frame
   402  
   403          Point position;                     // Window position on screen (required on fullscreen toggle)
   404          Size display;                       // Display width and height (monitor, device-screen, LCD, ...)
   405          Size screen;                        // Screen width and height (used render area)
   406          Size currentFbo;                    // Current render width and height (depends on active fbo)
   407          Size render;                        // Framebuffer width and height (render area, including black bars if required)
   408          Point renderOffset;                 // Offset from render area (must be divided by 2)
   409          Matrix screenScale;                 // Matrix to scale screen (framebuffer rendering)
   410  
   411          char **dropFilepaths;         // Store dropped files paths pointers (provided by GLFW)
   412          unsigned int dropFileCount;         // Count dropped files strings
   413  
   414      } Window;
   415  #if defined(PLATFORM_ANDROID)
   416      struct {
   417          bool appEnabled;                    // Flag to detect if app is active ** = true
   418          struct android_app *app;            // Android activity
   419          struct android_poll_source *source; // Android events polling source
   420          bool contextRebindRequired;         // Used to know context rebind required
   421      } Android;
   422  #endif
   423      struct {
   424          const char *basePath;               // Base path for data storage
   425      } Storage;
   426      struct {
   427  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   428          InputEventWorker eventWorker[10];   // List of worker threads for every monitored "/dev/input/event<N>"
   429  #endif
   430          struct {
   431              int exitKey;                    // Default exit key
   432              char currentKeyState[MAX_KEYBOARD_KEYS];        // Registers current frame key state
   433              char previousKeyState[MAX_KEYBOARD_KEYS];       // Registers previous frame key state
   434  
   435              int keyPressedQueue[MAX_KEY_PRESSED_QUEUE];     // Input keys queue
   436              int keyPressedQueueCount;       // Input keys queue count
   437  
   438              int charPressedQueue[MAX_CHAR_PRESSED_QUEUE];   // Input characters queue (unicode)
   439              int charPressedQueueCount;      // Input characters queue count
   440  
   441  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   442              int defaultMode;                // Default keyboard mode
   443  #if defined(SUPPORT_SSH_KEYBOARD_RPI)
   444              bool evtMode;                   // Keyboard in event mode
   445  #endif
   446              int defaultFileFlags;           // Default IO file flags
   447              struct termios defaultSettings; // Default keyboard settings
   448              int fd;                         // File descriptor for the evdev keyboard
   449  #endif
   450          } Keyboard;
   451          struct {
   452              Vector2 offset;                 // Mouse offset
   453              Vector2 scale;                  // Mouse scaling
   454              Vector2 currentPosition;        // Mouse position on screen
   455              Vector2 previousPosition;       // Previous mouse position
   456  
   457              int cursor;                     // Tracks current mouse cursor
   458              bool cursorHidden;              // Track if cursor is hidden
   459              bool cursorOnScreen;            // Tracks if cursor is inside client area
   460  
   461              char currentButtonState[MAX_MOUSE_BUTTONS];     // Registers current mouse button state
   462              char previousButtonState[MAX_MOUSE_BUTTONS];    // Registers previous mouse button state
   463              Vector2 currentWheelMove;       // Registers current mouse wheel variation
   464              Vector2 previousWheelMove;      // Registers previous mouse wheel variation
   465  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   466              // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update
   467              char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab
   468  #endif
   469          } Mouse;
   470          struct {
   471              int pointCount;                             // Number of touch points active
   472              int pointId[MAX_TOUCH_POINTS];              // Point identifiers
   473              Vector2 position[MAX_TOUCH_POINTS];         // Touch position on screen
   474              char currentTouchState[MAX_TOUCH_POINTS];   // Registers current touch state
   475              char previousTouchState[MAX_TOUCH_POINTS];  // Registers previous touch state
   476          } Touch;
   477          struct {
   478              int lastButtonPressed;          // Register last gamepad button pressed
   479              int axisCount;                  // Register number of available gamepad axis
   480              bool ready[MAX_GAMEPADS];       // Flag to know if gamepad is ready
   481              char name[MAX_GAMEPADS][64];    // Gamepad name holder
   482              char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];     // Current gamepad buttons state
   483              char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];    // Previous gamepad buttons state
   484              float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS];                // Gamepad axis state
   485  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   486              pthread_t threadId;             // Gamepad reading thread id
   487              int streamId[MAX_GAMEPADS];     // Gamepad device file descriptor
   488  #endif
   489          } Gamepad;
   490      } Input;
   491      struct {
   492          double current;                     // Current time measure
   493          double previous;                    // Previous time measure
   494          double update;                      // Time measure for frame update
   495          double draw;                        // Time measure for frame draw
   496          double frame;                       // Time measure for one frame
   497          double target;                      // Desired time for one frame, if 0 not applied
   498  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   499          unsigned long long base;            // Base time measure for hi-res timer
   500  #endif
   501          unsigned int frameCounter;          // Frame counter
   502      } Time;
   503  } CoreData;
   504  
   505  //----------------------------------------------------------------------------------
   506  // Global Variables Definition
   507  //----------------------------------------------------------------------------------
   508  RLAPI const char *raylib_version = RAYLIB_VERSION;  // raylib version exported symbol, required for some bindings
   509  
   510  static CoreData CORE = { 0 };               // Global CORE state context
   511  
   512  #if defined(SUPPORT_SCREEN_CAPTURE)
   513  static int screenshotCounter = 0;           // Screenshots counter
   514  #endif
   515  
   516  #if defined(SUPPORT_GIF_RECORDING)
   517  static int gifFrameCounter = 0;             // GIF frames counter
   518  static bool gifRecording = false;           // GIF recording state
   519  static MsfGifState gifState = { 0 };        // MSGIF context state
   520  #endif
   521  
   522  #if defined(SUPPORT_EVENTS_AUTOMATION)
   523  #define MAX_CODE_AUTOMATION_EVENTS      16384
   524  
   525  typedef enum AutomationEventType {
   526      EVENT_NONE = 0,
   527      // Input events
   528      INPUT_KEY_UP,                   // param[0]: key
   529      INPUT_KEY_DOWN,                 // param[0]: key
   530      INPUT_KEY_PRESSED,              // param[0]: key
   531      INPUT_KEY_RELEASED,             // param[0]: key
   532      INPUT_MOUSE_BUTTON_UP,          // param[0]: button
   533      INPUT_MOUSE_BUTTON_DOWN,        // param[0]: button
   534      INPUT_MOUSE_POSITION,           // param[0]: x, param[1]: y
   535      INPUT_MOUSE_WHEEL_MOTION,       // param[0]: x delta, param[1]: y delta
   536      INPUT_GAMEPAD_CONNECT,          // param[0]: gamepad
   537      INPUT_GAMEPAD_DISCONNECT,       // param[0]: gamepad
   538      INPUT_GAMEPAD_BUTTON_UP,        // param[0]: button
   539      INPUT_GAMEPAD_BUTTON_DOWN,      // param[0]: button
   540      INPUT_GAMEPAD_AXIS_MOTION,      // param[0]: axis, param[1]: delta
   541      INPUT_TOUCH_UP,                 // param[0]: id
   542      INPUT_TOUCH_DOWN,               // param[0]: id
   543      INPUT_TOUCH_POSITION,           // param[0]: x, param[1]: y
   544      INPUT_GESTURE,                  // param[0]: gesture
   545      // Window events
   546      WINDOW_CLOSE,                   // no params
   547      WINDOW_MAXIMIZE,                // no params
   548      WINDOW_MINIMIZE,                // no params
   549      WINDOW_RESIZE,                  // param[0]: width, param[1]: height
   550      // Custom events
   551      ACTION_TAKE_SCREENSHOT,
   552      ACTION_SETTARGETFPS
   553  } AutomationEventType;
   554  
   555  // Event type
   556  // Used to enable events flags
   557  typedef enum {
   558      EVENT_INPUT_KEYBOARD    = 0,
   559      EVENT_INPUT_MOUSE       = 1,
   560      EVENT_INPUT_GAMEPAD     = 2,
   561      EVENT_INPUT_TOUCH       = 4,
   562      EVENT_INPUT_GESTURE     = 8,
   563      EVENT_WINDOW            = 16,
   564      EVENT_CUSTOM            = 32
   565  } EventType;
   566  
   567  static const char *autoEventTypeName[] = {
   568      "EVENT_NONE",
   569      "INPUT_KEY_UP",
   570      "INPUT_KEY_DOWN",
   571      "INPUT_KEY_PRESSED",
   572      "INPUT_KEY_RELEASED",
   573      "INPUT_MOUSE_BUTTON_UP",
   574      "INPUT_MOUSE_BUTTON_DOWN",
   575      "INPUT_MOUSE_POSITION",
   576      "INPUT_MOUSE_WHEEL_MOTION",
   577      "INPUT_GAMEPAD_CONNECT",
   578      "INPUT_GAMEPAD_DISCONNECT",
   579      "INPUT_GAMEPAD_BUTTON_UP",
   580      "INPUT_GAMEPAD_BUTTON_DOWN",
   581      "INPUT_GAMEPAD_AXIS_MOTION",
   582      "INPUT_TOUCH_UP",
   583      "INPUT_TOUCH_DOWN",
   584      "INPUT_TOUCH_POSITION",
   585      "INPUT_GESTURE",
   586      "WINDOW_CLOSE",
   587      "WINDOW_MAXIMIZE",
   588      "WINDOW_MINIMIZE",
   589      "WINDOW_RESIZE",
   590      "ACTION_TAKE_SCREENSHOT",
   591      "ACTION_SETTARGETFPS"
   592  };
   593  
   594  // Automation Event (20 bytes)
   595  typedef struct AutomationEvent {
   596      unsigned int frame;                 // Event frame
   597      unsigned int type;                  // Event type (AutoEventType)
   598      int params[3];                      // Event parameters (if required)
   599  } AutomationEvent;
   600  
   601  static AutomationEvent *events = NULL;        // Events array
   602  static unsigned int eventCount = 0;     // Events count
   603  static bool eventsPlaying = false;      // Play events
   604  static bool eventsRecording = false;    // Record events
   605  
   606  //static short eventsEnabled = 0b0000001111111111;    // Events enabled for checking
   607  #endif
   608  //-----------------------------------------------------------------------------------
   609  
   610  //----------------------------------------------------------------------------------
   611  // Other Modules Functions Declaration (required by core)
   612  //----------------------------------------------------------------------------------
   613  #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
   614  extern void LoadFontDefault(void);          // [Module: text] Loads default font on InitWindow()
   615  extern void UnloadFontDefault(void);        // [Module: text] Unloads default font from GPU memory
   616  #endif
   617  
   618  //----------------------------------------------------------------------------------
   619  // Module specific Functions Declaration
   620  //----------------------------------------------------------------------------------
   621  static void InitTimer(void);                            // Initialize timer (hi-resolution if available)
   622  static bool InitGraphicsDevice(int width, int height);  // Initialize graphics device
   623  static void SetupFramebuffer(int width, int height);    // Setup main framebuffer
   624  static void SetupViewport(int width, int height);       // Set viewport for a provided width and height
   625  
   626  static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter);   // Scan all files and directories in a base path
   627  static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter);  // Scan all files and directories recursively from a base path
   628  
   629  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
   630  static void ErrorCallback(int error, const char *description);                             // GLFW3 Error Callback, runs on GLFW3 error
   631  // Window callbacks events
   632  static void WindowSizeCallback(GLFWwindow *window, int width, int height);                 // GLFW3 WindowSize Callback, runs when window is resized
   633  #if !defined(PLATFORM_WEB)
   634  static void WindowMaximizeCallback(GLFWwindow* window, int maximized);                     // GLFW3 Window Maximize Callback, runs when window is maximized
   635  #endif
   636  static void WindowIconifyCallback(GLFWwindow *window, int iconified);                      // GLFW3 WindowIconify Callback, runs when window is minimized/restored
   637  static void WindowFocusCallback(GLFWwindow *window, int focused);                          // GLFW3 WindowFocus Callback, runs when window get/lose focus
   638  static void WindowDropCallback(GLFWwindow *window, int count, const char **paths);         // GLFW3 Window Drop Callback, runs when drop files into window
   639  // Input callbacks events
   640  static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);  // GLFW3 Keyboard Callback, runs on key pressed
   641  static void CharCallback(GLFWwindow *window, unsigned int key);                            // GLFW3 Char Key Callback, runs on key pressed (get char value)
   642  static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);     // GLFW3 Mouse Button Callback, runs on mouse button pressed
   643  static void MouseCursorPosCallback(GLFWwindow *window, double x, double y);                // GLFW3 Cursor Position Callback, runs on mouse move
   644  static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset);       // GLFW3 Srolling Callback, runs on mouse wheel
   645  static void CursorEnterCallback(GLFWwindow *window, int enter);                            // GLFW3 Cursor Enter Callback, cursor enters client area
   646  #endif
   647  
   648  #if defined(PLATFORM_ANDROID)
   649  static void AndroidCommandCallback(struct android_app *app, int32_t cmd);                  // Process Android activity lifecycle commands
   650  static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event);          // Process Android inputs
   651  #endif
   652  
   653  #if defined(PLATFORM_WEB)
   654  static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
   655  static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
   656  static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
   657  
   658  static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
   659  static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
   660  static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
   661  #endif
   662  
   663  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   664  static void InitKeyboard(void);                         // Initialize raw keyboard system
   665  static void RestoreKeyboard(void);                      // Restore keyboard system
   666  #if defined(SUPPORT_SSH_KEYBOARD_RPI)
   667  static void ProcessKeyboard(void);                      // Process keyboard events
   668  #endif
   669  
   670  static void InitEvdevInput(void);                       // Initialize evdev inputs
   671  static void ConfigureEvdevDevice(char *device);         // Identifies a input device and configures it for use if appropriate
   672  static void PollKeyboardEvents(void);                   // Process evdev keyboard events.
   673  static void *EventThread(void *arg);                    // Input device events reading thread
   674  
   675  static void InitGamepad(void);                          // Initialize raw gamepad input
   676  static void *GamepadThread(void *arg);                  // Mouse reading thread
   677  
   678  #if defined(PLATFORM_DRM)
   679  static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode);                               // Search matching DRM mode in connector's mode list
   680  static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced);      // Search exactly matching DRM connector mode in connector's list
   681  static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced);    // Search the nearest matching DRM connector mode in connector's list
   682  #endif
   683  
   684  #endif  // PLATFORM_RPI || PLATFORM_DRM
   685  
   686  #if defined(SUPPORT_EVENTS_AUTOMATION)
   687  static void LoadAutomationEvents(const char *fileName);     // Load automation events from file
   688  static void ExportAutomationEvents(const char *fileName);   // Export recorded automation events into a file
   689  static void RecordAutomationEvent(unsigned int frame);      // Record frame events (to internal events array)
   690  static void PlayAutomationEvent(unsigned int frame);        // Play frame events (from internal events array)
   691  #endif
   692  
   693  #if defined(_WIN32)
   694  // NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
   695  void __stdcall Sleep(unsigned long msTimeout);              // Required for: WaitTime()
   696  #endif
   697  
   698  #if !defined(SUPPORT_MODULE_RTEXT)
   699  const char *TextFormat(const char *text, ...);       // Formatting of text with variables to 'embed'
   700  #endif // !SUPPORT_MODULE_RTEXT
   701  
   702  //----------------------------------------------------------------------------------
   703  // Module Functions Definition - Window and OpenGL Context Functions
   704  //----------------------------------------------------------------------------------
   705  #if defined(PLATFORM_ANDROID)
   706  // To allow easier porting to android, we allow the user to define a
   707  // main function which we call from android_main, defined by ourselves
   708  extern int main(int argc, char *argv[]);
   709  
   710  void android_main(struct android_app *app)
   711  {
   712      char arg0[] = "raylib";     // NOTE: argv[] are mutable
   713      CORE.Android.app = app;
   714  
   715      // NOTE: Return codes != 0 are skipped
   716      (void)main(1, (char *[]) { arg0, NULL });
   717  }
   718  
   719  // NOTE: Add this to header (if apps really need it)
   720  struct android_app *GetAndroidApp(void)
   721  {
   722      return CORE.Android.app;
   723  }
   724  #endif
   725  
   726  // Initialize window and OpenGL context
   727  // NOTE: data parameter could be used to pass any kind of required data to the initialization
   728  void InitWindow(int width, int height, const char *title)
   729  {
   730      TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
   731  
   732      TRACELOG(LOG_INFO, "Supported raylib modules:");
   733      TRACELOG(LOG_INFO, "    > rcore:..... loaded (mandatory)");
   734      TRACELOG(LOG_INFO, "    > rlgl:...... loaded (mandatory)");
   735  #if defined(SUPPORT_MODULE_RSHAPES)
   736      TRACELOG(LOG_INFO, "    > rshapes:... loaded (optional)");
   737  #else
   738      TRACELOG(LOG_INFO, "    > rshapes:... not loaded (optional)");
   739  #endif
   740  #if defined(SUPPORT_MODULE_RTEXTURES)
   741      TRACELOG(LOG_INFO, "    > rtextures:. loaded (optional)");
   742  #else
   743      TRACELOG(LOG_INFO, "    > rtextures:. not loaded (optional)");
   744  #endif
   745  #if defined(SUPPORT_MODULE_RTEXT)
   746      TRACELOG(LOG_INFO, "    > rtext:..... loaded (optional)");
   747  #else
   748      TRACELOG(LOG_INFO, "    > rtext:..... not loaded (optional)");
   749  #endif
   750  #if defined(SUPPORT_MODULE_RMODELS)
   751      TRACELOG(LOG_INFO, "    > rmodels:... loaded (optional)");
   752  #else
   753      TRACELOG(LOG_INFO, "    > rmodels:... not loaded (optional)");
   754  #endif
   755  #if defined(SUPPORT_MODULE_RAUDIO)
   756      TRACELOG(LOG_INFO, "    > raudio:.... loaded (optional)");
   757  #else
   758      TRACELOG(LOG_INFO, "    > raudio:.... not loaded (optional)");
   759  #endif
   760  
   761      if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
   762  
   763      // Initialize global input state
   764      memset(&CORE.Input, 0, sizeof(CORE.Input));
   765      CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
   766      CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
   767      CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
   768      CORE.Input.Gamepad.lastButtonPressed = -1;
   769  #if defined(SUPPORT_EVENTS_WAITING)
   770      CORE.Window.eventWaiting = true;
   771  #endif
   772  
   773  #if defined(PLATFORM_ANDROID)
   774      CORE.Window.screen.width = width;
   775      CORE.Window.screen.height = height;
   776      CORE.Window.currentFbo.width = width;
   777      CORE.Window.currentFbo.height = height;
   778  
   779      // Set desired windows flags before initializing anything
   780      ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0);  //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
   781  
   782      int orientation = AConfiguration_getOrientation(CORE.Android.app->config);
   783  
   784      if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait");
   785      else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape");
   786  
   787      // TODO: Automatic orientation doesn't seem to work
   788      if (width <= height)
   789      {
   790          AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT);
   791          TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait");
   792      }
   793      else
   794      {
   795          AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND);
   796          TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape");
   797      }
   798  
   799      //AConfiguration_getDensity(CORE.Android.app->config);
   800      //AConfiguration_getKeyboard(CORE.Android.app->config);
   801      //AConfiguration_getScreenSize(CORE.Android.app->config);
   802      //AConfiguration_getScreenLong(CORE.Android.app->config);
   803  
   804      // Initialize App command system
   805      // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()...
   806      CORE.Android.app->onAppCmd = AndroidCommandCallback;
   807  
   808      // Initialize input events system
   809      CORE.Android.app->onInputEvent = AndroidInputCallback;
   810  
   811      // Initialize assets manager
   812      InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath);
   813  
   814      // Initialize base path for storage
   815      CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath;
   816  
   817      TRACELOG(LOG_INFO, "ANDROID: App initialized successfully");
   818  
   819      // Android ALooper_pollAll() variables
   820      int pollResult = 0;
   821      int pollEvents = 0;
   822  
   823      // Wait for window to be initialized (display and context)
   824      while (!CORE.Window.ready)
   825      {
   826          // Process events loop
   827          while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0)
   828          {
   829              // Process this event
   830              if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source);
   831  
   832              // NOTE: Never close window, native activity is controlled by the system!
   833              //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true;
   834          }
   835      }
   836  #endif
   837  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   838      // Initialize graphics device (display device and OpenGL context)
   839      // NOTE: returns true if window and graphic device has been initialized successfully
   840      CORE.Window.ready = InitGraphicsDevice(width, height);
   841  
   842      // If graphic device is no properly initialized, we end program
   843      if (!CORE.Window.ready)
   844      {
   845          TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device");
   846          return;
   847      }
   848  
   849      // Initialize hi-res timer
   850      InitTimer();
   851  
   852      // Initialize random seed
   853      srand((unsigned int)time(NULL));
   854  
   855      // Initialize base path for storage
   856      CORE.Storage.basePath = GetWorkingDirectory();
   857  
   858  #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
   859      // Load default font
   860      // WARNING: External function: Module required: rtext
   861      LoadFontDefault();
   862      #if defined(SUPPORT_MODULE_RSHAPES)
   863      Rectangle rec = GetFontDefault().recs[95];
   864      // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
   865      SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes
   866      #endif
   867  #else
   868      #if defined(SUPPORT_MODULE_RSHAPES)
   869      // Set default texture and rectangle to be used for shapes drawing
   870      // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
   871      Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
   872      SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f });    // WARNING: Module required: rshapes
   873      #endif
   874  #endif
   875  #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
   876      if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
   877      {
   878          // Set default font texture filter for HighDPI (blurry)
   879          // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
   880          rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR);
   881          rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
   882      }
   883  #endif
   884  
   885  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
   886      // Initialize raw input system
   887      InitEvdevInput();   // Evdev inputs initialization
   888      InitGamepad();      // Gamepad init
   889      InitKeyboard();     // Keyboard init (stdin)
   890  #endif
   891  
   892  #if defined(PLATFORM_WEB)
   893      // Setup callback funtions for the DOM events
   894      emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback);
   895  
   896      // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
   897      // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
   898      //emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
   899      // Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
   900      //emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
   901      // Trigger this once to get initial window sizing
   902      //EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
   903  
   904      // Support keyboard events -> Not used, GLFW.JS takes care of that
   905      //emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
   906      //emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
   907  
   908      // Support mouse events
   909      emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
   910  
   911      // Support touch events
   912      emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
   913      emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
   914      emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
   915      emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
   916  
   917      // Support gamepad events (not provided by GLFW3 on emscripten)
   918      emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
   919      emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
   920  #endif
   921  
   922      CORE.Input.Mouse.currentPosition.x = (float)CORE.Window.screen.width/2.0f;
   923      CORE.Input.Mouse.currentPosition.y = (float)CORE.Window.screen.height/2.0f;
   924  
   925  #if defined(SUPPORT_EVENTS_AUTOMATION)
   926      events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent));
   927      CORE.Time.frameCounter = 0;
   928  #endif
   929  
   930  #endif        // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_RPI || PLATFORM_DRM
   931  }
   932  
   933  // Close window and unload OpenGL context
   934  void CloseWindow(void)
   935  {
   936  #if defined(SUPPORT_GIF_RECORDING)
   937      if (gifRecording)
   938      {
   939          MsfGifResult result = msf_gif_end(&gifState);
   940          msf_gif_free(result);
   941          gifRecording = false;
   942      }
   943  #endif
   944  
   945  #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
   946      UnloadFontDefault();        // WARNING: Module required: rtext
   947  #endif
   948  
   949      rlglClose();                // De-init rlgl
   950  
   951  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
   952      glfwDestroyWindow(CORE.Window.handle);
   953      glfwTerminate();
   954  #endif
   955  
   956  #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
   957      timeEndPeriod(1);           // Restore time period
   958  #endif
   959  
   960  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
   961      // Close surface, context and display
   962      if (CORE.Window.device != EGL_NO_DISPLAY)
   963      {
   964          eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
   965  
   966          if (CORE.Window.surface != EGL_NO_SURFACE)
   967          {
   968              eglDestroySurface(CORE.Window.device, CORE.Window.surface);
   969              CORE.Window.surface = EGL_NO_SURFACE;
   970          }
   971  
   972          if (CORE.Window.context != EGL_NO_CONTEXT)
   973          {
   974              eglDestroyContext(CORE.Window.device, CORE.Window.context);
   975              CORE.Window.context = EGL_NO_CONTEXT;
   976          }
   977  
   978          eglTerminate(CORE.Window.device);
   979          CORE.Window.device = EGL_NO_DISPLAY;
   980      }
   981  #endif
   982  
   983  #if defined(PLATFORM_DRM)
   984      if (CORE.Window.prevFB)
   985      {
   986          drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
   987          CORE.Window.prevFB = 0;
   988      }
   989  
   990      if (CORE.Window.prevBO)
   991      {
   992          gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
   993          CORE.Window.prevBO = NULL;
   994      }
   995  
   996      if (CORE.Window.gbmSurface)
   997      {
   998          gbm_surface_destroy(CORE.Window.gbmSurface);
   999          CORE.Window.gbmSurface = NULL;
  1000      }
  1001  
  1002      if (CORE.Window.gbmDevice)
  1003      {
  1004          gbm_device_destroy(CORE.Window.gbmDevice);
  1005          CORE.Window.gbmDevice = NULL;
  1006      }
  1007  
  1008      if (CORE.Window.crtc)
  1009      {
  1010          if (CORE.Window.connector)
  1011          {
  1012              drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id,
  1013                  CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode);
  1014              drmModeFreeConnector(CORE.Window.connector);
  1015              CORE.Window.connector = NULL;
  1016          }
  1017  
  1018          drmModeFreeCrtc(CORE.Window.crtc);
  1019          CORE.Window.crtc = NULL;
  1020      }
  1021  
  1022      if (CORE.Window.fd != -1)
  1023      {
  1024          close(CORE.Window.fd);
  1025          CORE.Window.fd = -1;
  1026      }
  1027  
  1028      // Close surface, context and display
  1029      if (CORE.Window.device != EGL_NO_DISPLAY)
  1030      {
  1031          if (CORE.Window.surface != EGL_NO_SURFACE)
  1032          {
  1033              eglDestroySurface(CORE.Window.device, CORE.Window.surface);
  1034              CORE.Window.surface = EGL_NO_SURFACE;
  1035          }
  1036  
  1037          if (CORE.Window.context != EGL_NO_CONTEXT)
  1038          {
  1039              eglDestroyContext(CORE.Window.device, CORE.Window.context);
  1040              CORE.Window.context = EGL_NO_CONTEXT;
  1041          }
  1042  
  1043          eglTerminate(CORE.Window.device);
  1044          CORE.Window.device = EGL_NO_DISPLAY;
  1045      }
  1046  #endif
  1047  
  1048  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  1049      // Wait for mouse and gamepad threads to finish before closing
  1050      // NOTE: Those threads should already have finished at this point
  1051      // because they are controlled by CORE.Window.shouldClose variable
  1052  
  1053      CORE.Window.shouldClose = true;   // Added to force threads to exit when the close window is called
  1054  
  1055      // Close the evdev keyboard
  1056      if (CORE.Input.Keyboard.fd != -1)
  1057      {
  1058          close(CORE.Input.Keyboard.fd);
  1059          CORE.Input.Keyboard.fd = -1;
  1060      }
  1061  
  1062      for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
  1063      {
  1064          if (CORE.Input.eventWorker[i].threadId)
  1065          {
  1066              pthread_join(CORE.Input.eventWorker[i].threadId, NULL);
  1067          }
  1068      }
  1069  
  1070      if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL);
  1071  #endif
  1072  
  1073  #if defined(SUPPORT_EVENTS_AUTOMATION)
  1074      free(events);
  1075  #endif
  1076  
  1077      CORE.Window.ready = false;
  1078      TRACELOG(LOG_INFO, "Window closed successfully");
  1079  }
  1080  
  1081  // Check if KEY_ESCAPE pressed or Close icon pressed
  1082  bool WindowShouldClose(void)
  1083  {
  1084  #if defined(PLATFORM_WEB)
  1085      // Emterpreter-Async required to run sync code
  1086      // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
  1087      // By default, this function is never called on a web-ready raylib example because we encapsulate
  1088      // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously
  1089      // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter!
  1090      emscripten_sleep(16);
  1091      return false;
  1092  #endif
  1093  
  1094  #if defined(PLATFORM_DESKTOP)
  1095      if (CORE.Window.ready)
  1096      {
  1097          // While window minimized, stop loop execution
  1098          while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents();
  1099  
  1100          CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle);
  1101  
  1102          // Reset close status for next frame
  1103          glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE);
  1104  
  1105          return CORE.Window.shouldClose;
  1106      }
  1107      else return true;
  1108  #endif
  1109  
  1110  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  1111      if (CORE.Window.ready) return CORE.Window.shouldClose;
  1112      else return true;
  1113  #endif
  1114  }
  1115  
  1116  // Check if window has been initialized successfully
  1117  bool IsWindowReady(void)
  1118  {
  1119      return CORE.Window.ready;
  1120  }
  1121  
  1122  // Check if window is currently fullscreen
  1123  bool IsWindowFullscreen(void)
  1124  {
  1125      return CORE.Window.fullscreen;
  1126  }
  1127  
  1128  // Check if window is currently hidden
  1129  bool IsWindowHidden(void)
  1130  {
  1131  #if defined(PLATFORM_DESKTOP)
  1132      return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0);
  1133  #endif
  1134      return false;
  1135  }
  1136  
  1137  // Check if window has been minimized
  1138  bool IsWindowMinimized(void)
  1139  {
  1140  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  1141      return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0);
  1142  #else
  1143      return false;
  1144  #endif
  1145  }
  1146  
  1147  // Check if window has been maximized (only PLATFORM_DESKTOP)
  1148  bool IsWindowMaximized(void)
  1149  {
  1150  #if defined(PLATFORM_DESKTOP)
  1151      return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0);
  1152  #else
  1153      return false;
  1154  #endif
  1155  }
  1156  
  1157  // Check if window has the focus
  1158  bool IsWindowFocused(void)
  1159  {
  1160  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  1161      return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0);
  1162  #else
  1163      return true;
  1164  #endif
  1165  }
  1166  
  1167  // Check if window has been resizedLastFrame
  1168  bool IsWindowResized(void)
  1169  {
  1170  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  1171      return CORE.Window.resizedLastFrame;
  1172  #else
  1173      return false;
  1174  #endif
  1175  }
  1176  
  1177  // Check if one specific window flag is enabled
  1178  bool IsWindowState(unsigned int flag)
  1179  {
  1180      return ((CORE.Window.flags & flag) > 0);
  1181  }
  1182  
  1183  // Toggle fullscreen mode (only PLATFORM_DESKTOP)
  1184  void ToggleFullscreen(void)
  1185  {
  1186  #if defined(PLATFORM_DESKTOP)
  1187      if (!CORE.Window.fullscreen)
  1188      {
  1189          // Store previous window position (in case we exit fullscreen)
  1190          glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y);
  1191  
  1192          int monitorCount = 0;
  1193          int monitorIndex = GetCurrentMonitor();
  1194          GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1195  
  1196          // Use current monitor, so we correctly get the display the window is on
  1197          GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL;
  1198  
  1199          if (monitor == NULL)
  1200          {
  1201              TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor");
  1202  
  1203              CORE.Window.fullscreen = false;
  1204              CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
  1205  
  1206              glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
  1207          }
  1208          else
  1209          {
  1210              CORE.Window.fullscreen = true;
  1211              CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
  1212  
  1213              glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
  1214          }
  1215      }
  1216      else
  1217      {
  1218          CORE.Window.fullscreen = false;
  1219          CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
  1220  
  1221          glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
  1222      }
  1223  
  1224      // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
  1225      // NOTE: V-Sync can be enabled by graphic driver configuration
  1226      if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1);
  1227  #endif
  1228  #if defined(PLATFORM_WEB)
  1229  /*
  1230      EM_ASM
  1231      (
  1232          // This strategy works well while using raylib minimal web shell for emscripten,
  1233          // it re-scales the canvas to fullscreen using monitor resolution, for tools this
  1234          // is a good strategy but maybe games prefer to keep current canvas resolution and
  1235          // display it in fullscreen, adjusting monitor resolution if possible
  1236          if (document.fullscreenElement) document.exitFullscreen();
  1237          else Module.requestFullscreen(true, true); //false, true);
  1238      );
  1239  */
  1240      //EM_ASM(Module.requestFullscreen(false, false););
  1241  /*
  1242      if (!CORE.Window.fullscreen)
  1243      {
  1244          // Option 1: Request fullscreen for the canvas element
  1245          // This option does not seem to work at all:
  1246          // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security,
  1247          // the user must click once on the canvas to hide the pointer or transition to full screen
  1248          //emscripten_request_fullscreen("#canvas", false);
  1249  
  1250          // Option 2: Request fullscreen for the canvas element with strategy
  1251          // This option does not seem to work at all
  1252          // Ref: https://github.com/emscripten-core/emscripten/issues/5124
  1253          // EmscriptenFullscreenStrategy strategy = {
  1254              // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
  1255              // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
  1256              // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
  1257              // .canvasResizedCallback = EmscriptenWindowResizedCallback,
  1258              // .canvasResizedCallbackUserData = NULL
  1259          // };
  1260          //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy);
  1261  
  1262          // Option 3: Request fullscreen for the canvas element with strategy
  1263          // It works as expected but only inside the browser (client area)
  1264          EmscriptenFullscreenStrategy strategy = {
  1265              .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
  1266              .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
  1267              .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
  1268              .canvasResizedCallback = EmscriptenWindowResizedCallback,
  1269              .canvasResizedCallbackUserData = NULL
  1270          };
  1271          emscripten_enter_soft_fullscreen("#canvas", &strategy);
  1272  
  1273          int width, height;
  1274          emscripten_get_canvas_element_size("#canvas", &width, &height);
  1275          TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height);
  1276  
  1277          CORE.Window.fullscreen = true;          // Toggle fullscreen flag
  1278          CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
  1279      }
  1280      else
  1281      {
  1282          //emscripten_exit_fullscreen();
  1283          //emscripten_exit_soft_fullscreen();
  1284  
  1285          int width, height;
  1286          emscripten_get_canvas_element_size("#canvas", &width, &height);
  1287          TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height);
  1288  
  1289          CORE.Window.fullscreen = false;          // Toggle fullscreen flag
  1290          CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
  1291      }
  1292  */
  1293  
  1294      CORE.Window.fullscreen = !CORE.Window.fullscreen;          // Toggle fullscreen flag
  1295  #endif
  1296  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  1297      TRACELOG(LOG_WARNING, "SYSTEM: Failed to toggle to windowed mode");
  1298  #endif
  1299  }
  1300  
  1301  // Set window state: maximized, if resizable (only PLATFORM_DESKTOP)
  1302  void MaximizeWindow(void)
  1303  {
  1304  #if defined(PLATFORM_DESKTOP)
  1305      if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE)
  1306      {
  1307          glfwMaximizeWindow(CORE.Window.handle);
  1308          CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
  1309      }
  1310  #endif
  1311  }
  1312  
  1313  // Set window state: minimized (only PLATFORM_DESKTOP)
  1314  void MinimizeWindow(void)
  1315  {
  1316  #if defined(PLATFORM_DESKTOP)
  1317      // NOTE: Following function launches callback that sets appropiate flag!
  1318      glfwIconifyWindow(CORE.Window.handle);
  1319  #endif
  1320  }
  1321  
  1322  // Set window state: not minimized/maximized (only PLATFORM_DESKTOP)
  1323  void RestoreWindow(void)
  1324  {
  1325  #if defined(PLATFORM_DESKTOP)
  1326      if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE)
  1327      {
  1328          // Restores the specified window if it was previously iconified (minimized) or maximized
  1329          glfwRestoreWindow(CORE.Window.handle);
  1330          CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
  1331          CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
  1332      }
  1333  #endif
  1334  }
  1335  
  1336  // Set window configuration state using flags
  1337  void SetWindowState(unsigned int flags)
  1338  {
  1339  #if defined(PLATFORM_DESKTOP)
  1340      // Check previous state and requested state to apply required changes
  1341      // NOTE: In most cases the functions already change the flags internally
  1342  
  1343      // State change: FLAG_VSYNC_HINT
  1344      if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0))
  1345      {
  1346          glfwSwapInterval(1);
  1347          CORE.Window.flags |= FLAG_VSYNC_HINT;
  1348      }
  1349  
  1350      // State change: FLAG_FULLSCREEN_MODE
  1351      if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE))
  1352      {
  1353          ToggleFullscreen();     // NOTE: Window state flag updated inside function
  1354      }
  1355  
  1356      // State change: FLAG_WINDOW_RESIZABLE
  1357      if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
  1358      {
  1359          glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE);
  1360          CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
  1361      }
  1362  
  1363      // State change: FLAG_WINDOW_UNDECORATED
  1364      if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED))
  1365      {
  1366          glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE);
  1367          CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
  1368      }
  1369  
  1370      // State change: FLAG_WINDOW_HIDDEN
  1371      if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
  1372      {
  1373          glfwHideWindow(CORE.Window.handle);
  1374          CORE.Window.flags |= FLAG_WINDOW_HIDDEN;
  1375      }
  1376  
  1377      // State change: FLAG_WINDOW_MINIMIZED
  1378      if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
  1379      {
  1380          //GLFW_ICONIFIED
  1381          MinimizeWindow();       // NOTE: Window state flag updated inside function
  1382      }
  1383  
  1384      // State change: FLAG_WINDOW_MAXIMIZED
  1385      if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
  1386      {
  1387          //GLFW_MAXIMIZED
  1388          MaximizeWindow();       // NOTE: Window state flag updated inside function
  1389      }
  1390  
  1391      // State change: FLAG_WINDOW_UNFOCUSED
  1392      if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
  1393      {
  1394          glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE);
  1395          CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
  1396      }
  1397  
  1398      // State change: FLAG_WINDOW_TOPMOST
  1399      if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
  1400      {
  1401          glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE);
  1402          CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
  1403      }
  1404  
  1405      // State change: FLAG_WINDOW_ALWAYS_RUN
  1406      if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
  1407      {
  1408          CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
  1409      }
  1410  
  1411      // The following states can not be changed after window creation
  1412  
  1413      // State change: FLAG_WINDOW_TRANSPARENT
  1414      if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
  1415      {
  1416          TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
  1417      }
  1418  
  1419      // State change: FLAG_WINDOW_HIGHDPI
  1420      if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
  1421      {
  1422          TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
  1423      }
  1424  
  1425      // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
  1426      if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
  1427      {
  1428          glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
  1429          CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH;
  1430      }
  1431  
  1432      // State change: FLAG_MSAA_4X_HINT
  1433      if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
  1434      {
  1435          TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
  1436      }
  1437  
  1438      // State change: FLAG_INTERLACED_HINT
  1439      if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
  1440      {
  1441          TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
  1442      }
  1443  #endif
  1444  }
  1445  
  1446  // Clear window configuration state flags
  1447  void ClearWindowState(unsigned int flags)
  1448  {
  1449  #if defined(PLATFORM_DESKTOP)
  1450      // Check previous state and requested state to apply required changes
  1451      // NOTE: In most cases the functions already change the flags internally
  1452  
  1453      // State change: FLAG_VSYNC_HINT
  1454      if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0))
  1455      {
  1456          glfwSwapInterval(0);
  1457          CORE.Window.flags &= ~FLAG_VSYNC_HINT;
  1458      }
  1459  
  1460      // State change: FLAG_FULLSCREEN_MODE
  1461      if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0))
  1462      {
  1463          ToggleFullscreen();     // NOTE: Window state flag updated inside function
  1464      }
  1465  
  1466      // State change: FLAG_WINDOW_RESIZABLE
  1467      if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
  1468      {
  1469          glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE);
  1470          CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
  1471      }
  1472  
  1473      // State change: FLAG_WINDOW_UNDECORATED
  1474      if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0))
  1475      {
  1476          glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE);
  1477          CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
  1478      }
  1479  
  1480      // State change: FLAG_WINDOW_HIDDEN
  1481      if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
  1482      {
  1483          glfwShowWindow(CORE.Window.handle);
  1484          CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
  1485      }
  1486  
  1487      // State change: FLAG_WINDOW_MINIMIZED
  1488      if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
  1489      {
  1490          RestoreWindow();       // NOTE: Window state flag updated inside function
  1491      }
  1492  
  1493      // State change: FLAG_WINDOW_MAXIMIZED
  1494      if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
  1495      {
  1496          RestoreWindow();       // NOTE: Window state flag updated inside function
  1497      }
  1498  
  1499      // State change: FLAG_WINDOW_UNFOCUSED
  1500      if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
  1501      {
  1502          glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE);
  1503          CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
  1504      }
  1505  
  1506      // State change: FLAG_WINDOW_TOPMOST
  1507      if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
  1508      {
  1509          glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE);
  1510          CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
  1511      }
  1512  
  1513      // State change: FLAG_WINDOW_ALWAYS_RUN
  1514      if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
  1515      {
  1516          CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
  1517      }
  1518  
  1519      // The following states can not be changed after window creation
  1520  
  1521      // State change: FLAG_WINDOW_TRANSPARENT
  1522      if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
  1523      {
  1524          TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
  1525      }
  1526  
  1527      // State change: FLAG_WINDOW_HIGHDPI
  1528      if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
  1529      {
  1530          TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
  1531      }
  1532  
  1533      // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
  1534      if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
  1535      {
  1536          glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
  1537          CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH;
  1538      }
  1539  
  1540      // State change: FLAG_MSAA_4X_HINT
  1541      if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
  1542      {
  1543          TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
  1544      }
  1545  
  1546      // State change: FLAG_INTERLACED_HINT
  1547      if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
  1548      {
  1549          TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
  1550      }
  1551  #endif
  1552  }
  1553  
  1554  // Set icon for window (only PLATFORM_DESKTOP)
  1555  // NOTE: Image must be in RGBA format, 8bit per channel
  1556  void SetWindowIcon(Image image)
  1557  {
  1558  #if defined(PLATFORM_DESKTOP)
  1559      if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
  1560      {
  1561          GLFWimage icon[1] = { 0 };
  1562  
  1563          icon[0].width = image.width;
  1564          icon[0].height = image.height;
  1565          icon[0].pixels = (unsigned char *)image.data;
  1566  
  1567          // NOTE 1: We only support one image icon
  1568          // NOTE 2: The specified image data is copied before this function returns
  1569          glfwSetWindowIcon(CORE.Window.handle, 1, icon);
  1570      }
  1571      else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
  1572  #endif
  1573  }
  1574  
  1575  // Set title for window (only PLATFORM_DESKTOP)
  1576  void SetWindowTitle(const char *title)
  1577  {
  1578      CORE.Window.title = title;
  1579  #if defined(PLATFORM_DESKTOP)
  1580      glfwSetWindowTitle(CORE.Window.handle, title);
  1581  #endif
  1582  }
  1583  
  1584  // Set window position on screen (windowed mode)
  1585  void SetWindowPosition(int x, int y)
  1586  {
  1587  #if defined(PLATFORM_DESKTOP)
  1588      glfwSetWindowPos(CORE.Window.handle, x, y);
  1589  #endif
  1590  }
  1591  
  1592  // Set monitor for the current window (fullscreen mode)
  1593  void SetWindowMonitor(int monitor)
  1594  {
  1595  #if defined(PLATFORM_DESKTOP)
  1596      int monitorCount = 0;
  1597      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1598  
  1599      if ((monitor >= 0) && (monitor < monitorCount))
  1600      {
  1601          TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
  1602  
  1603          const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
  1604          glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate);
  1605      }
  1606      else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1607  #endif
  1608  }
  1609  
  1610  // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
  1611  void SetWindowMinSize(int width, int height)
  1612  {
  1613  #if defined(PLATFORM_DESKTOP)
  1614      const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
  1615      glfwSetWindowSizeLimits(CORE.Window.handle, width, height, mode->width, mode->height);
  1616  #endif
  1617  }
  1618  
  1619  // Set window dimensions
  1620  void SetWindowSize(int width, int height)
  1621  {
  1622  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  1623      glfwSetWindowSize(CORE.Window.handle, width, height);
  1624  #endif
  1625  }
  1626  
  1627  // Set window opacity, value opacity is between 0.0 and 1.0
  1628  void SetWindowOpacity(float opacity)
  1629  {
  1630  #if defined(PLATFORM_DESKTOP)
  1631      if (opacity >= 1.0f) opacity = 1.0f;
  1632      else if (opacity <= 0.0f) opacity = 0.0f;
  1633      glfwSetWindowOpacity(CORE.Window.handle, opacity);
  1634  #endif
  1635  }
  1636  
  1637  // Get current screen width
  1638  int GetScreenWidth(void)
  1639  {
  1640      return CORE.Window.screen.width;
  1641  }
  1642  
  1643  // Get current screen height
  1644  int GetScreenHeight(void)
  1645  {
  1646      return CORE.Window.screen.height;
  1647  }
  1648  
  1649  // Get current render width which is equal to screen width * dpi scale
  1650  int GetRenderWidth(void)
  1651  {
  1652      return CORE.Window.render.width;
  1653  }
  1654  
  1655  // Get current screen height which is equal to screen height * dpi scale
  1656  int GetRenderHeight(void)
  1657  {
  1658      return CORE.Window.render.height;
  1659  }
  1660  
  1661  // Get native window handle
  1662  void *GetWindowHandle(void)
  1663  {
  1664  #if defined(PLATFORM_DESKTOP) && defined(_WIN32)
  1665      // NOTE: Returned handle is: void *HWND (windows.h)
  1666      return glfwGetWin32Window(CORE.Window.handle);
  1667  #endif
  1668  #if defined(__linux__)
  1669      // NOTE: Returned handle is: unsigned long Window (X.h)
  1670      // typedef unsigned long XID;
  1671      // typedef XID Window;
  1672      //unsigned long id = (unsigned long)glfwGetX11Window(window);
  1673      return NULL;    // TODO: Find a way to return value... cast to void *?
  1674  #endif
  1675  #if defined(__APPLE__)
  1676      // NOTE: Returned handle is: (objc_object *)
  1677      return NULL;    // TODO: return (void *)glfwGetCocoaWindow(window);
  1678  #endif
  1679  
  1680      return NULL;
  1681  }
  1682  
  1683  // Get number of monitors
  1684  int GetMonitorCount(void)
  1685  {
  1686  #if defined(PLATFORM_DESKTOP)
  1687      int monitorCount;
  1688      glfwGetMonitors(&monitorCount);
  1689      return monitorCount;
  1690  #else
  1691      return 1;
  1692  #endif
  1693  }
  1694  
  1695  // Get number of monitors
  1696  int GetCurrentMonitor(void)
  1697  {
  1698      int index = 0;
  1699  
  1700  #if defined(PLATFORM_DESKTOP)
  1701      int monitorCount;
  1702      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1703      GLFWmonitor *monitor = NULL;
  1704  
  1705      if (monitorCount > 1)
  1706      {
  1707          if (IsWindowFullscreen())
  1708          {
  1709              // Get the handle of the monitor that the specified window is in full screen on
  1710              monitor = glfwGetWindowMonitor(CORE.Window.handle);
  1711  
  1712              for (int i = 0; i < monitorCount; i++)
  1713              {
  1714                  if (monitors[i] == monitor)
  1715                  {
  1716                      index = i;
  1717                      break;
  1718                  }
  1719              }
  1720          }
  1721          else
  1722          {
  1723              int x = 0;
  1724              int y = 0;
  1725  
  1726              glfwGetWindowPos(CORE.Window.handle, &x, &y);
  1727  
  1728              for (int i = 0; i < monitorCount; i++)
  1729              {
  1730                  int mx = 0;
  1731                  int my = 0;
  1732  
  1733                  int width = 0;
  1734                  int height = 0;
  1735  
  1736                  monitor = monitors[i];
  1737                  glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height);
  1738  
  1739                  if (x >= mx && x <= (mx + width) && y >= my && y <= (my + height))
  1740                  {
  1741                      index = i;
  1742                      break;
  1743                  }
  1744              }
  1745          }
  1746      }
  1747  #endif
  1748  
  1749      return index;
  1750  }
  1751  
  1752  // Get selected monitor position
  1753  Vector2 GetMonitorPosition(int monitor)
  1754  {
  1755  #if defined(PLATFORM_DESKTOP)
  1756      int monitorCount;
  1757      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1758  
  1759      if ((monitor >= 0) && (monitor < monitorCount))
  1760      {
  1761          int x, y;
  1762          glfwGetMonitorPos(monitors[monitor], &x, &y);
  1763  
  1764          return (Vector2){ (float)x, (float)y };
  1765      }
  1766      else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1767  #endif
  1768      return (Vector2){ 0, 0 };
  1769  }
  1770  
  1771  // Get selected monitor width (currently used by monitor)
  1772  int GetMonitorWidth(int monitor)
  1773  {
  1774  #if defined(PLATFORM_DESKTOP)
  1775      int monitorCount;
  1776      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1777  
  1778      if ((monitor >= 0) && (monitor < monitorCount))
  1779      {
  1780          const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
  1781  
  1782          if (mode) return mode->width;
  1783          else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
  1784      }
  1785      else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1786  #endif
  1787      return 0;
  1788  }
  1789  
  1790  // Get selected monitor height (currently used by monitor)
  1791  int GetMonitorHeight(int monitor)
  1792  {
  1793  #if defined(PLATFORM_DESKTOP)
  1794      int monitorCount;
  1795      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1796  
  1797      if ((monitor >= 0) && (monitor < monitorCount))
  1798      {
  1799          const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
  1800  
  1801          if (mode) return mode->height;
  1802          else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
  1803      }
  1804      else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1805  #endif
  1806      return 0;
  1807  }
  1808  
  1809  // Get selected monitor physical width in millimetres
  1810  int GetMonitorPhysicalWidth(int monitor)
  1811  {
  1812  #if defined(PLATFORM_DESKTOP)
  1813      int monitorCount;
  1814      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1815  
  1816      if ((monitor >= 0) && (monitor < monitorCount))
  1817      {
  1818          int physicalWidth;
  1819          glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL);
  1820          return physicalWidth;
  1821      }
  1822      else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1823  #endif
  1824      return 0;
  1825  }
  1826  
  1827  // Get selected monitor physical height in millimetres
  1828  int GetMonitorPhysicalHeight(int monitor)
  1829  {
  1830  #if defined(PLATFORM_DESKTOP)
  1831      int monitorCount;
  1832      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1833  
  1834      if ((monitor >= 0) && (monitor < monitorCount))
  1835      {
  1836          int physicalHeight;
  1837          glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight);
  1838          return physicalHeight;
  1839      }
  1840      else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1841  #endif
  1842      return 0;
  1843  }
  1844  
  1845  // Get selected monitor refresh rate
  1846  int GetMonitorRefreshRate(int monitor)
  1847  {
  1848  #if defined(PLATFORM_DESKTOP)
  1849      int monitorCount;
  1850      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1851  
  1852      if ((monitor >= 0) && (monitor < monitorCount))
  1853      {
  1854          const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]);
  1855          return vidmode->refreshRate;
  1856      }
  1857      else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1858  #endif
  1859  #if defined(PLATFORM_DRM)
  1860      if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0))
  1861      {
  1862          return CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh;
  1863      }
  1864  #endif
  1865      return 0;
  1866  }
  1867  
  1868  // Get window position XY on monitor
  1869  Vector2 GetWindowPosition(void)
  1870  {
  1871      int x = 0;
  1872      int y = 0;
  1873  #if defined(PLATFORM_DESKTOP)
  1874      glfwGetWindowPos(CORE.Window.handle, &x, &y);
  1875  #endif
  1876      return (Vector2){ (float)x, (float)y };
  1877  }
  1878  
  1879  // Get window scale DPI factor for current monitor
  1880  Vector2 GetWindowScaleDPI(void)
  1881  {
  1882      Vector2 scale = { 1.0f, 1.0f };
  1883  
  1884  #if defined(PLATFORM_DESKTOP)
  1885      float xdpi = 1.0;
  1886      float ydpi = 1.0;
  1887      Vector2 windowPos = GetWindowPosition();
  1888  
  1889      int monitorCount = 0;
  1890      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1891  
  1892      // Check window monitor
  1893      for (int i = 0; i < monitorCount; i++)
  1894      {
  1895          glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi);
  1896  
  1897          int xpos, ypos, width, height;
  1898          glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height);
  1899  
  1900          if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) &&
  1901              (windowPos.y >= ypos) && (windowPos.y < ypos + height))
  1902          {
  1903              scale.x = xdpi;
  1904              scale.y = ydpi;
  1905              break;
  1906          }
  1907      }
  1908  #endif
  1909  
  1910      return scale;
  1911  }
  1912  
  1913  // Get the human-readable, UTF-8 encoded name of the selected monitor
  1914  const char *GetMonitorName(int monitor)
  1915  {
  1916  #if defined(PLATFORM_DESKTOP)
  1917      int monitorCount;
  1918      GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
  1919  
  1920      if ((monitor >= 0) && (monitor < monitorCount))
  1921      {
  1922          return glfwGetMonitorName(monitors[monitor]);
  1923      }
  1924      else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
  1925  #endif
  1926      return "";
  1927  }
  1928  
  1929  // Set clipboard text content
  1930  void SetClipboardText(const char *text)
  1931  {
  1932  #if defined(PLATFORM_DESKTOP)
  1933      glfwSetClipboardString(CORE.Window.handle, text);
  1934  #endif
  1935  #if defined(PLATFORM_WEB)
  1936      emscripten_run_script(TextFormat("navigator.clipboard.writeText('%s')", text));
  1937  #endif
  1938  }
  1939  
  1940  // Get clipboard text content
  1941  // NOTE: returned string is allocated and freed by GLFW
  1942  const char *GetClipboardText(void)
  1943  {
  1944  #if defined(PLATFORM_DESKTOP)
  1945      return glfwGetClipboardString(CORE.Window.handle);
  1946  #endif
  1947  #if defined(PLATFORM_WEB)
  1948      return emscripten_run_script_string("navigator.clipboard.readText()");
  1949  #endif
  1950      return NULL;
  1951  }
  1952  
  1953  // Enable waiting for events on EndDrawing(), no automatic event polling
  1954  void EnableEventWaiting(void)
  1955  {
  1956      CORE.Window.eventWaiting = true;
  1957  }
  1958  
  1959  // Disable waiting for events on EndDrawing(), automatic events polling
  1960  void DisableEventWaiting(void)
  1961  {
  1962      CORE.Window.eventWaiting = false;
  1963  }
  1964  
  1965  // Show mouse cursor
  1966  void ShowCursor(void)
  1967  {
  1968  #if defined(PLATFORM_DESKTOP)
  1969      glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  1970  #endif
  1971  
  1972      CORE.Input.Mouse.cursorHidden = false;
  1973  }
  1974  
  1975  // Hides mouse cursor
  1976  void HideCursor(void)
  1977  {
  1978  #if defined(PLATFORM_DESKTOP)
  1979      glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
  1980  #endif
  1981  
  1982      CORE.Input.Mouse.cursorHidden = true;
  1983  }
  1984  
  1985  // Check if cursor is not visible
  1986  bool IsCursorHidden(void)
  1987  {
  1988      return CORE.Input.Mouse.cursorHidden;
  1989  }
  1990  
  1991  // Enables cursor (unlock cursor)
  1992  void EnableCursor(void)
  1993  {
  1994  #if defined(PLATFORM_DESKTOP)
  1995      glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  1996  #endif
  1997  #if defined(PLATFORM_WEB)
  1998      emscripten_exit_pointerlock();
  1999  #endif
  2000  
  2001      CORE.Input.Mouse.cursorHidden = false;
  2002  }
  2003  
  2004  // Disables cursor (lock cursor)
  2005  void DisableCursor(void)
  2006  {
  2007  #if defined(PLATFORM_DESKTOP)
  2008      glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  2009  #endif
  2010  #if defined(PLATFORM_WEB)
  2011      emscripten_request_pointerlock("#canvas", 1);
  2012  #endif
  2013  
  2014      CORE.Input.Mouse.cursorHidden = true;
  2015  }
  2016  
  2017  // Check if cursor is on the current screen.
  2018  bool IsCursorOnScreen(void)
  2019  {
  2020      return CORE.Input.Mouse.cursorOnScreen;
  2021  }
  2022  
  2023  // Set background color (framebuffer clear color)
  2024  void ClearBackground(Color color)
  2025  {
  2026      rlClearColor(color.r, color.g, color.b, color.a);   // Set clear color
  2027      rlClearScreenBuffers();                             // Clear current framebuffers
  2028  }
  2029  
  2030  // Setup canvas (framebuffer) to start drawing
  2031  void BeginDrawing(void)
  2032  {
  2033      // WARNING: Previously to BeginDrawing() other render textures drawing could happen,
  2034      // consequently the measure for update vs draw is not accurate (only the total frame time is accurate)
  2035  
  2036      CORE.Time.current = GetTime();      // Number of elapsed seconds since InitTimer()
  2037      CORE.Time.update = CORE.Time.current - CORE.Time.previous;
  2038      CORE.Time.previous = CORE.Time.current;
  2039  
  2040      rlLoadIdentity();                   // Reset current matrix (modelview)
  2041      rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling
  2042  
  2043      //rlTranslatef(0.375, 0.375, 0);    // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
  2044                                          // NOTE: Not required with OpenGL 3.3+
  2045  }
  2046  
  2047  // End canvas drawing and swap buffers (double buffering)
  2048  void EndDrawing(void)
  2049  {
  2050      rlDrawRenderBatchActive();      // Update and draw internal render batch
  2051  
  2052  #if defined(SUPPORT_GIF_RECORDING)
  2053      // Draw record indicator
  2054      if (gifRecording)
  2055      {
  2056          #define GIF_RECORD_FRAMERATE    10
  2057          gifFrameCounter++;
  2058  
  2059          // NOTE: We record one gif frame every 10 game frames
  2060          if ((gifFrameCounter%GIF_RECORD_FRAMERATE) == 0)
  2061          {
  2062              // Get image data for the current frame (from backbuffer)
  2063              // NOTE: This process is quite slow... :(
  2064              Vector2 scale = GetWindowScaleDPI();
  2065              unsigned char *screenData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
  2066              msf_gif_frame(&gifState, screenData, 10, 16, (int)((float)CORE.Window.render.width*scale.x)*4);
  2067  
  2068              RL_FREE(screenData);    // Free image data
  2069          }
  2070  
  2071      #if defined(SUPPORT_MODULE_RSHAPES) && defined(SUPPORT_MODULE_RTEXT)
  2072          if (((gifFrameCounter/15)%2) == 1)
  2073          {
  2074              DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON);                 // WARNING: Module required: rshapes
  2075              DrawText("GIF RECORDING", 50, CORE.Window.screen.height - 25, 10, RED);     // WARNING: Module required: rtext
  2076          }
  2077      #endif
  2078  
  2079          rlDrawRenderBatchActive();  // Update and draw internal render batch
  2080      }
  2081  #endif
  2082  
  2083  #if defined(SUPPORT_EVENTS_AUTOMATION)
  2084      // Draw record/play indicator
  2085      if (eventsRecording)
  2086      {
  2087          gifFrameCounter++;
  2088  
  2089          if (((gifFrameCounter/15)%2) == 1)
  2090          {
  2091              DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON);
  2092              DrawText("EVENTS RECORDING", 50, CORE.Window.screen.height - 25, 10, RED);
  2093          }
  2094  
  2095          rlDrawRenderBatchActive();  // Update and draw internal render batch
  2096      }
  2097      else if (eventsPlaying)
  2098      {
  2099          gifFrameCounter++;
  2100  
  2101          if (((gifFrameCounter/15)%2) == 1)
  2102          {
  2103              DrawCircle(30, CORE.Window.screen.height - 20, 10, LIME);
  2104              DrawText("EVENTS PLAYING", 50, CORE.Window.screen.height - 25, 10, GREEN);
  2105          }
  2106  
  2107          rlDrawRenderBatchActive();  // Update and draw internal render batch
  2108      }
  2109  #endif
  2110  
  2111  #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
  2112      SwapScreenBuffer();                  // Copy back buffer to front buffer (screen)
  2113  
  2114      // Frame time control system
  2115      CORE.Time.current = GetTime();
  2116      CORE.Time.draw = CORE.Time.current - CORE.Time.previous;
  2117      CORE.Time.previous = CORE.Time.current;
  2118  
  2119      CORE.Time.frame = CORE.Time.update + CORE.Time.draw;
  2120  
  2121      // Wait for some milliseconds...
  2122      if (CORE.Time.frame < CORE.Time.target)
  2123      {
  2124          WaitTime(CORE.Time.target - CORE.Time.frame);
  2125  
  2126          CORE.Time.current = GetTime();
  2127          double waitTime = CORE.Time.current - CORE.Time.previous;
  2128          CORE.Time.previous = CORE.Time.current;
  2129  
  2130          CORE.Time.frame += waitTime;    // Total frame time: update + draw + wait
  2131      }
  2132  
  2133      PollInputEvents();      // Poll user events (before next frame update)
  2134  #endif
  2135  
  2136  #if defined(SUPPORT_EVENTS_AUTOMATION)
  2137      // Events recording and playing logic
  2138      if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter);
  2139      else if (eventsPlaying)
  2140      {
  2141          // TODO: When should we play? After/before/replace PollInputEvents()?
  2142          if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false;
  2143          PlayAutomationEvent(CORE.Time.frameCounter);
  2144      }
  2145  #endif
  2146  
  2147      CORE.Time.frameCounter++;
  2148  }
  2149  
  2150  // Initialize 2D mode with custom camera (2D)
  2151  void BeginMode2D(Camera2D camera)
  2152  {
  2153      rlDrawRenderBatchActive();      // Update and draw internal render batch
  2154  
  2155      rlLoadIdentity();               // Reset current matrix (modelview)
  2156  
  2157      // Apply 2d camera transformation to modelview
  2158      rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera)));
  2159  
  2160      // Apply screen scaling if required
  2161      rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale));
  2162  }
  2163  
  2164  // Ends 2D mode with custom camera
  2165  void EndMode2D(void)
  2166  {
  2167      rlDrawRenderBatchActive();      // Update and draw internal render batch
  2168  
  2169      rlLoadIdentity();               // Reset current matrix (modelview)
  2170      rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
  2171  }
  2172  
  2173  // Initializes 3D mode with custom camera (3D)
  2174  void BeginMode3D(Camera3D camera)
  2175  {
  2176      rlDrawRenderBatchActive();      // Update and draw internal render batch
  2177  
  2178      rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
  2179      rlPushMatrix();                 // Save previous matrix, which contains the settings for the 2d ortho projection
  2180      rlLoadIdentity();               // Reset current matrix (projection)
  2181  
  2182      float aspect = (float)CORE.Window.currentFbo.width/(float)CORE.Window.currentFbo.height;
  2183  
  2184      // NOTE: zNear and zFar values are important when computing depth buffer values
  2185      if (camera.projection == CAMERA_PERSPECTIVE)
  2186      {
  2187          // Setup perspective projection
  2188          double top = RL_CULL_DISTANCE_NEAR*tan(camera.fovy*0.5*DEG2RAD);
  2189          double right = top*aspect;
  2190  
  2191          rlFrustum(-right, right, -top, top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  2192      }
  2193      else if (camera.projection == CAMERA_ORTHOGRAPHIC)
  2194      {
  2195          // Setup orthographic projection
  2196          double top = camera.fovy/2.0;
  2197          double right = top*aspect;
  2198  
  2199          rlOrtho(-right, right, -top,top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  2200      }
  2201  
  2202      rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
  2203      rlLoadIdentity();               // Reset current matrix (modelview)
  2204  
  2205      // Setup Camera view
  2206      Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
  2207      rlMultMatrixf(MatrixToFloat(matView));      // Multiply modelview matrix by view matrix (camera)
  2208  
  2209      rlEnableDepthTest();            // Enable DEPTH_TEST for 3D
  2210  }
  2211  
  2212  // Ends 3D mode and returns to default 2D orthographic mode
  2213  void EndMode3D(void)
  2214  {
  2215      rlDrawRenderBatchActive();      // Update and draw internal render batch
  2216  
  2217      rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
  2218      rlPopMatrix();                  // Restore previous matrix (projection) from matrix stack
  2219  
  2220      rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
  2221      rlLoadIdentity();               // Reset current matrix (modelview)
  2222  
  2223      rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
  2224  
  2225      rlDisableDepthTest();           // Disable DEPTH_TEST for 2D
  2226  }
  2227  
  2228  // Initializes render texture for drawing
  2229  void BeginTextureMode(RenderTexture2D target)
  2230  {
  2231      rlDrawRenderBatchActive();      // Update and draw internal render batch
  2232  
  2233      rlEnableFramebuffer(target.id); // Enable render target
  2234  
  2235      // Set viewport and RLGL internal framebuffer size
  2236      rlViewport(0, 0, target.texture.width, target.texture.height);
  2237      rlSetFramebufferWidth(target.texture.width);
  2238      rlSetFramebufferHeight(target.texture.height);
  2239  
  2240      rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
  2241      rlLoadIdentity();               // Reset current matrix (projection)
  2242  
  2243      // Set orthographic projection to current framebuffer size
  2244      // NOTE: Configured top-left corner as (0, 0)
  2245      rlOrtho(0, target.texture.width, target.texture.height, 0, 0.0f, 1.0f);
  2246  
  2247      rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
  2248      rlLoadIdentity();               // Reset current matrix (modelview)
  2249  
  2250      //rlScalef(0.0f, -1.0f, 0.0f);  // Flip Y-drawing (?)
  2251  
  2252      // Setup current width/height for proper aspect ratio
  2253      // calculation when using BeginMode3D()
  2254      CORE.Window.currentFbo.width = target.texture.width;
  2255      CORE.Window.currentFbo.height = target.texture.height;
  2256  }
  2257  
  2258  // Ends drawing to render texture
  2259  void EndTextureMode(void)
  2260  {
  2261      rlDrawRenderBatchActive();      // Update and draw internal render batch
  2262  
  2263      rlDisableFramebuffer();         // Disable render target (fbo)
  2264  
  2265      // Set viewport to default framebuffer size
  2266      SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
  2267  
  2268      // Reset current fbo to screen size
  2269      CORE.Window.currentFbo.width = CORE.Window.render.width;
  2270      CORE.Window.currentFbo.height = CORE.Window.render.height;
  2271  }
  2272  
  2273  // Begin custom shader mode
  2274  void BeginShaderMode(Shader shader)
  2275  {
  2276      rlSetShader(shader.id, shader.locs);
  2277  }
  2278  
  2279  // End custom shader mode (returns to default shader)
  2280  void EndShaderMode(void)
  2281  {
  2282      rlSetShader(rlGetShaderIdDefault(), rlGetShaderLocsDefault());
  2283  }
  2284  
  2285  // Begin blending mode (alpha, additive, multiplied, subtract, custom)
  2286  // NOTE: Blend modes supported are enumerated in BlendMode enum
  2287  void BeginBlendMode(int mode)
  2288  {
  2289      rlSetBlendMode(mode);
  2290  }
  2291  
  2292  // End blending mode (reset to default: alpha blending)
  2293  void EndBlendMode(void)
  2294  {
  2295      rlSetBlendMode(BLEND_ALPHA);
  2296  }
  2297  
  2298  // Begin scissor mode (define screen area for following drawing)
  2299  // NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left
  2300  void BeginScissorMode(int x, int y, int width, int height)
  2301  {
  2302      rlDrawRenderBatchActive();      // Update and draw internal render batch
  2303  
  2304      rlEnableScissorTest();
  2305  
  2306  #if defined(__APPLE__)
  2307      Vector2 scale = GetWindowScaleDPI();
  2308      rlScissor((int)(x*scale.x), (int)(GetScreenHeight()*scale.y - (((y + height)*scale.y))), (int)(width*scale.x), (int)(height*scale.y));
  2309  #else
  2310      if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
  2311      {
  2312          Vector2 scale = GetWindowScaleDPI();
  2313          rlScissor((int)(x*scale.x), (int)(CORE.Window.currentFbo.height - (y + height)*scale.y), (int)(width*scale.x), (int)(height*scale.y));
  2314      }
  2315      else
  2316      {
  2317          rlScissor(x, CORE.Window.currentFbo.height - (y + height), width, height);
  2318      }
  2319  #endif
  2320  }
  2321  
  2322  // End scissor mode
  2323  void EndScissorMode(void)
  2324  {
  2325      rlDrawRenderBatchActive();      // Update and draw internal render batch
  2326      rlDisableScissorTest();
  2327  }
  2328  
  2329  // Begin VR drawing configuration
  2330  void BeginVrStereoMode(VrStereoConfig config)
  2331  {
  2332      rlEnableStereoRender();
  2333  
  2334      // Set stereo render matrices
  2335      rlSetMatrixProjectionStereo(config.projection[0], config.projection[1]);
  2336      rlSetMatrixViewOffsetStereo(config.viewOffset[0], config.viewOffset[1]);
  2337  }
  2338  
  2339  // End VR drawing process (and desktop mirror)
  2340  void EndVrStereoMode(void)
  2341  {
  2342      rlDisableStereoRender();
  2343  }
  2344  
  2345  // Load VR stereo config for VR simulator device parameters
  2346  VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
  2347  {
  2348      VrStereoConfig config = { 0 };
  2349  
  2350      if ((rlGetVersion() == RL_OPENGL_33) || (rlGetVersion() == RL_OPENGL_ES_20))
  2351      {
  2352          // Compute aspect ratio
  2353          float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution;
  2354  
  2355          // Compute lens parameters
  2356          float lensShift = (device.hScreenSize*0.25f - device.lensSeparationDistance*0.5f)/device.hScreenSize;
  2357          config.leftLensCenter[0] = 0.25f + lensShift;
  2358          config.leftLensCenter[1] = 0.5f;
  2359          config.rightLensCenter[0] = 0.75f - lensShift;
  2360          config.rightLensCenter[1] = 0.5f;
  2361          config.leftScreenCenter[0] = 0.25f;
  2362          config.leftScreenCenter[1] = 0.5f;
  2363          config.rightScreenCenter[0] = 0.75f;
  2364          config.rightScreenCenter[1] = 0.5f;
  2365  
  2366          // Compute distortion scale parameters
  2367          // NOTE: To get lens max radius, lensShift must be normalized to [-1..1]
  2368          float lensRadius = fabsf(-1.0f - 4.0f*lensShift);
  2369          float lensRadiusSq = lensRadius*lensRadius;
  2370          float distortionScale = device.lensDistortionValues[0] +
  2371                                  device.lensDistortionValues[1]*lensRadiusSq +
  2372                                  device.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq +
  2373                                  device.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq;
  2374  
  2375          float normScreenWidth = 0.5f;
  2376          float normScreenHeight = 1.0f;
  2377          config.scaleIn[0] = 2.0f/normScreenWidth;
  2378          config.scaleIn[1] = 2.0f/normScreenHeight/aspect;
  2379          config.scale[0] = normScreenWidth*0.5f/distortionScale;
  2380          config.scale[1] = normScreenHeight*0.5f*aspect/distortionScale;
  2381  
  2382          // Fovy is normally computed with: 2*atan2f(device.vScreenSize, 2*device.eyeToScreenDistance)
  2383          // ...but with lens distortion it is increased (see Oculus SDK Documentation)
  2384          float fovy = 2.0f*atan2f(device.vScreenSize*0.5f*distortionScale, device.eyeToScreenDistance);     // Really need distortionScale?
  2385         // float fovy = 2.0f*(float)atan2f(device.vScreenSize*0.5f, device.eyeToScreenDistance);
  2386  
  2387          // Compute camera projection matrices
  2388          float projOffset = 4.0f*lensShift;      // Scaled to projection space coordinates [-1..1]
  2389          Matrix proj = MatrixPerspective(fovy, aspect, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  2390  
  2391          config.projection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f));
  2392          config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
  2393  
  2394          // Compute camera transformation matrices
  2395          // NOTE: Camera movement might seem more natural if we model the head.
  2396          // Our axis of rotation is the base of our head, so we might want to add
  2397          // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions.
  2398          config.viewOffset[0] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
  2399          config.viewOffset[1] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
  2400  
  2401          // Compute eyes Viewports
  2402          /*
  2403          config.eyeViewportRight[0] = 0;
  2404          config.eyeViewportRight[1] = 0;
  2405          config.eyeViewportRight[2] = device.hResolution/2;
  2406          config.eyeViewportRight[3] = device.vResolution;
  2407  
  2408          config.eyeViewportLeft[0] = device.hResolution/2;
  2409          config.eyeViewportLeft[1] = 0;
  2410          config.eyeViewportLeft[2] = device.hResolution/2;
  2411          config.eyeViewportLeft[3] = device.vResolution;
  2412          */
  2413      }
  2414      else TRACELOG(LOG_WARNING, "RLGL: VR Simulator not supported on OpenGL 1.1");
  2415  
  2416      return config;
  2417  }
  2418  
  2419  // Unload VR stereo config properties
  2420  void UnloadVrStereoConfig(VrStereoConfig config)
  2421  {
  2422      //...
  2423  }
  2424  
  2425  // Load shader from files and bind default locations
  2426  // NOTE: If shader string is NULL, using default vertex/fragment shaders
  2427  Shader LoadShader(const char *vsFileName, const char *fsFileName)
  2428  {
  2429      Shader shader = { 0 };
  2430  
  2431      char *vShaderStr = NULL;
  2432      char *fShaderStr = NULL;
  2433  
  2434      if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName);
  2435      if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName);
  2436  
  2437      shader = LoadShaderFromMemory(vShaderStr, fShaderStr);
  2438  
  2439      UnloadFileText(vShaderStr);
  2440      UnloadFileText(fShaderStr);
  2441  
  2442      return shader;
  2443  }
  2444  
  2445  // Load shader from code strings and bind default locations
  2446  Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
  2447  {
  2448      Shader shader = { 0 };
  2449      shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
  2450  
  2451      // NOTE: All locations must be reseted to -1 (no location)
  2452      for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
  2453  
  2454      shader.id = rlLoadShaderCode(vsCode, fsCode);
  2455  
  2456      // After shader loading, we TRY to set default location names
  2457      if (shader.id > 0)
  2458      {
  2459          // Default shader attribute locations have been binded before linking:
  2460          //          vertex position location    = 0
  2461          //          vertex texcoord location    = 1
  2462          //          vertex normal location      = 2
  2463          //          vertex color location       = 3
  2464          //          vertex tangent location     = 4
  2465          //          vertex texcoord2 location   = 5
  2466  
  2467          // NOTE: If any location is not found, loc point becomes -1
  2468  
  2469          // Get handles to GLSL input attibute locations
  2470          shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
  2471          shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
  2472          shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
  2473          shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
  2474          shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
  2475          shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
  2476  
  2477          // Get handles to GLSL uniform locations (vertex shader)
  2478          shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
  2479          shader.locs[SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW);
  2480          shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
  2481          shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
  2482          shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
  2483  
  2484          // Get handles to GLSL uniform locations (fragment shader)
  2485          shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
  2486          shader.locs[SHADER_LOC_MAP_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0);  // SHADER_LOC_MAP_ALBEDO
  2487          shader.locs[SHADER_LOC_MAP_SPECULAR] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1); // SHADER_LOC_MAP_METALNESS
  2488          shader.locs[SHADER_LOC_MAP_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2);
  2489      }
  2490  
  2491      return shader;
  2492  }
  2493  
  2494  // Unload shader from GPU memory (VRAM)
  2495  void UnloadShader(Shader shader)
  2496  {
  2497      if (shader.id != rlGetShaderIdDefault())
  2498      {
  2499          rlUnloadShaderProgram(shader.id);
  2500          RL_FREE(shader.locs);
  2501      }
  2502  }
  2503  
  2504  // Get shader uniform location
  2505  int GetShaderLocation(Shader shader, const char *uniformName)
  2506  {
  2507      return rlGetLocationUniform(shader.id, uniformName);
  2508  }
  2509  
  2510  // Get shader attribute location
  2511  int GetShaderLocationAttrib(Shader shader, const char *attribName)
  2512  {
  2513      return rlGetLocationAttrib(shader.id, attribName);
  2514  }
  2515  
  2516  // Set shader uniform value
  2517  void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType)
  2518  {
  2519      SetShaderValueV(shader, locIndex, value, uniformType, 1);
  2520  }
  2521  
  2522  // Set shader uniform value vector
  2523  void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
  2524  {
  2525      rlEnableShader(shader.id);
  2526      rlSetUniform(locIndex, value, uniformType, count);
  2527      //rlDisableShader();      // Avoid reseting current shader program, in case other uniforms are set
  2528  }
  2529  
  2530  // Set shader uniform value (matrix 4x4)
  2531  void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
  2532  {
  2533      rlEnableShader(shader.id);
  2534      rlSetUniformMatrix(locIndex, mat);
  2535      //rlDisableShader();
  2536  }
  2537  
  2538  // Set shader uniform value for texture
  2539  void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
  2540  {
  2541      rlEnableShader(shader.id);
  2542      rlSetUniformSampler(locIndex, texture.id);
  2543      //rlDisableShader();
  2544  }
  2545  
  2546  // Get a ray trace from mouse position
  2547  Ray GetMouseRay(Vector2 mouse, Camera camera)
  2548  {
  2549      Ray ray = { 0 };
  2550  
  2551      // Calculate normalized device coordinates
  2552      // NOTE: y value is negative
  2553      float x = (2.0f*mouse.x)/(float)GetScreenWidth() - 1.0f;
  2554      float y = 1.0f - (2.0f*mouse.y)/(float)GetScreenHeight();
  2555      float z = 1.0f;
  2556  
  2557      // Store values in a vector
  2558      Vector3 deviceCoords = { x, y, z };
  2559  
  2560      // Calculate view matrix from camera look at
  2561      Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
  2562  
  2563      Matrix matProj = MatrixIdentity();
  2564  
  2565      if (camera.projection == CAMERA_PERSPECTIVE)
  2566      {
  2567          // Calculate projection matrix from perspective
  2568          matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)GetScreenWidth()/(double)GetScreenHeight()), RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  2569      }
  2570      else if (camera.projection == CAMERA_ORTHOGRAPHIC)
  2571      {
  2572          float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
  2573          double top = camera.fovy/2.0;
  2574          double right = top*aspect;
  2575  
  2576          // Calculate projection matrix from orthographic
  2577          matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
  2578      }
  2579  
  2580      // Unproject far/near points
  2581      Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
  2582      Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
  2583  
  2584      // Unproject the mouse cursor in the near plane.
  2585      // We need this as the source position because orthographic projects, compared to perspect doesn't have a
  2586      // convergence point, meaning that the "eye" of the camera is more like a plane than a point.
  2587      Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
  2588  
  2589      // Calculate normalized direction vector
  2590      Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));
  2591  
  2592      if (camera.projection == CAMERA_PERSPECTIVE) ray.position = camera.position;
  2593      else if (camera.projection == CAMERA_ORTHOGRAPHIC) ray.position = cameraPlanePointerPos;
  2594  
  2595      // Apply calculated vectors to ray
  2596      ray.direction = direction;
  2597  
  2598      return ray;
  2599  }
  2600  
  2601  // Get transform matrix for camera
  2602  Matrix GetCameraMatrix(Camera camera)
  2603  {
  2604      return MatrixLookAt(camera.position, camera.target, camera.up);
  2605  }
  2606  
  2607  // Get camera 2d transform matrix
  2608  Matrix GetCameraMatrix2D(Camera2D camera)
  2609  {
  2610      Matrix matTransform = { 0 };
  2611      // The camera in world-space is set by
  2612      //   1. Move it to target
  2613      //   2. Rotate by -rotation and scale by (1/zoom)
  2614      //      When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller),
  2615      //      not for the camera getting bigger, hence the invert. Same deal with rotation.
  2616      //   3. Move it by (-offset);
  2617      //      Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera)
  2618      //      we need to do it into opposite direction (inverse transform)
  2619  
  2620      // Having camera transform in world-space, inverse of it gives the modelview transform.
  2621      // Since (A*B*C)' = C'*B'*A', the modelview is
  2622      //   1. Move to offset
  2623      //   2. Rotate and Scale
  2624      //   3. Move by -target
  2625      Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
  2626      Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
  2627      Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
  2628      Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f);
  2629  
  2630      matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
  2631  
  2632      return matTransform;
  2633  }
  2634  
  2635  // Get the screen space position from a 3d world space position
  2636  Vector2 GetWorldToScreen(Vector3 position, Camera camera)
  2637  {
  2638      Vector2 screenPosition = GetWorldToScreenEx(position, camera, GetScreenWidth(), GetScreenHeight());
  2639  
  2640      return screenPosition;
  2641  }
  2642  
  2643  // Get size position for a 3d world space position (useful for texture drawing)
  2644  Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height)
  2645  {
  2646      // Calculate projection matrix (from perspective instead of frustum
  2647      Matrix matProj = MatrixIdentity();
  2648  
  2649      if (camera.projection == CAMERA_PERSPECTIVE)
  2650      {
  2651          // Calculate projection matrix from perspective
  2652          matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  2653      }
  2654      else if (camera.projection == CAMERA_ORTHOGRAPHIC)
  2655      {
  2656          float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
  2657          double top = camera.fovy/2.0;
  2658          double right = top*aspect;
  2659  
  2660          // Calculate projection matrix from orthographic
  2661          matProj = MatrixOrtho(-right, right, -top, top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
  2662      }
  2663  
  2664      // Calculate view matrix from camera look at (and transpose it)
  2665      Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
  2666  
  2667      // TODO: Why not use Vector3Transform(Vector3 v, Matrix mat)?
  2668  
  2669      // Convert world position vector to quaternion
  2670      Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
  2671  
  2672      // Transform world position to view
  2673      worldPos = QuaternionTransform(worldPos, matView);
  2674  
  2675      // Transform result to projection (clip space position)
  2676      worldPos = QuaternionTransform(worldPos, matProj);
  2677  
  2678      // Calculate normalized device coordinates (inverted y)
  2679      Vector3 ndcPos = { worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w };
  2680  
  2681      // Calculate 2d screen position vector
  2682      Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)width, (ndcPos.y + 1.0f)/2.0f*(float)height };
  2683  
  2684      return screenPosition;
  2685  }
  2686  
  2687  // Get the screen space position for a 2d camera world space position
  2688  Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera)
  2689  {
  2690      Matrix matCamera = GetCameraMatrix2D(camera);
  2691      Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera);
  2692  
  2693      return (Vector2){ transform.x, transform.y };
  2694  }
  2695  
  2696  // Get the world space position for a 2d camera screen space position
  2697  Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
  2698  {
  2699      Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera));
  2700      Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera);
  2701  
  2702      return (Vector2){ transform.x, transform.y };
  2703  }
  2704  
  2705  // Set target FPS (maximum)
  2706  void SetTargetFPS(int fps)
  2707  {
  2708      if (fps < 1) CORE.Time.target = 0.0;
  2709      else CORE.Time.target = 1.0/(double)fps;
  2710  
  2711      TRACELOG(LOG_INFO, "TIMER: Target time per frame: %02.03f milliseconds", (float)CORE.Time.target*1000.0f);
  2712  }
  2713  
  2714  // Get current FPS
  2715  // NOTE: We calculate an average framerate
  2716  int GetFPS(void)
  2717  {
  2718      int fps = 0;
  2719  
  2720  #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
  2721      #define FPS_CAPTURE_FRAMES_COUNT    30      // 30 captures
  2722      #define FPS_AVERAGE_TIME_SECONDS   0.5f     // 500 millisecondes
  2723      #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT)
  2724  
  2725      static int index = 0;
  2726      static float history[FPS_CAPTURE_FRAMES_COUNT] = { 0 };
  2727      static float average = 0, last = 0;
  2728      float fpsFrame = GetFrameTime();
  2729  
  2730      if (fpsFrame == 0) return 0;
  2731  
  2732      if ((GetTime() - last) > FPS_STEP)
  2733      {
  2734          last = (float)GetTime();
  2735          index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT;
  2736          average -= history[index];
  2737          history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT;
  2738          average += history[index];
  2739      }
  2740  
  2741      fps = (int)roundf(1.0f/average);
  2742  #endif
  2743  
  2744      return fps;
  2745  }
  2746  
  2747  // Get time in seconds for last frame drawn (delta time)
  2748  float GetFrameTime(void)
  2749  {
  2750      return (float)CORE.Time.frame;
  2751  }
  2752  
  2753  // Get elapsed time measure in seconds since InitTimer()
  2754  // NOTE: On PLATFORM_DESKTOP InitTimer() is called on InitWindow()
  2755  // NOTE: On PLATFORM_DESKTOP, timer is initialized on glfwInit()
  2756  double GetTime(void)
  2757  {
  2758  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  2759      return glfwGetTime();   // Elapsed time since glfwInit()
  2760  #endif
  2761  
  2762  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  2763      struct timespec ts = { 0 };
  2764      clock_gettime(CLOCK_MONOTONIC, &ts);
  2765      unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
  2766  
  2767      return (double)(time - CORE.Time.base)*1e-9;  // Elapsed time since InitTimer()
  2768  #endif
  2769  }
  2770  
  2771  // Setup window configuration flags (view FLAGS)
  2772  // NOTE: This function is expected to be called before window creation,
  2773  // because it setups some flags for the window creation process.
  2774  // To configure window states after creation, just use SetWindowState()
  2775  void SetConfigFlags(unsigned int flags)
  2776  {
  2777      // Selected flags are set but not evaluated at this point,
  2778      // flag evaluation happens at InitWindow() or SetWindowState()
  2779      CORE.Window.flags |= flags;
  2780  }
  2781  
  2782  // NOTE TRACELOG() function is located in [utils.h]
  2783  
  2784  // Takes a screenshot of current screen (saved a .png)
  2785  void TakeScreenshot(const char *fileName)
  2786  {
  2787  #if defined(SUPPORT_MODULE_RTEXTURES)
  2788      Vector2 scale = GetWindowScaleDPI();
  2789      unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
  2790      Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
  2791  
  2792      char path[2048] = { 0 };
  2793      strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName));
  2794  
  2795      ExportImage(image, path);           // WARNING: Module required: rtextures
  2796      RL_FREE(imgData);
  2797  
  2798  #if defined(PLATFORM_WEB)
  2799      // Download file from MEMFS (emscripten memory filesystem)
  2800      // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html
  2801      emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path)));
  2802  #endif
  2803  
  2804      TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path);
  2805  #else
  2806      TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures");
  2807  #endif
  2808  }
  2809  
  2810  // Get a random value between min and max (both included)
  2811  int GetRandomValue(int min, int max)
  2812  {
  2813      if (min > max)
  2814      {
  2815          int tmp = max;
  2816          max = min;
  2817          min = tmp;
  2818      }
  2819      
  2820      // WARNING: Ranges higher than RAND_MAX will return invalid results. More specifically, if (max - min) > INT_MAX there will
  2821      // be an overflow, and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold.
  2822      if ((unsigned int)(max - min) > (unsigned int)RAND_MAX)
  2823      {
  2824          TRACELOG(LOG_WARNING, "Invalid GetRandomValue arguments. Range should not be higher than %i.", RAND_MAX);
  2825      }
  2826  
  2827      return (rand()%(abs(max - min) + 1) + min);
  2828  }
  2829  
  2830  // Set the seed for the random number generator
  2831  void SetRandomSeed(unsigned int seed)
  2832  {
  2833      srand(seed);
  2834  }
  2835  
  2836  // Check if the file exists
  2837  bool FileExists(const char *fileName)
  2838  {
  2839      bool result = false;
  2840  
  2841  #if defined(_WIN32)
  2842      if (_access(fileName, 0) != -1) result = true;
  2843  #else
  2844      if (access(fileName, F_OK) != -1) result = true;
  2845  #endif
  2846  
  2847      // NOTE: Alternatively, stat() can be used instead of access()
  2848      //#include <sys/stat.h>
  2849      //struct stat statbuf;
  2850      //if (stat(filename, &statbuf) == 0) result = true;
  2851  
  2852      return result;
  2853  }
  2854  
  2855  // Check file extension
  2856  // NOTE: Extensions checking is not case-sensitive
  2857  bool IsFileExtension(const char *fileName, const char *ext)
  2858  {
  2859      #define MAX_FILE_EXTENSION_SIZE  16
  2860  
  2861      bool result = false;
  2862      const char *fileExt = GetFileExtension(fileName);
  2863  
  2864      if (fileExt != NULL)
  2865      {
  2866  #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_TEXT_MANIPULATION)
  2867          int extCount = 0;
  2868          const char **checkExts = TextSplit(ext, ';', &extCount);  // WARNING: Module required: rtext
  2869  
  2870          char fileExtLower[MAX_FILE_EXTENSION_SIZE + 1] = { 0 };
  2871          strncpy(fileExtLower, TextToLower(fileExt),MAX_FILE_EXTENSION_SIZE);  // WARNING: Module required: rtext
  2872  
  2873          for (int i = 0; i < extCount; i++)
  2874          {
  2875              if (strcmp(fileExtLower, TextToLower(checkExts[i])) == 0)
  2876              {
  2877                  result = true;
  2878                  break;
  2879              }
  2880          }
  2881  #else
  2882          if (strcmp(fileExt, ext) == 0) result = true;
  2883  #endif
  2884      }
  2885  
  2886      return result;
  2887  }
  2888  
  2889  // Check if a directory path exists
  2890  bool DirectoryExists(const char *dirPath)
  2891  {
  2892      bool result = false;
  2893      DIR *dir = opendir(dirPath);
  2894  
  2895      if (dir != NULL)
  2896      {
  2897          result = true;
  2898          closedir(dir);
  2899      }
  2900  
  2901      return result;
  2902  }
  2903  
  2904  // Get file length in bytes
  2905  // NOTE: GetFileSize() conflicts with windows.h
  2906  int GetFileLength(const char *fileName)
  2907  {
  2908      int size = 0;
  2909  
  2910      FILE *file = fopen(fileName, "rb");
  2911  
  2912      if (file != NULL)
  2913      {
  2914          fseek(file, 0L, SEEK_END);
  2915          size = (int)ftell(file);
  2916          fclose(file);
  2917      }
  2918  
  2919      return size;
  2920  }
  2921  
  2922  // Get pointer to extension for a filename string (includes the dot: .png)
  2923  const char *GetFileExtension(const char *fileName)
  2924  {
  2925      const char *dot = strrchr(fileName, '.');
  2926  
  2927      if (!dot || dot == fileName) return NULL;
  2928  
  2929      return dot;
  2930  }
  2931  
  2932  // String pointer reverse break: returns right-most occurrence of charset in s
  2933  static const char *strprbrk(const char *s, const char *charset)
  2934  {
  2935      const char *latestMatch = NULL;
  2936      for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
  2937      return latestMatch;
  2938  }
  2939  
  2940  // Get pointer to filename for a path string
  2941  const char *GetFileName(const char *filePath)
  2942  {
  2943      const char *fileName = NULL;
  2944      if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
  2945  
  2946      if (!fileName) return filePath;
  2947  
  2948      return fileName + 1;
  2949  }
  2950  
  2951  // Get filename string without extension (uses static string)
  2952  const char *GetFileNameWithoutExt(const char *filePath)
  2953  {
  2954      #define MAX_FILENAMEWITHOUTEXT_LENGTH   256
  2955  
  2956      static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
  2957      memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
  2958  
  2959      if (filePath != NULL) strcpy(fileName, GetFileName(filePath));   // Get filename with extension
  2960  
  2961      int size = (int)strlen(fileName);   // Get size in bytes
  2962  
  2963      for (int i = 0; (i < size) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
  2964      {
  2965          if (fileName[i] == '.')
  2966          {
  2967              // NOTE: We break on first '.' found
  2968              fileName[i] = '\0';
  2969              break;
  2970          }
  2971      }
  2972  
  2973      return fileName;
  2974  }
  2975  
  2976  // Get directory for a given filePath
  2977  const char *GetDirectoryPath(const char *filePath)
  2978  {
  2979  /*
  2980      // NOTE: Directory separator is different in Windows and other platforms,
  2981      // fortunately, Windows also support the '/' separator, that's the one should be used
  2982      #if defined(_WIN32)
  2983          char separator = '\\';
  2984      #else
  2985          char separator = '/';
  2986      #endif
  2987  */
  2988      const char *lastSlash = NULL;
  2989      static char dirPath[MAX_FILEPATH_LENGTH] = { 0 };
  2990      memset(dirPath, 0, MAX_FILEPATH_LENGTH);
  2991  
  2992      // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /),
  2993      // we add the current directory path to dirPath
  2994      if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/')
  2995      {
  2996          // For security, we set starting path to current directory,
  2997          // obtained path will be concated to this
  2998          dirPath[0] = '.';
  2999          dirPath[1] = '/';
  3000      }
  3001  
  3002      lastSlash = strprbrk(filePath, "\\/");
  3003      if (lastSlash)
  3004      {
  3005          if (lastSlash == filePath)
  3006          {
  3007              // The last and only slash is the leading one: path is in a root directory
  3008              dirPath[0] = filePath[0];
  3009              dirPath[1] = '\0';
  3010          }
  3011          else
  3012          {
  3013              // NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
  3014              memcpy(dirPath + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0), filePath, strlen(filePath) - (strlen(lastSlash) - 1));
  3015              dirPath[strlen(filePath) - strlen(lastSlash) + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0)] = '\0';  // Add '\0' manually
  3016          }
  3017      }
  3018  
  3019      return dirPath;
  3020  }
  3021  
  3022  // Get previous directory path for a given path
  3023  const char *GetPrevDirectoryPath(const char *dirPath)
  3024  {
  3025      static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
  3026      memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
  3027      int pathLen = (int)strlen(dirPath);
  3028  
  3029      if (pathLen <= 3) strcpy(prevDirPath, dirPath);
  3030  
  3031      for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--)
  3032      {
  3033          if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
  3034          {
  3035              // Check for root: "C:\" or "/"
  3036              if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
  3037  
  3038              strncpy(prevDirPath, dirPath, i);
  3039              break;
  3040          }
  3041      }
  3042  
  3043      return prevDirPath;
  3044  }
  3045  
  3046  // Get current working directory
  3047  const char *GetWorkingDirectory(void)
  3048  {
  3049      static char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
  3050      memset(currentDir, 0, MAX_FILEPATH_LENGTH);
  3051  
  3052      char *path = GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
  3053  
  3054      return path;
  3055  }
  3056  
  3057  const char *GetApplicationDirectory(void)
  3058  {
  3059      static char appDir[MAX_FILEPATH_LENGTH] = { 0 };
  3060      memset(appDir, 0, MAX_FILEPATH_LENGTH);
  3061  
  3062  #if defined(_WIN32)
  3063      int len = 0;
  3064  #if defined (UNICODE)
  3065      unsigned short widePath[MAX_PATH];
  3066      len = GetModuleFileNameW(NULL, widePath, MAX_PATH);
  3067      len = WideCharToMultiByte(0, 0, widePath, len, appDir, MAX_PATH, NULL, NULL);
  3068  #else
  3069      len = GetModuleFileNameA(NULL, appDir, MAX_PATH);
  3070  #endif
  3071      if (len > 0)
  3072      {
  3073          for (int i = len; i >= 0; --i)
  3074          {
  3075              if (appDir[i] == '\\')
  3076              {
  3077                  appDir[i + 1] = '\0';
  3078                  break;
  3079              }
  3080          }
  3081      }
  3082      else
  3083      {
  3084          appDir[0] = '.';
  3085          appDir[1] = '\\';
  3086      }
  3087  
  3088  #elif defined(__linux__)
  3089      unsigned int size = sizeof(appDir);
  3090      ssize_t len = readlink("/proc/self/exe", appDir, size);
  3091  
  3092      if (len > 0)
  3093      {
  3094          for (int i = len; i >= 0; --i)
  3095          {
  3096              if (appDir[i] == '/')
  3097              {
  3098                  appDir[i + 1] = '\0';
  3099                  break;
  3100              }
  3101          }
  3102      }
  3103      else
  3104      {
  3105          appDir[0] = '.';
  3106          appDir[1] = '/';
  3107      }
  3108  #elif defined(__APPLE__)
  3109      uint32_t size = sizeof(appDir);
  3110  
  3111      if (_NSGetExecutablePath(appDir, &size) == 0)
  3112      {
  3113          int len = strlen(appDir);
  3114          for (int i = len; i >= 0; --i)
  3115          {
  3116              if (appDir[i] == '/')
  3117              {
  3118                  appDir[i + 1] = '\0';
  3119                  break;
  3120              }
  3121          }
  3122      }
  3123      else
  3124      {
  3125          appDir[0] = '.';
  3126          appDir[1] = '/';
  3127      }
  3128  #endif
  3129  
  3130      return appDir;
  3131  }
  3132  
  3133  // Load directory filepaths
  3134  // NOTE: Base path is prepended to the scanned filepaths
  3135  // WARNING: Directory is scanned twice, first time to get files count
  3136  // No recursive scanning is done!
  3137  FilePathList LoadDirectoryFiles(const char *dirPath)
  3138  {
  3139      FilePathList files = { 0 };
  3140      unsigned int fileCounter = 0;
  3141  
  3142      struct dirent *entity;
  3143      DIR *dir = opendir(dirPath);
  3144  
  3145      if (dir != NULL) // It's a directory
  3146      {
  3147          // SCAN 1: Count files
  3148          while ((entity = readdir(dir)) != NULL)
  3149          {
  3150              // NOTE: We skip '.' (current dir) and '..' (parent dir) filepaths
  3151              if ((strcmp(entity->d_name, ".") != 0) && (strcmp(entity->d_name, "..") != 0)) fileCounter++;
  3152          }
  3153  
  3154          // Memory allocation for dirFileCount
  3155          files.capacity = fileCounter;
  3156          files.paths = (char **)RL_MALLOC(files.capacity*sizeof(char *));
  3157          for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char));
  3158  
  3159          closedir(dir);
  3160  
  3161          // SCAN 2: Read filepaths
  3162          // NOTE: Directory paths are also registered
  3163          ScanDirectoryFiles(dirPath, &files, NULL);
  3164  
  3165          // Security check: read files.count should match fileCounter
  3166          if (files.count != files.capacity) TRACELOG(LOG_WARNING, "FILEIO: Read files count do not match capacity allocated");
  3167      }
  3168      else TRACELOG(LOG_WARNING, "FILEIO: Failed to open requested directory");  // Maybe it's a file...
  3169  
  3170      return files;
  3171  }
  3172  
  3173  // Load directory filepaths with extension filtering and recursive directory scan
  3174  // NOTE: On recursive loading we do not pre-scan for file count, we use MAX_FILEPATH_CAPACITY
  3175  FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs)
  3176  {
  3177      FilePathList files = { 0 };
  3178  
  3179      files.capacity = MAX_FILEPATH_CAPACITY;
  3180      files.paths = (char **)RL_CALLOC(files.capacity, sizeof(char *));
  3181      for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
  3182  
  3183      // WARNING: basePath is always prepended to scanned paths
  3184      if (scanSubdirs) ScanDirectoryFilesRecursively(basePath, &files, filter);
  3185      else ScanDirectoryFiles(basePath, &files, filter);
  3186  
  3187      return files;
  3188  }
  3189  
  3190  // Unload directory filepaths
  3191  void UnloadDirectoryFiles(FilePathList files)
  3192  {
  3193      if (files.capacity > 0)
  3194      {
  3195          for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
  3196  
  3197          RL_FREE(files.paths);
  3198      }
  3199  }
  3200  
  3201  // Change working directory, returns true on success
  3202  bool ChangeDirectory(const char *dir)
  3203  {
  3204      bool result = CHDIR(dir);
  3205  
  3206      if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir);
  3207  
  3208      return (result == 0);
  3209  }
  3210  
  3211  // Check if a given path point to a file
  3212  bool IsPathFile(const char *path)
  3213  {
  3214      struct stat pathStat = { 0 };
  3215      stat(path, &pathStat);
  3216  
  3217      return S_ISREG(pathStat.st_mode);
  3218  }
  3219  
  3220  // Check if a file has been dropped into window
  3221  bool IsFileDropped(void)
  3222  {
  3223      if (CORE.Window.dropFileCount > 0) return true;
  3224      else return false;
  3225  }
  3226  
  3227  // Load dropped filepaths
  3228  FilePathList LoadDroppedFiles(void)
  3229  {
  3230      FilePathList files = { 0 };
  3231  
  3232      files.count = CORE.Window.dropFileCount;
  3233      files.paths = CORE.Window.dropFilepaths;
  3234  
  3235      return files;
  3236  }
  3237  
  3238  // Unload dropped filepaths
  3239  void UnloadDroppedFiles(FilePathList files)
  3240  {
  3241      // WARNING: files pointers are the same as internal ones
  3242  
  3243      if (files.count > 0)
  3244      {
  3245          for (unsigned int i = 0; i < files.count; i++) RL_FREE(files.paths[i]);
  3246  
  3247          RL_FREE(files.paths);
  3248  
  3249          CORE.Window.dropFileCount = 0;
  3250          CORE.Window.dropFilepaths = NULL;
  3251      }
  3252  }
  3253  
  3254  // Get file modification time (last write time)
  3255  long GetFileModTime(const char *fileName)
  3256  {
  3257      struct stat result = { 0 };
  3258  
  3259      if (stat(fileName, &result) == 0)
  3260      {
  3261          time_t mod = result.st_mtime;
  3262  
  3263          return (long)mod;
  3264      }
  3265  
  3266      return 0;
  3267  }
  3268  
  3269  // Compress data (DEFLATE algorithm)
  3270  unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize)
  3271  {
  3272      #define COMPRESSION_QUALITY_DEFLATE  8
  3273  
  3274      unsigned char *compData = NULL;
  3275  
  3276  #if defined(SUPPORT_COMPRESSION_API)
  3277      // Compress data and generate a valid DEFLATE stream
  3278      struct sdefl sdefl = { 0 };
  3279      int bounds = sdefl_bound(dataSize);
  3280      compData = (unsigned char *)RL_CALLOC(bounds, 1);
  3281      *compDataSize = sdeflate(&sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE);   // Compression level 8, same as stbwi
  3282  
  3283      TraceLog(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize);
  3284  #endif
  3285  
  3286      return compData;
  3287  }
  3288  
  3289  // Decompress data (DEFLATE algorithm)
  3290  unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize)
  3291  {
  3292      unsigned char *data = NULL;
  3293  
  3294  #if defined(SUPPORT_COMPRESSION_API)
  3295      // Decompress data from a valid DEFLATE stream
  3296      data = RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
  3297      int length = sinflate(data, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize);
  3298  
  3299      // WARNING: RL_REALLOC can make (and leave) data copies in memory, be careful with sensitive compressed data!
  3300      // TODO: Use a different approach, create another buffer, copy data manually to it and wipe original buffer memory
  3301      unsigned char *temp = RL_REALLOC(data, length);
  3302  
  3303      if (temp != NULL) data = temp;
  3304      else TRACELOG(LOG_WARNING, "SYSTEM: Failed to re-allocate required decompression memory");
  3305  
  3306      *dataSize = length;
  3307  
  3308      TraceLog(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize);
  3309  #endif
  3310  
  3311      return data;
  3312  }
  3313  
  3314  // Encode data to Base64 string
  3315  char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
  3316  {
  3317      static const unsigned char base64encodeTable[] = {
  3318          'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
  3319          'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  3320          'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
  3321      };
  3322  
  3323      static const int modTable[] = { 0, 2, 1 };
  3324  
  3325      *outputSize = 4*((dataSize + 2)/3);
  3326  
  3327      char *encodedData = RL_MALLOC(*outputSize);
  3328  
  3329      if (encodedData == NULL) return NULL;
  3330  
  3331      for (int i = 0, j = 0; i < dataSize;)
  3332      {
  3333          unsigned int octetA = (i < dataSize)? (unsigned char)data[i++] : 0;
  3334          unsigned int octetB = (i < dataSize)? (unsigned char)data[i++] : 0;
  3335          unsigned int octetC = (i < dataSize)? (unsigned char)data[i++] : 0;
  3336  
  3337          unsigned int triple = (octetA << 0x10) + (octetB << 0x08) + octetC;
  3338  
  3339          encodedData[j++] = base64encodeTable[(triple >> 3*6) & 0x3F];
  3340          encodedData[j++] = base64encodeTable[(triple >> 2*6) & 0x3F];
  3341          encodedData[j++] = base64encodeTable[(triple >> 1*6) & 0x3F];
  3342          encodedData[j++] = base64encodeTable[(triple >> 0*6) & 0x3F];
  3343      }
  3344  
  3345      for (int i = 0; i < modTable[dataSize%3]; i++) encodedData[*outputSize - 1 - i] = '=';  // Padding character
  3346  
  3347      return encodedData;
  3348  }
  3349  
  3350  // Decode Base64 string data
  3351  unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize)
  3352  {
  3353      static const unsigned char base64decodeTable[] = {
  3354          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  3355          0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
  3356          11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
  3357          37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
  3358      };
  3359  
  3360      // Get output size of Base64 input data
  3361      int outSize = 0;
  3362      for (int i = 0; data[4*i] != 0; i++)
  3363      {
  3364          if (data[4*i + 3] == '=')
  3365          {
  3366              if (data[4*i + 2] == '=') outSize += 1;
  3367              else outSize += 2;
  3368          }
  3369          else outSize += 3;
  3370      }
  3371  
  3372      // Allocate memory to store decoded Base64 data
  3373      unsigned char *decodedData = (unsigned char *)RL_MALLOC(outSize);
  3374  
  3375      for (int i = 0; i < outSize/3; i++)
  3376      {
  3377          unsigned char a = base64decodeTable[(int)data[4*i]];
  3378          unsigned char b = base64decodeTable[(int)data[4*i + 1]];
  3379          unsigned char c = base64decodeTable[(int)data[4*i + 2]];
  3380          unsigned char d = base64decodeTable[(int)data[4*i + 3]];
  3381  
  3382          decodedData[3*i] = (a << 2) | (b >> 4);
  3383          decodedData[3*i + 1] = (b << 4) | (c >> 2);
  3384          decodedData[3*i + 2] = (c << 6) | d;
  3385      }
  3386  
  3387      if (outSize%3 == 1)
  3388      {
  3389          int n = outSize/3;
  3390          unsigned char a = base64decodeTable[(int)data[4*n]];
  3391          unsigned char b = base64decodeTable[(int)data[4*n + 1]];
  3392          decodedData[outSize - 1] = (a << 2) | (b >> 4);
  3393      }
  3394      else if (outSize%3 == 2)
  3395      {
  3396          int n = outSize/3;
  3397          unsigned char a = base64decodeTable[(int)data[4*n]];
  3398          unsigned char b = base64decodeTable[(int)data[4*n + 1]];
  3399          unsigned char c = base64decodeTable[(int)data[4*n + 2]];
  3400          decodedData[outSize - 2] = (a << 2) | (b >> 4);
  3401          decodedData[outSize - 1] = (b << 4) | (c >> 2);
  3402      }
  3403  
  3404      *outputSize = outSize;
  3405      return decodedData;
  3406  }
  3407  
  3408  // Open URL with default system browser (if available)
  3409  // NOTE: This function is only safe to use if you control the URL given.
  3410  // A user could craft a malicious string performing another action.
  3411  // Only call this function yourself not with user input or make sure to check the string yourself.
  3412  // Ref: https://github.com/raysan5/raylib/issues/686
  3413  void OpenURL(const char *url)
  3414  {
  3415      // Small security check trying to avoid (partially) malicious code...
  3416      // sorry for the inconvenience when you hit this point...
  3417      if (strchr(url, '\'') != NULL)
  3418      {
  3419          TRACELOG(LOG_WARNING, "SYSTEM: Provided URL is not valid");
  3420      }
  3421      else
  3422      {
  3423  #if defined(PLATFORM_DESKTOP)
  3424          char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
  3425      #if defined(_WIN32)
  3426          sprintf(cmd, "explorer \"%s\"", url);
  3427      #endif
  3428      #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
  3429          sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
  3430      #endif
  3431      #if defined(__APPLE__)
  3432          sprintf(cmd, "open '%s'", url);
  3433      #endif
  3434          int result = system(cmd);
  3435          if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
  3436          RL_FREE(cmd);
  3437  #endif
  3438  #if defined(PLATFORM_WEB)
  3439          emscripten_run_script(TextFormat("window.open('%s', '_blank')", url));
  3440  #endif
  3441  #if defined(PLATFORM_ANDROID)
  3442          JNIEnv *env = NULL;
  3443          JavaVM *vm = CORE.Android.app->activity->vm;
  3444          (*vm)->AttachCurrentThread(vm, &env, NULL);
  3445  
  3446          jstring urlString = (*env)->NewStringUTF(env, url);
  3447          jclass uriClass = (*env)->FindClass(env, "android/net/Uri");
  3448          jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
  3449          jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString);
  3450  
  3451          jclass intentClass = (*env)->FindClass(env, "android/content/Intent");
  3452          jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;");
  3453          jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId);
  3454          jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "<init>", "(Ljava/lang/String;Landroid/net/Uri;)V");
  3455          jobject intent = (*env)->AllocObject(env, intentClass);
  3456  
  3457          (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri);
  3458          jclass activityClass = (*env)->FindClass(env, "android/app/Activity");
  3459          jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V");
  3460          (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent);
  3461  
  3462          (*vm)->DetachCurrentThread(vm);
  3463  #endif
  3464      }
  3465  }
  3466  
  3467  //----------------------------------------------------------------------------------
  3468  // Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions
  3469  //----------------------------------------------------------------------------------
  3470  // Check if a key has been pressed once
  3471  bool IsKeyPressed(int key)
  3472  {
  3473      bool pressed = false;
  3474  
  3475      if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true;
  3476  
  3477      return pressed;
  3478  }
  3479  
  3480  // Check if a key is being pressed (key held down)
  3481  bool IsKeyDown(int key)
  3482  {
  3483      if (CORE.Input.Keyboard.currentKeyState[key] == 1) return true;
  3484      else return false;
  3485  }
  3486  
  3487  // Check if a key has been released once
  3488  bool IsKeyReleased(int key)
  3489  {
  3490      bool released = false;
  3491  
  3492      if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true;
  3493  
  3494      return released;
  3495  }
  3496  
  3497  // Check if a key is NOT being pressed (key not held down)
  3498  bool IsKeyUp(int key)
  3499  {
  3500      if (CORE.Input.Keyboard.currentKeyState[key] == 0) return true;
  3501      else return false;
  3502  }
  3503  
  3504  // Get the last key pressed
  3505  int GetKeyPressed(void)
  3506  {
  3507      int value = 0;
  3508  
  3509      if (CORE.Input.Keyboard.keyPressedQueueCount > 0)
  3510      {
  3511          // Get character from the queue head
  3512          value = CORE.Input.Keyboard.keyPressedQueue[0];
  3513  
  3514          // Shift elements 1 step toward the head.
  3515          for (int i = 0; i < (CORE.Input.Keyboard.keyPressedQueueCount - 1); i++)
  3516              CORE.Input.Keyboard.keyPressedQueue[i] = CORE.Input.Keyboard.keyPressedQueue[i + 1];
  3517  
  3518          // Reset last character in the queue
  3519          CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 0;
  3520          CORE.Input.Keyboard.keyPressedQueueCount--;
  3521      }
  3522  
  3523      return value;
  3524  }
  3525  
  3526  // Get the last char pressed
  3527  int GetCharPressed(void)
  3528  {
  3529      int value = 0;
  3530  
  3531      if (CORE.Input.Keyboard.charPressedQueueCount > 0)
  3532      {
  3533          // Get character from the queue head
  3534          value = CORE.Input.Keyboard.charPressedQueue[0];
  3535  
  3536          // Shift elements 1 step toward the head.
  3537          for (int i = 0; i < (CORE.Input.Keyboard.charPressedQueueCount - 1); i++)
  3538              CORE.Input.Keyboard.charPressedQueue[i] = CORE.Input.Keyboard.charPressedQueue[i + 1];
  3539  
  3540          // Reset last character in the queue
  3541          CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = 0;
  3542          CORE.Input.Keyboard.charPressedQueueCount--;
  3543      }
  3544  
  3545      return value;
  3546  }
  3547  
  3548  // Set a custom key to exit program
  3549  // NOTE: default exitKey is ESCAPE
  3550  void SetExitKey(int key)
  3551  {
  3552  #if !defined(PLATFORM_ANDROID)
  3553      CORE.Input.Keyboard.exitKey = key;
  3554  #endif
  3555  }
  3556  
  3557  // NOTE: Gamepad support not implemented in emscripten GLFW3 (PLATFORM_WEB)
  3558  
  3559  // Check if a gamepad is available
  3560  bool IsGamepadAvailable(int gamepad)
  3561  {
  3562      bool result = false;
  3563  
  3564      if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad]) result = true;
  3565  
  3566      return result;
  3567  }
  3568  
  3569  // Get gamepad internal name id
  3570  const char *GetGamepadName(int gamepad)
  3571  {
  3572  #if defined(PLATFORM_DESKTOP)
  3573      if (CORE.Input.Gamepad.ready[gamepad]) return glfwGetJoystickName(gamepad);
  3574      else return NULL;
  3575  #endif
  3576  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  3577      if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]);
  3578      return CORE.Input.Gamepad.name[gamepad];
  3579  #endif
  3580  #if defined(PLATFORM_WEB)
  3581      return CORE.Input.Gamepad.name[gamepad];
  3582  #endif
  3583      return NULL;
  3584  }
  3585  
  3586  // Get gamepad axis count
  3587  int GetGamepadAxisCount(int gamepad)
  3588  {
  3589  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  3590      int axisCount = 0;
  3591      if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount);
  3592      CORE.Input.Gamepad.axisCount = axisCount;
  3593  #endif
  3594  
  3595      return CORE.Input.Gamepad.axisCount;
  3596  }
  3597  
  3598  // Get axis movement vector for a gamepad
  3599  float GetGamepadAxisMovement(int gamepad, int axis)
  3600  {
  3601      float value = 0;
  3602  
  3603      if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) &&
  3604          (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis];      // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA
  3605  
  3606      return value;
  3607  }
  3608  
  3609  // Check if a gamepad button has been pressed once
  3610  bool IsGamepadButtonPressed(int gamepad, int button)
  3611  {
  3612      bool pressed = false;
  3613  
  3614      if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
  3615          (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 0) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) pressed = true;
  3616  
  3617      return pressed;
  3618  }
  3619  
  3620  // Check if a gamepad button is being pressed
  3621  bool IsGamepadButtonDown(int gamepad, int button)
  3622  {
  3623      bool result = false;
  3624  
  3625      if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
  3626          (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) result = true;
  3627  
  3628      return result;
  3629  }
  3630  
  3631  // Check if a gamepad button has NOT been pressed once
  3632  bool IsGamepadButtonReleased(int gamepad, int button)
  3633  {
  3634      bool released = false;
  3635  
  3636      if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
  3637          (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 1) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) released = true;
  3638  
  3639      return released;
  3640  }
  3641  
  3642  // Check if a gamepad button is NOT being pressed
  3643  bool IsGamepadButtonUp(int gamepad, int button)
  3644  {
  3645      bool result = false;
  3646  
  3647      if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
  3648          (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) result = true;
  3649  
  3650      return result;
  3651  }
  3652  
  3653  // Get the last gamepad button pressed
  3654  int GetGamepadButtonPressed(void)
  3655  {
  3656      return CORE.Input.Gamepad.lastButtonPressed;
  3657  }
  3658  
  3659  // Set internal gamepad mappings
  3660  int SetGamepadMappings(const char *mappings)
  3661  {
  3662      int result = 0;
  3663  
  3664  #if defined(PLATFORM_DESKTOP)
  3665      result = glfwUpdateGamepadMappings(mappings);
  3666  #endif
  3667  
  3668      return result;
  3669  }
  3670  
  3671  // Check if a mouse button has been pressed once
  3672  bool IsMouseButtonPressed(int button)
  3673  {
  3674      bool pressed = false;
  3675  
  3676      if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) pressed = true;
  3677  
  3678      // Map touches to mouse buttons checking
  3679      if ((CORE.Input.Touch.currentTouchState[button] == 1) && (CORE.Input.Touch.previousTouchState[button] == 0)) pressed = true;
  3680  
  3681      return pressed;
  3682  }
  3683  
  3684  // Check if a mouse button is being pressed
  3685  bool IsMouseButtonDown(int button)
  3686  {
  3687      bool down = false;
  3688  
  3689      if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true;
  3690  
  3691      // Map touches to mouse buttons checking
  3692      if (CORE.Input.Touch.currentTouchState[button] == 1) down = true;
  3693  
  3694      return down;
  3695  }
  3696  
  3697  // Check if a mouse button has been released once
  3698  bool IsMouseButtonReleased(int button)
  3699  {
  3700      bool released = false;
  3701  
  3702      if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) released = true;
  3703  
  3704      // Map touches to mouse buttons checking
  3705      if ((CORE.Input.Touch.currentTouchState[button] == 0) && (CORE.Input.Touch.previousTouchState[button] == 1)) released = true;
  3706  
  3707      return released;
  3708  }
  3709  
  3710  // Check if a mouse button is NOT being pressed
  3711  bool IsMouseButtonUp(int button)
  3712  {
  3713      return !IsMouseButtonDown(button);
  3714  }
  3715  
  3716  // Get mouse position X
  3717  int GetMouseX(void)
  3718  {
  3719  #if defined(PLATFORM_ANDROID)
  3720      return (int)CORE.Input.Touch.position[0].x;
  3721  #else
  3722      return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
  3723  #endif
  3724  }
  3725  
  3726  // Get mouse position Y
  3727  int GetMouseY(void)
  3728  {
  3729  #if defined(PLATFORM_ANDROID)
  3730      return (int)CORE.Input.Touch.position[0].y;
  3731  #else
  3732      return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
  3733  #endif
  3734  }
  3735  
  3736  // Get mouse position XY
  3737  Vector2 GetMousePosition(void)
  3738  {
  3739      Vector2 position = { 0 };
  3740  
  3741  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
  3742      position = GetTouchPosition(0);
  3743  #else
  3744      position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x;
  3745      position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y;
  3746  #endif
  3747  
  3748      return position;
  3749  }
  3750  
  3751  // Get mouse delta between frames
  3752  Vector2 GetMouseDelta(void)
  3753  {
  3754      Vector2 delta = {0};
  3755  
  3756      delta.x = CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x;
  3757      delta.y = CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y;
  3758  
  3759      return delta;
  3760  }
  3761  
  3762  // Set mouse position XY
  3763  void SetMousePosition(int x, int y)
  3764  {
  3765      CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
  3766  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  3767      // NOTE: emscripten not implemented
  3768      glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
  3769  #endif
  3770  }
  3771  
  3772  // Set mouse offset
  3773  // NOTE: Useful when rendering to different size targets
  3774  void SetMouseOffset(int offsetX, int offsetY)
  3775  {
  3776      CORE.Input.Mouse.offset = (Vector2){ (float)offsetX, (float)offsetY };
  3777  }
  3778  
  3779  // Set mouse scaling
  3780  // NOTE: Useful when rendering to different size targets
  3781  void SetMouseScale(float scaleX, float scaleY)
  3782  {
  3783      CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY };
  3784  }
  3785  
  3786  // Get mouse wheel movement Y
  3787  float GetMouseWheelMove(void)
  3788  {
  3789      float result = 0.0f;
  3790  
  3791  #if !defined(PLATFORM_ANDROID)
  3792      if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x;
  3793      else result = (float)CORE.Input.Mouse.currentWheelMove.y;
  3794  #endif
  3795  
  3796      return result;
  3797  }
  3798  
  3799  // Get mouse wheel movement X/Y as a vector
  3800  Vector2 GetMouseWheelMoveV(void)
  3801  {
  3802      Vector2 result = { 0 };
  3803  
  3804      result = CORE.Input.Mouse.currentWheelMove;
  3805  
  3806      return result;
  3807  }
  3808  
  3809  // Set mouse cursor
  3810  // NOTE: This is a no-op on platforms other than PLATFORM_DESKTOP
  3811  void SetMouseCursor(int cursor)
  3812  {
  3813  #if defined(PLATFORM_DESKTOP)
  3814      CORE.Input.Mouse.cursor = cursor;
  3815      if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL);
  3816      else
  3817      {
  3818          // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values
  3819          glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor));
  3820      }
  3821  #endif
  3822  }
  3823  
  3824  // Get touch position X for touch point 0 (relative to screen size)
  3825  int GetTouchX(void)
  3826  {
  3827  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
  3828      return (int)CORE.Input.Touch.position[0].x;
  3829  #else   // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM
  3830      return GetMouseX();
  3831  #endif
  3832  }
  3833  
  3834  // Get touch position Y for touch point 0 (relative to screen size)
  3835  int GetTouchY(void)
  3836  {
  3837  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
  3838      return (int)CORE.Input.Touch.position[0].y;
  3839  #else   // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM
  3840      return GetMouseY();
  3841  #endif
  3842  }
  3843  
  3844  // Get touch position XY for a touch point index (relative to screen size)
  3845  // TODO: Touch position should be scaled depending on display size and render size
  3846  Vector2 GetTouchPosition(int index)
  3847  {
  3848      Vector2 position = { -1.0f, -1.0f };
  3849  
  3850  #if defined(PLATFORM_DESKTOP)
  3851      // TODO: GLFW does not support multi-touch input just yet
  3852      // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
  3853      // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
  3854      if (index == 0) position = GetMousePosition();
  3855  #endif
  3856  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  3857      if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
  3858      else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
  3859  #endif
  3860  
  3861      return position;
  3862  }
  3863  
  3864  // Get touch point identifier for given index
  3865  int GetTouchPointId(int index)
  3866  {
  3867      int id = -1;
  3868  
  3869      if (index < MAX_TOUCH_POINTS) id = CORE.Input.Touch.pointId[index];
  3870  
  3871      return id;
  3872  }
  3873  
  3874  // Get number of touch points
  3875  int GetTouchPointCount(void)
  3876  {
  3877      return CORE.Input.Touch.pointCount;
  3878  }
  3879  
  3880  //----------------------------------------------------------------------------------
  3881  // Module specific Functions Definition
  3882  //----------------------------------------------------------------------------------
  3883  
  3884  // Initialize display device and framebuffer
  3885  // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
  3886  // If width or height are 0, default display size will be used for framebuffer size
  3887  // NOTE: returns false in case graphic device could not be created
  3888  static bool InitGraphicsDevice(int width, int height)
  3889  {
  3890      CORE.Window.screen.width = width;            // User desired width
  3891      CORE.Window.screen.height = height;          // User desired height
  3892      CORE.Window.screenScale = MatrixIdentity();  // No draw scaling required by default
  3893  
  3894      // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
  3895      // ...in top-down or left-right to match display aspect ratio (no weird scalings)
  3896  
  3897  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  3898      glfwSetErrorCallback(ErrorCallback);
  3899  /*
  3900      // TODO: Setup GLFW custom allocators to match raylib ones
  3901      const GLFWallocator allocator = {
  3902          .allocate = MemAlloc,
  3903          .deallocate = MemFree,
  3904          .reallocate = MemRealloc,
  3905          .user = NULL
  3906      };
  3907  
  3908      glfwInitAllocator(&allocator);
  3909  */
  3910  #if defined(__APPLE__)
  3911      glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
  3912  #endif
  3913  
  3914      if (!glfwInit())
  3915      {
  3916          TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW");
  3917          return false;
  3918      }
  3919  
  3920      glfwDefaultWindowHints();                       // Set default windows hints
  3921      //glfwWindowHint(GLFW_RED_BITS, 8);             // Framebuffer red color component bits
  3922      //glfwWindowHint(GLFW_GREEN_BITS, 8);           // Framebuffer green color component bits
  3923      //glfwWindowHint(GLFW_BLUE_BITS, 8);            // Framebuffer blue color component bits
  3924      //glfwWindowHint(GLFW_ALPHA_BITS, 8);           // Framebuffer alpha color component bits
  3925      //glfwWindowHint(GLFW_DEPTH_BITS, 24);          // Depthbuffer bits
  3926      //glfwWindowHint(GLFW_REFRESH_RATE, 0);         // Refresh rate for fullscreen window
  3927      //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
  3928      //glfwWindowHint(GLFW_AUX_BUFFERS, 0);          // Number of auxiliar buffers
  3929  
  3930      // Check window creation flags
  3931      if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
  3932  
  3933      if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
  3934      else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);     // Window initially hidden
  3935  
  3936      if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
  3937      else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE);   // Decorated window
  3938  
  3939      if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
  3940      else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);  // Avoid window being resizable
  3941  
  3942      // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
  3943      if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
  3944  
  3945      // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
  3946      if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
  3947  
  3948      if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
  3949      else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
  3950  
  3951      if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
  3952      else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
  3953  
  3954      // NOTE: Some GLFW flags are not supported on HTML5
  3955  #if defined(PLATFORM_DESKTOP)
  3956      if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);     // Transparent framebuffer
  3957      else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE);  // Opaque framebuffer
  3958  
  3959      if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
  3960      {
  3961          // Resize window content area based on the monitor content scale.
  3962          // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11.
  3963          // On platforms like macOS the resolution of the framebuffer is changed independently of the window size.
  3964          glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);   // Scale content area based on the monitor content scale where window is placed on
  3965      #if defined(__APPLE__)
  3966          glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
  3967      #endif
  3968      }
  3969      else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
  3970  
  3971      // Mouse passthrough
  3972      if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
  3973      else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
  3974  #endif
  3975  
  3976      if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
  3977      {
  3978          // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
  3979          TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
  3980          glfwWindowHint(GLFW_SAMPLES, 4);   // Tries to enable multisampling x4 (MSAA), default is 0
  3981      }
  3982  
  3983      // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version
  3984      // with backward compatibility to older OpenGL versions.
  3985      // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context.
  3986  
  3987      // Check selection OpenGL version
  3988      if (rlGetVersion() == RL_OPENGL_21)
  3989      {
  3990          glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);          // Choose OpenGL major version (just hint)
  3991          glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);          // Choose OpenGL minor version (just hint)
  3992      }
  3993      else if (rlGetVersion() == RL_OPENGL_33)
  3994      {
  3995          glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);          // Choose OpenGL major version (just hint)
  3996          glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);          // Choose OpenGL minor version (just hint)
  3997          glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
  3998                                                                         // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
  3999  #if defined(__APPLE__)
  4000          glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);  // OSX Requires fordward compatibility
  4001  #else
  4002          glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Fordward Compatibility Hint: Only 3.3 and above!
  4003  #endif
  4004          //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
  4005      }
  4006      else if (rlGetVersion() == RL_OPENGL_43)
  4007      {
  4008          glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);          // Choose OpenGL major version (just hint)
  4009          glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);          // Choose OpenGL minor version (just hint)
  4010          glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  4011          glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
  4012  #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
  4013          glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);   // Enable OpenGL Debug Context
  4014  #endif
  4015      }
  4016      else if (rlGetVersion() == RL_OPENGL_ES_20)                    // Request OpenGL ES 2.0 context
  4017      {
  4018          glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  4019          glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  4020          glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
  4021  #if defined(PLATFORM_DESKTOP)
  4022          glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
  4023  #else
  4024          glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
  4025  #endif
  4026      }
  4027  
  4028  #if defined(PLATFORM_DESKTOP)
  4029      // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions.
  4030      // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn.
  4031      // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience.
  4032      // REF: https://github.com/raysan5/raylib/issues/1554
  4033      if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL);
  4034  #endif
  4035  
  4036  #if defined(PLATFORM_DESKTOP)
  4037      // Find monitor resolution
  4038      GLFWmonitor *monitor = glfwGetPrimaryMonitor();
  4039      if (!monitor)
  4040      {
  4041          TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
  4042          return false;
  4043      }
  4044  
  4045      const GLFWvidmode *mode = glfwGetVideoMode(monitor);
  4046  
  4047      CORE.Window.display.width = mode->width;
  4048      CORE.Window.display.height = mode->height;
  4049  
  4050      // Set screen width/height to the display width/height if they are 0
  4051      if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
  4052      if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
  4053  #endif  // PLATFORM_DESKTOP
  4054  
  4055  #if defined(PLATFORM_WEB)
  4056      // NOTE: Getting video modes is not implemented in emscripten GLFW3 version
  4057      CORE.Window.display.width = CORE.Window.screen.width;
  4058      CORE.Window.display.height = CORE.Window.screen.height;
  4059  #endif  // PLATFORM_WEB
  4060  
  4061      if (CORE.Window.fullscreen)
  4062      {
  4063          // remember center for switchinging from fullscreen to window
  4064          CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
  4065          CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
  4066  
  4067          if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
  4068          if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
  4069  
  4070          // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor
  4071          int count = 0;
  4072          const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count);
  4073  
  4074          // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height
  4075          for (int i = 0; i < count; i++)
  4076          {
  4077              if ((unsigned int)modes[i].width >= CORE.Window.screen.width)
  4078              {
  4079                  if ((unsigned int)modes[i].height >= CORE.Window.screen.height)
  4080                  {
  4081                      CORE.Window.display.width = modes[i].width;
  4082                      CORE.Window.display.height = modes[i].height;
  4083                      break;
  4084                  }
  4085              }
  4086          }
  4087          TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
  4088  
  4089          // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
  4090          // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
  4091          // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
  4092          // by the sides to fit all monitor space...
  4093  
  4094          // Try to setup the most appropiate fullscreen framebuffer for the requested screenWidth/screenHeight
  4095          // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
  4096          // 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
  4097          // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
  4098          // HighDPI monitors are properly considered in a following similar function: SetupViewport()
  4099          SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
  4100  
  4101          CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
  4102  
  4103          // NOTE: Full-screen change, not working properly...
  4104          //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
  4105      }
  4106      else
  4107      {
  4108  #if defined(PLATFORM_DESKTOP)
  4109          // If we are windowed fullscreen, ensures that window does not minimize when focus is lost
  4110          if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
  4111          {
  4112              glfwWindowHint(GLFW_AUTO_ICONIFY, 0);
  4113          }
  4114  #endif
  4115          // No-fullscreen window creation
  4116          CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
  4117  
  4118          if (CORE.Window.handle)
  4119          {
  4120              CORE.Window.render.width = CORE.Window.screen.width;
  4121              CORE.Window.render.height = CORE.Window.screen.height;
  4122          }
  4123      }
  4124  
  4125      if (!CORE.Window.handle)
  4126      {
  4127          glfwTerminate();
  4128          TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
  4129          return false;
  4130      }
  4131  
  4132      // Set window callback events
  4133      glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback);      // NOTE: Resizing not allowed by default!
  4134  #if !defined(PLATFORM_WEB)
  4135      glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback);
  4136  #endif
  4137      glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback);
  4138      glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback);
  4139      glfwSetDropCallback(CORE.Window.handle, WindowDropCallback);
  4140  
  4141      // Set input callback events
  4142      glfwSetKeyCallback(CORE.Window.handle, KeyCallback);
  4143      glfwSetCharCallback(CORE.Window.handle, CharCallback);
  4144      glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback);
  4145      glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback);   // Track mouse position changes
  4146      glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback);
  4147      glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback);
  4148  
  4149      glfwMakeContextCurrent(CORE.Window.handle);
  4150  
  4151  #if !defined(PLATFORM_WEB)
  4152      glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE);    // Enable lock keys modifiers (CAPS, NUM)
  4153  
  4154      glfwSwapInterval(0);        // No V-Sync by default
  4155  #endif
  4156  
  4157      // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
  4158      // NOTE: V-Sync can be enabled by graphic driver configuration
  4159      if (CORE.Window.flags & FLAG_VSYNC_HINT)
  4160      {
  4161          // WARNING: It seems to hits a critical render path in Intel HD Graphics
  4162          glfwSwapInterval(1);
  4163          TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC");
  4164      }
  4165  
  4166      int fbWidth = CORE.Window.screen.width;
  4167      int fbHeight = CORE.Window.screen.height;
  4168  
  4169  #if defined(PLATFORM_DESKTOP)
  4170      if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
  4171      {
  4172          // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling
  4173          // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
  4174      #if !defined(__APPLE__)
  4175          glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight);
  4176  
  4177          // Screen scaling matrix is required in case desired screen area is different than display area
  4178          CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
  4179  
  4180          // Mouse input scaling for the new screen size
  4181          SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight);
  4182      #endif
  4183      }
  4184  #endif
  4185  
  4186      CORE.Window.render.width = fbWidth;
  4187      CORE.Window.render.height = fbHeight;
  4188      CORE.Window.currentFbo.width = fbWidth;
  4189      CORE.Window.currentFbo.height = fbHeight;
  4190  
  4191      TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
  4192      TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
  4193      TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
  4194      TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
  4195      TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
  4196  
  4197  #endif  // PLATFORM_DESKTOP || PLATFORM_WEB
  4198  
  4199  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  4200      CORE.Window.fullscreen = true;
  4201      CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
  4202  
  4203  #if defined(PLATFORM_RPI)
  4204      bcm_host_init();
  4205  
  4206      DISPMANX_ELEMENT_HANDLE_T dispmanElement = { 0 };
  4207      DISPMANX_DISPLAY_HANDLE_T dispmanDisplay = { 0 };
  4208      DISPMANX_UPDATE_HANDLE_T dispmanUpdate = { 0 };
  4209  
  4210      VC_RECT_T dstRect = { 0 };
  4211      VC_RECT_T srcRect = { 0 };
  4212  #endif
  4213  
  4214  #if defined(PLATFORM_DRM)
  4215      CORE.Window.fd = -1;
  4216      CORE.Window.connector = NULL;
  4217      CORE.Window.modeIndex = -1;
  4218      CORE.Window.crtc = NULL;
  4219      CORE.Window.gbmDevice = NULL;
  4220      CORE.Window.gbmSurface = NULL;
  4221      CORE.Window.prevBO = NULL;
  4222      CORE.Window.prevFB = 0;
  4223  
  4224  #if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
  4225      CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
  4226  #else
  4227      TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card");
  4228      CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card",  O_RDWR); // VideoCore VI (Raspberry Pi 4)
  4229  
  4230      if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL))
  4231      {
  4232          TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1");
  4233          CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded
  4234      }
  4235  
  4236      if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL))
  4237      {
  4238          TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0");
  4239          CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
  4240      }
  4241  #endif
  4242      if (-1 == CORE.Window.fd)
  4243      {
  4244          TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card");
  4245          return false;
  4246      }
  4247  
  4248      drmModeRes *res = drmModeGetResources(CORE.Window.fd);
  4249      if (!res)
  4250      {
  4251          TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources");
  4252          return false;
  4253      }
  4254  
  4255      TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors);
  4256      for (size_t i = 0; i < res->count_connectors; i++)
  4257      {
  4258          TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i);
  4259          drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]);
  4260          TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes);
  4261          if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id))
  4262          {
  4263              TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected");
  4264              CORE.Window.connector = con;
  4265              break;
  4266          }
  4267          else
  4268          {
  4269              TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)");
  4270              drmModeFreeConnector(con);
  4271          }
  4272      }
  4273  
  4274      if (!CORE.Window.connector)
  4275      {
  4276          TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found");
  4277          drmModeFreeResources(res);
  4278          return false;
  4279      }
  4280  
  4281      drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id);
  4282      if (!enc)
  4283      {
  4284          TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder");
  4285          drmModeFreeResources(res);
  4286          return false;
  4287      }
  4288  
  4289      CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id);
  4290      if (!CORE.Window.crtc)
  4291      {
  4292          TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc");
  4293          drmModeFreeEncoder(enc);
  4294          drmModeFreeResources(res);
  4295          return false;
  4296      }
  4297  
  4298      // If InitWindow should use the current mode find it in the connector's mode list
  4299      if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0))
  4300      {
  4301          TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode...");
  4302  
  4303          CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode);
  4304  
  4305          if (CORE.Window.modeIndex < 0)
  4306          {
  4307              TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found");
  4308              drmModeFreeEncoder(enc);
  4309              drmModeFreeResources(res);
  4310              return false;
  4311          }
  4312  
  4313          CORE.Window.screen.width = CORE.Window.display.width;
  4314          CORE.Window.screen.height = CORE.Window.display.height;
  4315      }
  4316  
  4317      const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT;
  4318      const int fps = (CORE.Time.target > 0) ? (1.0/CORE.Time.target) : 60;
  4319  
  4320      // Try to find an exact matching mode
  4321      CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
  4322  
  4323      // If nothing found, try to find a nearly matching mode
  4324      if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
  4325  
  4326      // If nothing found, try to find an exactly matching mode including interlaced
  4327      if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
  4328  
  4329      // If nothing found, try to find a nearly matching mode including interlaced
  4330      if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
  4331  
  4332      // If nothing found, there is no suitable mode
  4333      if (CORE.Window.modeIndex < 0)
  4334      {
  4335          TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode");
  4336          drmModeFreeEncoder(enc);
  4337          drmModeFreeResources(res);
  4338          return false;
  4339      }
  4340  
  4341      CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay;
  4342      CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay;
  4343  
  4344      TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name,
  4345          CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay,
  4346          (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p',
  4347          CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh);
  4348  
  4349      // Use the width and height of the surface for render
  4350      CORE.Window.render.width = CORE.Window.screen.width;
  4351      CORE.Window.render.height = CORE.Window.screen.height;
  4352  
  4353      drmModeFreeEncoder(enc);
  4354      enc = NULL;
  4355  
  4356      drmModeFreeResources(res);
  4357      res = NULL;
  4358  
  4359      CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd);
  4360      if (!CORE.Window.gbmDevice)
  4361      {
  4362          TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device");
  4363          return false;
  4364      }
  4365  
  4366      CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay,
  4367          CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
  4368      if (!CORE.Window.gbmSurface)
  4369      {
  4370          TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface");
  4371          return false;
  4372      }
  4373  #endif
  4374  
  4375      EGLint samples = 0;
  4376      EGLint sampleBuffer = 0;
  4377      if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
  4378      {
  4379          samples = 4;
  4380          sampleBuffer = 1;
  4381          TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
  4382      }
  4383  
  4384      const EGLint framebufferAttribs[] =
  4385      {
  4386          EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,     // Type of context support -> Required on RPI?
  4387  #if defined(PLATFORM_DRM)
  4388          EGL_SURFACE_TYPE, EGL_WINDOW_BIT,          // Don't use it on Android!
  4389  #endif
  4390          EGL_RED_SIZE, 8,            // RED color bit depth (alternative: 5)
  4391          EGL_GREEN_SIZE, 8,          // GREEN color bit depth (alternative: 6)
  4392          EGL_BLUE_SIZE, 8,           // BLUE color bit depth (alternative: 5)
  4393  #if defined(PLATFORM_DRM)
  4394          EGL_ALPHA_SIZE, 8,        // ALPHA bit depth (required for transparent framebuffer)
  4395  #endif
  4396          //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
  4397          EGL_DEPTH_SIZE, 16,         // Depth buffer size (Required to use Depth testing!)
  4398          //EGL_STENCIL_SIZE, 8,      // Stencil buffer size
  4399          EGL_SAMPLE_BUFFERS, sampleBuffer,    // Activate MSAA
  4400          EGL_SAMPLES, samples,       // 4x Antialiasing if activated (Free on MALI GPUs)
  4401          EGL_NONE
  4402      };
  4403  
  4404      const EGLint contextAttribs[] =
  4405      {
  4406          EGL_CONTEXT_CLIENT_VERSION, 2,
  4407          EGL_NONE
  4408      };
  4409  
  4410  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  4411      EGLint numConfigs = 0;
  4412  
  4413      // Get an EGL device connection
  4414  #if defined(PLATFORM_DRM)
  4415      CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice);
  4416  #else
  4417      CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  4418  #endif
  4419      if (CORE.Window.device == EGL_NO_DISPLAY)
  4420      {
  4421          TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
  4422          return false;
  4423      }
  4424  
  4425      // Initialize the EGL device connection
  4426      if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE)
  4427      {
  4428          // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
  4429          TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
  4430          return false;
  4431      }
  4432  
  4433  #if defined(PLATFORM_DRM)
  4434      if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs))
  4435      {
  4436          TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
  4437          return false;
  4438      }
  4439  
  4440      TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs);
  4441  
  4442      EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs));
  4443      if (!configs)
  4444      {
  4445          TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
  4446          return false;
  4447      }
  4448  
  4449      EGLint matchingNumConfigs = 0;
  4450      if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
  4451      {
  4452          TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
  4453          free(configs);
  4454          return false;
  4455      }
  4456  
  4457      TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs);
  4458  
  4459      // find the EGL config that matches the previously setup GBM format
  4460      int found = 0;
  4461      for (EGLint i = 0; i < matchingNumConfigs; ++i)
  4462      {
  4463          EGLint id = 0;
  4464          if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
  4465          {
  4466              TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError());
  4467              continue;
  4468          }
  4469  
  4470          if (GBM_FORMAT_ARGB8888 == id)
  4471          {
  4472              TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i);
  4473              CORE.Window.config = configs[i];
  4474              found = 1;
  4475              break;
  4476          }
  4477      }
  4478  
  4479      RL_FREE(configs);
  4480  
  4481      if (!found)
  4482      {
  4483          TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config");
  4484          return false;
  4485      }
  4486  #else
  4487      // Get an appropriate EGL framebuffer configuration
  4488      eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs);
  4489  #endif
  4490  
  4491      // Set rendering API
  4492      eglBindAPI(EGL_OPENGL_ES_API);
  4493  
  4494      // Create an EGL rendering context
  4495      CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs);
  4496      if (CORE.Window.context == EGL_NO_CONTEXT)
  4497      {
  4498          TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
  4499          return false;
  4500      }
  4501  #endif
  4502  
  4503      // Create an EGL window surface
  4504      //---------------------------------------------------------------------------------
  4505  #if defined(PLATFORM_ANDROID)
  4506      EGLint displayFormat = 0;
  4507  
  4508      // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
  4509      // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
  4510      eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
  4511  
  4512      // At this point we need to manage render size vs screen size
  4513      // NOTE: This function use and modify global module variables:
  4514      //  -> CORE.Window.screen.width/CORE.Window.screen.height
  4515      //  -> CORE.Window.render.width/CORE.Window.render.height
  4516      //  -> CORE.Window.screenScale
  4517      SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
  4518  
  4519      ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
  4520      //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat);       // Force use of native display size
  4521  
  4522      CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL);
  4523  #endif  // PLATFORM_ANDROID
  4524  
  4525  #if defined(PLATFORM_RPI)
  4526      graphics_get_display_size(0, &CORE.Window.display.width, &CORE.Window.display.height);
  4527  
  4528      // Screen size security check
  4529      if (CORE.Window.screen.width <= 0) CORE.Window.screen.width = CORE.Window.display.width;
  4530      if (CORE.Window.screen.height <= 0) CORE.Window.screen.height = CORE.Window.display.height;
  4531  
  4532      // At this point we need to manage render size vs screen size
  4533      // NOTE: This function use and modify global module variables:
  4534      //  -> CORE.Window.screen.width/CORE.Window.screen.height
  4535      //  -> CORE.Window.render.width/CORE.Window.render.height
  4536      //  -> CORE.Window.screenScale
  4537      SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
  4538  
  4539      dstRect.x = 0;
  4540      dstRect.y = 0;
  4541      dstRect.width = CORE.Window.display.width;
  4542      dstRect.height = CORE.Window.display.height;
  4543  
  4544      srcRect.x = 0;
  4545      srcRect.y = 0;
  4546      srcRect.width = CORE.Window.render.width << 16;
  4547      srcRect.height = CORE.Window.render.height << 16;
  4548  
  4549      // NOTE: RPI dispmanx windowing system takes care of source rectangle scaling to destination rectangle by hardware (no cost)
  4550      // Take care that renderWidth/renderHeight fit on displayWidth/displayHeight aspect ratio
  4551  
  4552      VC_DISPMANX_ALPHA_T alpha = { 0 };
  4553      alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
  4554      //alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE;       // TODO: Allow transparent framebuffer! -> FLAG_WINDOW_TRANSPARENT
  4555      alpha.opacity = 255;    // Set transparency level for framebuffer, requires EGLAttrib: EGL_TRANSPARENT_TYPE
  4556      alpha.mask = 0;
  4557  
  4558      dispmanDisplay = vc_dispmanx_display_open(0);   // LCD
  4559      dispmanUpdate = vc_dispmanx_update_start(0);
  4560  
  4561      dispmanElement = vc_dispmanx_element_add(dispmanUpdate, dispmanDisplay, 0/*layer*/, &dstRect, 0/*src*/,
  4562                                              &srcRect, DISPMANX_PROTECTION_NONE, &alpha, 0/*clamp*/, DISPMANX_NO_ROTATE);
  4563  
  4564      CORE.Window.handle.element = dispmanElement;
  4565      CORE.Window.handle.width = CORE.Window.render.width;
  4566      CORE.Window.handle.height = CORE.Window.render.height;
  4567      vc_dispmanx_update_submit_sync(dispmanUpdate);
  4568  
  4569      CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, &CORE.Window.handle, NULL);
  4570  
  4571      const unsigned char *const renderer = glGetString(GL_RENDERER);
  4572      if (renderer) TRACELOG(LOG_INFO, "DISPLAY: Renderer name is: %s", renderer);
  4573      else TRACELOG(LOG_WARNING, "DISPLAY: Failed to get renderer name");
  4574      //---------------------------------------------------------------------------------
  4575  #endif  // PLATFORM_RPI
  4576  
  4577  #if defined(PLATFORM_DRM)
  4578      CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL);
  4579      if (EGL_NO_SURFACE == CORE.Window.surface)
  4580      {
  4581          TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
  4582          return false;
  4583      }
  4584  
  4585      // At this point we need to manage render size vs screen size
  4586      // NOTE: This function use and modify global module variables:
  4587      //  -> CORE.Window.screen.width/CORE.Window.screen.height
  4588      //  -> CORE.Window.render.width/CORE.Window.render.height
  4589      //  -> CORE.Window.screenScale
  4590      SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
  4591  #endif  // PLATFORM_DRM
  4592  
  4593      // There must be at least one frame displayed before the buffers are swapped
  4594      //eglSwapInterval(CORE.Window.device, 1);
  4595  
  4596      if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE)
  4597      {
  4598          TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface");
  4599          return false;
  4600      }
  4601      else
  4602      {
  4603          CORE.Window.render.width = CORE.Window.screen.width;
  4604          CORE.Window.render.height = CORE.Window.screen.height;
  4605          CORE.Window.currentFbo.width = CORE.Window.render.width;
  4606          CORE.Window.currentFbo.height = CORE.Window.render.height;
  4607  
  4608          TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
  4609          TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
  4610          TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
  4611          TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
  4612          TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
  4613      }
  4614  #endif  // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM
  4615  
  4616      // Load OpenGL extensions
  4617      // NOTE: GL procedures address loader is required to load extensions
  4618  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  4619      rlLoadExtensions(glfwGetProcAddress);
  4620  #else
  4621      rlLoadExtensions(eglGetProcAddress);
  4622  #endif
  4623  
  4624      // Initialize OpenGL context (states and resources)
  4625      // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
  4626      rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
  4627  
  4628      // Setup default viewport
  4629      // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
  4630      SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
  4631  
  4632  #if defined(PLATFORM_ANDROID)
  4633      CORE.Window.ready = true;
  4634  #endif
  4635  
  4636      if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
  4637  
  4638      return true;
  4639  }
  4640  
  4641  // Set viewport for a provided width and height
  4642  static void SetupViewport(int width, int height)
  4643  {
  4644      CORE.Window.render.width = width;
  4645      CORE.Window.render.height = height;
  4646  
  4647      // Set viewport width and height
  4648      // NOTE: We consider render size (scaled) and offset in case black bars are required and
  4649      // render area does not match full display area (this situation is only applicable on fullscreen mode)
  4650  #if defined(__APPLE__)
  4651      float xScale = 1.0f, yScale = 1.0f;
  4652      glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale);
  4653      rlViewport(CORE.Window.renderOffset.x/2*xScale, CORE.Window.renderOffset.y/2*yScale, (CORE.Window.render.width)*xScale, (CORE.Window.render.height)*yScale);
  4654  #else
  4655      rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width, CORE.Window.render.height);
  4656  #endif
  4657  
  4658      rlMatrixMode(RL_PROJECTION);        // Switch to projection matrix
  4659      rlLoadIdentity();                   // Reset current matrix (projection)
  4660  
  4661      // Set orthographic projection to current framebuffer size
  4662      // NOTE: Configured top-left corner as (0, 0)
  4663      rlOrtho(0, CORE.Window.render.width, CORE.Window.render.height, 0, 0.0f, 1.0f);
  4664  
  4665      rlMatrixMode(RL_MODELVIEW);         // Switch back to modelview matrix
  4666      rlLoadIdentity();                   // Reset current matrix (modelview)
  4667  }
  4668  
  4669  // Compute framebuffer size relative to screen size and display size
  4670  // NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
  4671  static void SetupFramebuffer(int width, int height)
  4672  {
  4673      // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
  4674      if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
  4675      {
  4676          TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
  4677  
  4678          // Downscaling to fit display with border-bars
  4679          float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
  4680          float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
  4681  
  4682          if (widthRatio <= heightRatio)
  4683          {
  4684              CORE.Window.render.width = CORE.Window.display.width;
  4685              CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
  4686              CORE.Window.renderOffset.x = 0;
  4687              CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
  4688          }
  4689          else
  4690          {
  4691              CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
  4692              CORE.Window.render.height = CORE.Window.display.height;
  4693              CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
  4694              CORE.Window.renderOffset.y = 0;
  4695          }
  4696  
  4697          // Screen scaling required
  4698          float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
  4699          CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
  4700  
  4701          // NOTE: We render to full display resolution!
  4702          // We just need to calculate above parameters for downscale matrix and offsets
  4703          CORE.Window.render.width = CORE.Window.display.width;
  4704          CORE.Window.render.height = CORE.Window.display.height;
  4705  
  4706          TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
  4707      }
  4708      else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
  4709      {
  4710          // Required screen size is smaller than display size
  4711          TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
  4712  
  4713          if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
  4714          {
  4715              CORE.Window.screen.width = CORE.Window.display.width;
  4716              CORE.Window.screen.height = CORE.Window.display.height;
  4717          }
  4718  
  4719          // Upscaling to fit display with border-bars
  4720          float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
  4721          float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
  4722  
  4723          if (displayRatio <= screenRatio)
  4724          {
  4725              CORE.Window.render.width = CORE.Window.screen.width;
  4726              CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
  4727              CORE.Window.renderOffset.x = 0;
  4728              CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
  4729          }
  4730          else
  4731          {
  4732              CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
  4733              CORE.Window.render.height = CORE.Window.screen.height;
  4734              CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
  4735              CORE.Window.renderOffset.y = 0;
  4736          }
  4737      }
  4738      else
  4739      {
  4740          CORE.Window.render.width = CORE.Window.screen.width;
  4741          CORE.Window.render.height = CORE.Window.screen.height;
  4742          CORE.Window.renderOffset.x = 0;
  4743          CORE.Window.renderOffset.y = 0;
  4744      }
  4745  }
  4746  
  4747  // Initialize hi-resolution timer
  4748  static void InitTimer(void)
  4749  {
  4750  // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
  4751  // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
  4752  // High resolutions can also prevent the CPU power management system from entering power-saving modes.
  4753  // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.
  4754  #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
  4755      timeBeginPeriod(1);                 // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
  4756  #endif
  4757  
  4758  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  4759      struct timespec now = { 0 };
  4760  
  4761      if (clock_gettime(CLOCK_MONOTONIC, &now) == 0)  // Success
  4762      {
  4763          CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;
  4764      }
  4765      else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available");
  4766  #endif
  4767  
  4768      CORE.Time.previous = GetTime();     // Get time as double
  4769  }
  4770  
  4771  // Wait for some time (stop program execution)
  4772  // NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
  4773  // take longer than expected... for that reason we use the busy wait loop
  4774  // Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
  4775  // Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timming on Win32!
  4776  void WaitTime(double seconds)
  4777  {
  4778  #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
  4779      double destinationTime = GetTime() + seconds;
  4780  #endif
  4781  
  4782  #if defined(SUPPORT_BUSY_WAIT_LOOP)
  4783      while (GetTime() < destinationTime) { }
  4784  #else
  4785      #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
  4786          double sleepSeconds = seconds - seconds*0.05;  // NOTE: We reserve a percentage of the time for busy waiting
  4787      #else
  4788          double sleepSeconds = seconds;
  4789      #endif
  4790  
  4791      // System halt functions
  4792      #if defined(_WIN32)
  4793          Sleep((unsigned long)(sleepSeconds*1000.0));
  4794      #endif
  4795      #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
  4796          struct timespec req = { 0 };
  4797          time_t sec = sleepSeconds;
  4798          long nsec = (sleepSeconds - sec)*1000000000L;
  4799          req.tv_sec = sec;
  4800          req.tv_nsec = nsec;
  4801  
  4802          // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated.
  4803          while (nanosleep(&req, &req) == -1) continue;
  4804      #endif
  4805      #if defined(__APPLE__)
  4806          usleep(sleepSeconds*1000000.0);
  4807      #endif
  4808  
  4809      #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
  4810          while (GetTime() < destinationTime) { }
  4811      #endif
  4812  #endif
  4813  }
  4814  
  4815  // Swap back buffer with front buffer (screen drawing)
  4816  void SwapScreenBuffer(void)
  4817  {
  4818  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  4819      glfwSwapBuffers(CORE.Window.handle);
  4820  #endif
  4821  
  4822  #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  4823      eglSwapBuffers(CORE.Window.device, CORE.Window.surface);
  4824  
  4825  #if defined(PLATFORM_DRM)
  4826  
  4827      if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
  4828  
  4829      struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface);
  4830      if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer");
  4831  
  4832      uint32_t fb = 0;
  4833      int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb);
  4834      if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result);
  4835  
  4836      result = drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0, &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]);
  4837      if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result);
  4838  
  4839      if (CORE.Window.prevFB)
  4840      {
  4841          result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
  4842          if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result);
  4843      }
  4844  
  4845      CORE.Window.prevFB = fb;
  4846  
  4847      if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
  4848  
  4849      CORE.Window.prevBO = bo;
  4850  
  4851  #endif  // PLATFORM_DRM
  4852  #endif  // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM
  4853  }
  4854  
  4855  // Register all input events
  4856  void PollInputEvents(void)
  4857  {
  4858  #if defined(SUPPORT_GESTURES_SYSTEM)
  4859      // NOTE: Gestures update must be called every frame to reset gestures correctly
  4860      // because ProcessGestureEvent() is just called on an event, not every frame
  4861      UpdateGestures();
  4862  #endif
  4863  
  4864      // Reset keys/chars pressed registered
  4865      CORE.Input.Keyboard.keyPressedQueueCount = 0;
  4866      CORE.Input.Keyboard.charPressedQueueCount = 0;
  4867  
  4868  #if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM))
  4869      // Reset last gamepad button/axis registered state
  4870      CORE.Input.Gamepad.lastButtonPressed = -1;
  4871      CORE.Input.Gamepad.axisCount = 0;
  4872  #endif
  4873  
  4874  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  4875      // Register previous keys states
  4876      for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
  4877  
  4878      PollKeyboardEvents();
  4879  
  4880      // Register previous mouse states
  4881      CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
  4882      CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
  4883      for (int i = 0; i < MAX_MOUSE_BUTTONS; i++)
  4884      {
  4885          CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
  4886          CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i];
  4887      }
  4888  
  4889      // Register gamepads buttons events
  4890      for (int i = 0; i < MAX_GAMEPADS; i++)
  4891      {
  4892          if (CORE.Input.Gamepad.ready[i])
  4893          {
  4894              // Register previous gamepad states
  4895              for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
  4896          }
  4897      }
  4898  #endif
  4899  
  4900  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  4901      // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback)
  4902  
  4903      // Register previous keys states
  4904      for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
  4905  
  4906      // Register previous mouse states
  4907      for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
  4908  
  4909      // Register previous mouse wheel state
  4910      CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
  4911      CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
  4912  
  4913      // Register previous mouse position
  4914      CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
  4915  #endif
  4916  
  4917      // Register previous touch states
  4918      for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
  4919  
  4920      // Reset touch positions
  4921      // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event,
  4922      // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
  4923      //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
  4924  
  4925  #if defined(PLATFORM_DESKTOP)
  4926      // Check if gamepads are ready
  4927      // NOTE: We do it here in case of disconnection
  4928      for (int i = 0; i < MAX_GAMEPADS; i++)
  4929      {
  4930          if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true;
  4931          else CORE.Input.Gamepad.ready[i] = false;
  4932      }
  4933  
  4934      // Register gamepads buttons events
  4935      for (int i = 0; i < MAX_GAMEPADS; i++)
  4936      {
  4937          if (CORE.Input.Gamepad.ready[i])     // Check if gamepad is available
  4938          {
  4939              // Register previous gamepad states
  4940              for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
  4941  
  4942              // Get current gamepad state
  4943              // NOTE: There is no callback available, so we get it manually
  4944              // Get remapped buttons
  4945              GLFWgamepadstate state = { 0 };
  4946              glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller
  4947  
  4948              const unsigned char *buttons = state.buttons;
  4949  
  4950              for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++)
  4951              {
  4952                  GamepadButton button = -1;
  4953  
  4954                  switch (k)
  4955                  {
  4956                      case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
  4957                      case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
  4958                      case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
  4959                      case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
  4960  
  4961                      case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
  4962                      case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
  4963  
  4964                      case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
  4965                      case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
  4966                      case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
  4967  
  4968                      case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
  4969                      case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
  4970                      case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
  4971                      case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
  4972  
  4973                      case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
  4974                      case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
  4975                      default: break;
  4976                  }
  4977  
  4978                  if (button != -1)   // Check for valid button
  4979                  {
  4980                      if (buttons[k] == GLFW_PRESS)
  4981                      {
  4982                          CORE.Input.Gamepad.currentButtonState[i][button] = 1;
  4983                          CORE.Input.Gamepad.lastButtonPressed = button;
  4984                      }
  4985                      else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
  4986                  }
  4987              }
  4988  
  4989              // Get current axis state
  4990              const float *axes = state.axes;
  4991  
  4992              for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++)
  4993              {
  4994                  CORE.Input.Gamepad.axisState[i][k] = axes[k];
  4995              }
  4996  
  4997              // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis)
  4998              CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f);
  4999              CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f);
  5000  
  5001              CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1;
  5002          }
  5003      }
  5004  
  5005      CORE.Window.resizedLastFrame = false;
  5006  
  5007      if (CORE.Window.eventWaiting) glfwWaitEvents();     // Wait for in input events before continue (drawing is paused)
  5008      else glfwPollEvents();      // Poll input events: keyboard/mouse/window events (callbacks)
  5009  #endif  // PLATFORM_DESKTOP
  5010  
  5011  #if defined(PLATFORM_WEB)
  5012      CORE.Window.resizedLastFrame = false;
  5013  #endif  // PLATFORM_WEB
  5014  
  5015  // Gamepad support using emscripten API
  5016  // NOTE: GLFW3 joystick functionality not available in web
  5017  #if defined(PLATFORM_WEB)
  5018      // Get number of gamepads connected
  5019      int numGamepads = 0;
  5020      if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads();
  5021  
  5022      for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++)
  5023      {
  5024          // Register previous gamepad button states
  5025          for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
  5026  
  5027          EmscriptenGamepadEvent gamepadState;
  5028  
  5029          int result = emscripten_get_gamepad_status(i, &gamepadState);
  5030  
  5031          if (result == EMSCRIPTEN_RESULT_SUCCESS)
  5032          {
  5033              // Register buttons data for every connected gamepad
  5034              for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++)
  5035              {
  5036                  GamepadButton button = -1;
  5037  
  5038                  // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface
  5039                  switch (j)
  5040                  {
  5041                      case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
  5042                      case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
  5043                      case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
  5044                      case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
  5045                      case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
  5046                      case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
  5047                      case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
  5048                      case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
  5049                      case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
  5050                      case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
  5051                      case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
  5052                      case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
  5053                      case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
  5054                      case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
  5055                      case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
  5056                      case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
  5057                      default: break;
  5058                  }
  5059  
  5060                  if (button != -1)   // Check for valid button
  5061                  {
  5062                      if (gamepadState.digitalButton[j] == 1)
  5063                      {
  5064                          CORE.Input.Gamepad.currentButtonState[i][button] = 1;
  5065                          CORE.Input.Gamepad.lastButtonPressed = button;
  5066                      }
  5067                      else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
  5068                  }
  5069  
  5070                  //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
  5071              }
  5072  
  5073              // Register axis data for every connected gamepad
  5074              for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++)
  5075              {
  5076                  CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j];
  5077              }
  5078  
  5079              CORE.Input.Gamepad.axisCount = gamepadState.numAxes;
  5080          }
  5081      }
  5082  #endif
  5083  
  5084  #if defined(PLATFORM_ANDROID)
  5085      // Register previous keys states
  5086      // NOTE: Android supports up to 260 keys
  5087      for (int i = 0; i < 260; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
  5088  
  5089      // Android ALooper_pollAll() variables
  5090      int pollResult = 0;
  5091      int pollEvents = 0;
  5092  
  5093      // Poll Events (registered events)
  5094      // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled)
  5095      while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0)
  5096      {
  5097          // Process this event
  5098          if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source);
  5099  
  5100          // NOTE: Never close window, native activity is controlled by the system!
  5101          if (CORE.Android.app->destroyRequested != 0)
  5102          {
  5103              //CORE.Window.shouldClose = true;
  5104              //ANativeActivity_finish(CORE.Android.app->activity);
  5105          }
  5106      }
  5107  #endif
  5108  
  5109  #if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI)
  5110      // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
  5111      // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
  5112  
  5113      if (!CORE.Input.Keyboard.evtMode) ProcessKeyboard();
  5114  
  5115      // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread()
  5116      // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread()
  5117  #endif
  5118  }
  5119  
  5120  // Scan all files and directories in a base path
  5121  // WARNING: files.paths[] must be previously allocated and
  5122  // contain enough space to store all required paths
  5123  static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const char *filter)
  5124  {
  5125      static char path[MAX_FILEPATH_LENGTH] = { 0 };
  5126      memset(path, 0, MAX_FILEPATH_LENGTH);
  5127  
  5128      struct dirent *dp = NULL;
  5129      DIR *dir = opendir(basePath);
  5130  
  5131      if (dir != NULL)
  5132      {
  5133          while ((dp = readdir(dir)) != NULL)
  5134          {
  5135              if ((strcmp(dp->d_name, ".") != 0) &&
  5136                  (strcmp(dp->d_name, "..") != 0))
  5137              {
  5138                  sprintf(path, "%s/%s", basePath, dp->d_name);
  5139  
  5140                  if (filter != NULL)
  5141                  {
  5142                      if (IsFileExtension(path, filter))
  5143                      {
  5144                          strcpy(files->paths[files->count], path);
  5145                          files->count++;
  5146                      }
  5147                  }
  5148                  else
  5149                  {
  5150                      strcpy(files->paths[files->count], path);
  5151                      files->count++;
  5152                  }
  5153              }
  5154          }
  5155  
  5156          closedir(dir);
  5157      }
  5158      else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
  5159  }
  5160  
  5161  // Scan all files and directories recursively from a base path
  5162  static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter)
  5163  {
  5164      char path[MAX_FILEPATH_LENGTH] = { 0 };
  5165      memset(path, 0, MAX_FILEPATH_LENGTH);
  5166  
  5167      struct dirent *dp = NULL;
  5168      DIR *dir = opendir(basePath);
  5169  
  5170      if (dir != NULL)
  5171      {
  5172          while (((dp = readdir(dir)) != NULL) && (files->count < files->capacity))
  5173          {
  5174              if ((strcmp(dp->d_name, ".") != 0) && (strcmp(dp->d_name, "..") != 0))
  5175              {
  5176                  // Construct new path from our base path
  5177                  sprintf(path, "%s/%s", basePath, dp->d_name);
  5178  
  5179                  if (IsPathFile(path))
  5180                  {
  5181                      if (filter != NULL)
  5182                      {
  5183                          if (IsFileExtension(path, filter))
  5184                          {
  5185                              strcpy(files->paths[files->count], path);
  5186                              files->count++;
  5187                          }
  5188                      }
  5189                      else
  5190                      {
  5191                          strcpy(files->paths[files->count], path);
  5192                          files->count++;
  5193                      }
  5194  
  5195                      if (files->count >= files->capacity)
  5196                      {
  5197                          TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
  5198                          break;
  5199                      }
  5200                  }
  5201                  else ScanDirectoryFilesRecursively(path, files, filter);
  5202              }
  5203          }
  5204  
  5205          closedir(dir);
  5206      }
  5207      else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
  5208  }
  5209  
  5210  #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
  5211  // GLFW3 Error Callback, runs on GLFW3 error
  5212  static void ErrorCallback(int error, const char *description)
  5213  {
  5214      TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
  5215  }
  5216  
  5217  // GLFW3 WindowSize Callback, runs when window is resizedLastFrame
  5218  // NOTE: Window resizing not allowed by default
  5219  static void WindowSizeCallback(GLFWwindow *window, int width, int height)
  5220  {
  5221      // Reset viewport and projection matrix for new size
  5222      SetupViewport(width, height);
  5223  
  5224      CORE.Window.currentFbo.width = width;
  5225      CORE.Window.currentFbo.height = height;
  5226      CORE.Window.resizedLastFrame = true;
  5227  
  5228      if (IsWindowFullscreen()) return;
  5229  
  5230      // Set current screen size
  5231  #if defined(__APPLE__)
  5232      CORE.Window.screen.width = width;
  5233      CORE.Window.screen.height = height;
  5234  #else
  5235      if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
  5236      {
  5237          Vector2 windowScaleDPI = GetWindowScaleDPI();
  5238  
  5239          CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x);
  5240          CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y);
  5241      }
  5242      else
  5243      {
  5244          CORE.Window.screen.width = width;
  5245          CORE.Window.screen.height = height;
  5246      }
  5247  #endif
  5248  
  5249      // NOTE: Postprocessing texture is not scaled to new size
  5250  }
  5251  
  5252  // GLFW3 WindowIconify Callback, runs when window is minimized/restored
  5253  static void WindowIconifyCallback(GLFWwindow *window, int iconified)
  5254  {
  5255      if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;  // The window was iconified
  5256      else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;           // The window was restored
  5257  }
  5258  
  5259  #if !defined(PLATFORM_WEB)
  5260  // GLFW3 WindowMaximize Callback, runs when window is maximized/restored
  5261  static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
  5262  {
  5263      if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;  // The window was maximized
  5264      else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;           // The window was restored
  5265  }
  5266  #endif
  5267  
  5268  // GLFW3 WindowFocus Callback, runs when window get/lose focus
  5269  static void WindowFocusCallback(GLFWwindow *window, int focused)
  5270  {
  5271      if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;   // The window was focused
  5272      else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;            // The window lost focus
  5273  }
  5274  
  5275  // GLFW3 Keyboard Callback, runs on key pressed
  5276  static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
  5277  {
  5278      if (key < 0) return;    // Security check, macOS fn key generates -1
  5279  
  5280      // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
  5281      // to work properly with our implementation (IsKeyDown/IsKeyUp checks)
  5282      if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
  5283      else CORE.Input.Keyboard.currentKeyState[key] = 1;
  5284  
  5285  #if !defined(PLATFORM_WEB)
  5286      // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys
  5287      if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) ||
  5288          ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1;
  5289  #endif
  5290  
  5291      // Check if there is space available in the key queue
  5292      if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS))
  5293      {
  5294          // Add character to the queue
  5295          CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
  5296          CORE.Input.Keyboard.keyPressedQueueCount++;
  5297      }
  5298  
  5299      // Check the exit key to set close window
  5300      if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE);
  5301  
  5302  #if defined(SUPPORT_SCREEN_CAPTURE)
  5303      if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS))
  5304      {
  5305  #if defined(SUPPORT_GIF_RECORDING)
  5306          if (mods == GLFW_MOD_CONTROL)
  5307          {
  5308              if (gifRecording)
  5309              {
  5310                  gifRecording = false;
  5311  
  5312                  MsfGifResult result = msf_gif_end(&gifState);
  5313  
  5314                  SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize);
  5315                  msf_gif_free(result);
  5316  
  5317              #if defined(PLATFORM_WEB)
  5318                  // Download file from MEMFS (emscripten memory filesystem)
  5319                  // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html
  5320                  emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1)));
  5321              #endif
  5322  
  5323                  TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording");
  5324              }
  5325              else
  5326              {
  5327                  gifRecording = true;
  5328                  gifFrameCounter = 0;
  5329  
  5330                  Vector2 scale = GetWindowScaleDPI();
  5331                  msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
  5332                  screenshotCounter++;
  5333  
  5334                  TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter));
  5335              }
  5336          }
  5337          else
  5338  #endif  // SUPPORT_GIF_RECORDING
  5339          {
  5340              TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
  5341              screenshotCounter++;
  5342          }
  5343      }
  5344  #endif  // SUPPORT_SCREEN_CAPTURE
  5345  
  5346  #if defined(SUPPORT_EVENTS_AUTOMATION)
  5347      if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS))
  5348      {
  5349          eventsRecording = !eventsRecording;
  5350  
  5351          // On finish recording, we export events into a file
  5352          if (!eventsRecording) ExportAutomationEvents("eventsrec.rep");
  5353      }
  5354      else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS))
  5355      {
  5356          LoadAutomationEvents("eventsrec.rep");
  5357          eventsPlaying = true;
  5358  
  5359          TRACELOG(LOG_WARNING, "eventsPlaying enabled!");
  5360      }
  5361  #endif
  5362  }
  5363  
  5364  // GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value)
  5365  static void CharCallback(GLFWwindow *window, unsigned int key)
  5366  {
  5367      //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
  5368  
  5369      // NOTE: Registers any key down considering OS keyboard layout but
  5370      // do not detects action events, those should be managed by user...
  5371      // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
  5372      // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
  5373  
  5374      // Check if there is space available in the queue
  5375      if (CORE.Input.Keyboard.charPressedQueueCount < MAX_KEY_PRESSED_QUEUE)
  5376      {
  5377          // Add character to the queue
  5378          CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key;
  5379          CORE.Input.Keyboard.charPressedQueueCount++;
  5380      }
  5381  }
  5382  
  5383  // GLFW3 Mouse Button Callback, runs on mouse button pressed
  5384  static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
  5385  {
  5386      // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
  5387      // but future releases may add more actions (i.e. GLFW_REPEAT)
  5388      CORE.Input.Mouse.currentButtonState[button] = action;
  5389  
  5390  #if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)         // PLATFORM_DESKTOP
  5391      // Process mouse events as touches to be able to use mouse-gestures
  5392      GestureEvent gestureEvent = { 0 };
  5393  
  5394      // Register touch actions
  5395      if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
  5396      else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP;
  5397  
  5398      // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback()
  5399  
  5400      // Assign a pointer ID
  5401      gestureEvent.pointId[0] = 0;
  5402  
  5403      // Register touch points count
  5404      gestureEvent.pointCount = 1;
  5405  
  5406      // Register touch points position, only one point registered
  5407      gestureEvent.position[0] = GetMousePosition();
  5408  
  5409      // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
  5410      gestureEvent.position[0].x /= (float)GetScreenWidth();
  5411      gestureEvent.position[0].y /= (float)GetScreenHeight();
  5412  
  5413      // Gesture data is sent to gestures system for processing
  5414      ProcessGestureEvent(gestureEvent);
  5415  #endif
  5416  }
  5417  
  5418  // GLFW3 Cursor Position Callback, runs on mouse move
  5419  static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
  5420  {
  5421      CORE.Input.Mouse.currentPosition.x = (float)x;
  5422      CORE.Input.Mouse.currentPosition.y = (float)y;
  5423      CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
  5424  
  5425  #if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)         // PLATFORM_DESKTOP
  5426      // Process mouse events as touches to be able to use mouse-gestures
  5427      GestureEvent gestureEvent = { 0 };
  5428  
  5429      gestureEvent.touchAction = TOUCH_ACTION_MOVE;
  5430  
  5431      // Assign a pointer ID
  5432      gestureEvent.pointId[0] = 0;
  5433  
  5434      // Register touch points count
  5435      gestureEvent.pointCount = 1;
  5436  
  5437      // Register touch points position, only one point registered
  5438      gestureEvent.position[0] = CORE.Input.Touch.position[0];
  5439  
  5440      // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
  5441      gestureEvent.position[0].x /= (float)GetScreenWidth();
  5442      gestureEvent.position[0].y /= (float)GetScreenHeight();
  5443  
  5444      // Gesture data is sent to gestures system for processing
  5445      ProcessGestureEvent(gestureEvent);
  5446  #endif
  5447  }
  5448  
  5449  // GLFW3 Scrolling Callback, runs on mouse wheel
  5450  static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
  5451  {
  5452      CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset };
  5453  }
  5454  
  5455  // GLFW3 CursorEnter Callback, when cursor enters the window
  5456  static void CursorEnterCallback(GLFWwindow *window, int enter)
  5457  {
  5458      if (enter == true) CORE.Input.Mouse.cursorOnScreen = true;
  5459      else CORE.Input.Mouse.cursorOnScreen = false;
  5460  }
  5461  
  5462  // GLFW3 Window Drop Callback, runs when drop files into window
  5463  static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
  5464  {
  5465      // In case previous dropped filepaths have not been freed, we free them
  5466      if (CORE.Window.dropFileCount > 0)
  5467      {
  5468          for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]);
  5469  
  5470          RL_FREE(CORE.Window.dropFilepaths);
  5471  
  5472          CORE.Window.dropFileCount = 0;
  5473          CORE.Window.dropFilepaths = NULL;
  5474      }
  5475  
  5476      // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy
  5477      CORE.Window.dropFileCount = count;
  5478      CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *));
  5479  
  5480      for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
  5481      {
  5482          CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
  5483          strcpy(CORE.Window.dropFilepaths[i], paths[i]);
  5484      }
  5485  }
  5486  #endif
  5487  
  5488  #if defined(PLATFORM_ANDROID)
  5489  // ANDROID: Process activity lifecycle commands
  5490  static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
  5491  {
  5492      switch (cmd)
  5493      {
  5494          case APP_CMD_START:
  5495          {
  5496              //rendering = true;
  5497          } break;
  5498          case APP_CMD_RESUME: break;
  5499          case APP_CMD_INIT_WINDOW:
  5500          {
  5501              if (app->window != NULL)
  5502              {
  5503                  if (CORE.Android.contextRebindRequired)
  5504                  {
  5505                      // Reset screen scaling to full display size
  5506                      EGLint displayFormat = 0;
  5507                      eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
  5508  
  5509                      // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the
  5510                      // context rebinding if the screen is scaled unless offsets are added. There's probably a more
  5511                      // appropriate way to fix this
  5512                      ANativeWindow_setBuffersGeometry(app->window,
  5513                          CORE.Window.render.width + CORE.Window.renderOffset.x,
  5514                          CORE.Window.render.height + CORE.Window.renderOffset.y,
  5515                          displayFormat);
  5516  
  5517                      // Recreate display surface and re-attach OpenGL context
  5518                      CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL);
  5519                      eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context);
  5520  
  5521                      CORE.Android.contextRebindRequired = false;
  5522                  }
  5523                  else
  5524                  {
  5525                      CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window);
  5526                      CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window);
  5527  
  5528                      // Initialize graphics device (display device and OpenGL context)
  5529                      InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height);
  5530  
  5531                      // Initialize hi-res timer
  5532                      InitTimer();
  5533  
  5534                      // Initialize random seed
  5535                      srand((unsigned int)time(NULL));
  5536  
  5537                  #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
  5538                      // Load default font
  5539                      // WARNING: External function: Module required: rtext
  5540                      LoadFontDefault();
  5541                      Rectangle rec = GetFontDefault().recs[95];
  5542                      // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
  5543                      #if defined(SUPPORT_MODULE_RSHAPES)
  5544                      SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });  // WARNING: Module required: rshapes
  5545                      #endif
  5546                  #endif
  5547  
  5548                      // TODO: GPU assets reload in case of lost focus (lost context)
  5549                      // NOTE: This problem has been solved just unbinding and rebinding context from display
  5550                      /*
  5551                      if (assetsReloadRequired)
  5552                      {
  5553                          for (int i = 0; i < assetCount; i++)
  5554                          {
  5555                              // TODO: Unload old asset if required
  5556  
  5557                              // Load texture again to pointed texture
  5558                              (*textureAsset + i) = LoadTexture(assetPath[i]);
  5559                          }
  5560                      }
  5561                      */
  5562                  }
  5563              }
  5564          } break;
  5565          case APP_CMD_GAINED_FOCUS:
  5566          {
  5567              CORE.Android.appEnabled = true;
  5568              //ResumeMusicStream();
  5569          } break;
  5570          case APP_CMD_PAUSE: break;
  5571          case APP_CMD_LOST_FOCUS:
  5572          {
  5573              CORE.Android.appEnabled = false;
  5574              //PauseMusicStream();
  5575          } break;
  5576          case APP_CMD_TERM_WINDOW:
  5577          {
  5578              // Dettach OpenGL context and destroy display surface
  5579              // NOTE 1: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
  5580              // NOTE 2: In some cases (too many context loaded), OS could unload context automatically... :(
  5581              eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  5582              eglDestroySurface(CORE.Window.device, CORE.Window.surface);
  5583  
  5584              CORE.Android.contextRebindRequired = true;
  5585          } break;
  5586          case APP_CMD_SAVE_STATE: break;
  5587          case APP_CMD_STOP: break;
  5588          case APP_CMD_DESTROY:
  5589          {
  5590              // TODO: Finish activity?
  5591              //ANativeActivity_finish(CORE.Android.app->activity);
  5592          } break;
  5593          case APP_CMD_CONFIG_CHANGED:
  5594          {
  5595              //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager);
  5596              //print_cur_config(CORE.Android.app);
  5597  
  5598              // Check screen orientation here!
  5599          } break;
  5600          default: break;
  5601      }
  5602  }
  5603  
  5604  static GamepadButton AndroidTranslateGamepadButton(int button)
  5605  {
  5606      switch (button)
  5607      {
  5608          case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN;
  5609          case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT;
  5610          case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT;
  5611          case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP;
  5612          case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1;
  5613          case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1;
  5614          case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2;
  5615          case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
  5616          case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB;
  5617          case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB;
  5618          case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT;
  5619          case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT;
  5620          case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE;
  5621          // On some (most?) gamepads dpad events are reported as axis motion instead
  5622          case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN;
  5623          case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT;
  5624          case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT;
  5625          case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP;
  5626          default: return GAMEPAD_BUTTON_UNKNOWN;
  5627      }
  5628  }
  5629  
  5630  // ANDROID: Get input events
  5631  static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
  5632  {
  5633      // If additional inputs are required check:
  5634      // https://developer.android.com/ndk/reference/group/input
  5635      // https://developer.android.com/training/game-controllers/controller-input
  5636  
  5637      int type = AInputEvent_getType(event);
  5638      int source = AInputEvent_getSource(event);
  5639  
  5640      if (type == AINPUT_EVENT_TYPE_MOTION)
  5641      {
  5642          if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
  5643              ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
  5644          {
  5645              // For now we'll assume a single gamepad which we "detect" on its input event
  5646              CORE.Input.Gamepad.ready[0] = true;
  5647  
  5648              CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue(
  5649                      event, AMOTION_EVENT_AXIS_X, 0);
  5650              CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue(
  5651                      event, AMOTION_EVENT_AXIS_Y, 0);
  5652              CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue(
  5653                      event, AMOTION_EVENT_AXIS_Z, 0);
  5654              CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue(
  5655                      event, AMOTION_EVENT_AXIS_RZ, 0);
  5656              CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue(
  5657                      event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f;
  5658              CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue(
  5659                      event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f;
  5660  
  5661              // dpad is reported as an axis on android
  5662              float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0);
  5663              float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0);
  5664  
  5665              if (dpadX == 1.0f)
  5666              {
  5667                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1;
  5668                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0;
  5669              }
  5670              else if (dpadX == -1.0f)
  5671              {
  5672                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0;
  5673                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1;
  5674              }
  5675              else
  5676              {
  5677                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0;
  5678                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0;
  5679              }
  5680  
  5681              if (dpadY == 1.0f)
  5682              {
  5683                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1;
  5684                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0;
  5685              }
  5686              else if (dpadY == -1.0f)
  5687              {
  5688                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0;
  5689                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1;
  5690              }
  5691              else
  5692              {
  5693                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0;
  5694                  CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0;
  5695              }
  5696  
  5697              return 1; // Handled gamepad axis motion
  5698          }
  5699      }
  5700      else if (type == AINPUT_EVENT_TYPE_KEY)
  5701      {
  5702          int32_t keycode = AKeyEvent_getKeyCode(event);
  5703          //int32_t AKeyEvent_getMetaState(event);
  5704  
  5705          // Handle gamepad button presses and releases
  5706          if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
  5707              ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
  5708          {
  5709              // For now we'll assume a single gamepad which we "detect" on its input event
  5710              CORE.Input.Gamepad.ready[0] = true;
  5711  
  5712              GamepadButton button = AndroidTranslateGamepadButton(keycode);
  5713              if (button == GAMEPAD_BUTTON_UNKNOWN)
  5714                  return 1;
  5715  
  5716              if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
  5717              {
  5718                  CORE.Input.Gamepad.currentButtonState[0][button] = 1;
  5719              }
  5720              else CORE.Input.Gamepad.currentButtonState[0][button] = 0;  // Key up
  5721              return 1; // Handled gamepad button
  5722          }
  5723  
  5724          // Save current button and its state
  5725          // NOTE: Android key action is 0 for down and 1 for up
  5726          if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
  5727          {
  5728              CORE.Input.Keyboard.currentKeyState[keycode] = 1;   // Key down
  5729  
  5730              CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;
  5731              CORE.Input.Keyboard.keyPressedQueueCount++;
  5732          }
  5733          else CORE.Input.Keyboard.currentKeyState[keycode] = 0;  // Key up
  5734  
  5735          if (keycode == AKEYCODE_POWER)
  5736          {
  5737              // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS
  5738              // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS
  5739              // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected.
  5740              // NOTE: AndroidManifest.xml must have <activity android:configChanges="orientation|keyboardHidden|screenSize" >
  5741              // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
  5742              return 0;
  5743          }
  5744          else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU))
  5745          {
  5746              // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS!
  5747              return 1;
  5748          }
  5749          else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN))
  5750          {
  5751              // Set default OS behaviour
  5752              return 0;
  5753          }
  5754  
  5755          return 0;
  5756      }
  5757  
  5758      // Register touch points count
  5759      CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event);
  5760  
  5761      for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
  5762      {
  5763          // Register touch points id
  5764          CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i);
  5765  
  5766          // Register touch points position
  5767          CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
  5768  
  5769          // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height
  5770          float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x) / (float)CORE.Window.display.width;
  5771          float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y) / (float)CORE.Window.display.height;
  5772          CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x * widthRatio - (float)CORE.Window.renderOffset.x / 2;
  5773          CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y * heightRatio - (float)CORE.Window.renderOffset.y / 2;
  5774      }
  5775  
  5776      int32_t action = AMotionEvent_getAction(event);
  5777      unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
  5778  
  5779  #if defined(SUPPORT_GESTURES_SYSTEM)        // PLATFORM_ANDROID
  5780      GestureEvent gestureEvent = { 0 };
  5781  
  5782      gestureEvent.pointCount = CORE.Input.Touch.pointCount;
  5783  
  5784      // Register touch actions
  5785      if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
  5786      else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP;
  5787      else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
  5788      else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
  5789  
  5790      for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
  5791      {
  5792          gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
  5793          gestureEvent.position[i] = CORE.Input.Touch.position[i];
  5794      }
  5795  
  5796      // Gesture data is sent to gestures system for processing
  5797      ProcessGestureEvent(gestureEvent);
  5798  #endif
  5799  
  5800      int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
  5801  
  5802      if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP)
  5803      {
  5804          // One of the touchpoints is released, remove it from touch point arrays
  5805          for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount-1) && (i < MAX_TOUCH_POINTS); i++)
  5806          {
  5807              CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1];
  5808              CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1];
  5809          }
  5810          CORE.Input.Touch.pointCount--;
  5811      }
  5812  
  5813      // When all touchpoints are tapped and released really quickly, this event is generated
  5814      if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0;
  5815  
  5816      if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
  5817      else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
  5818  
  5819      return 0;
  5820  }
  5821  #endif
  5822  
  5823  #if defined(PLATFORM_WEB)
  5824  // Register fullscreen change events
  5825  static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData)
  5826  {
  5827      // TODO: Implement EmscriptenFullscreenChangeCallback()?
  5828  
  5829      return 1;   // The event was consumed by the callback handler
  5830  }
  5831  
  5832  // Register window resize event
  5833  static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
  5834  {
  5835      // TODO: Implement EmscriptenWindowResizedCallback()?
  5836  
  5837      return 1;   // The event was consumed by the callback handler
  5838  }
  5839  
  5840  EM_JS(int, GetCanvasWidth, (), { return canvas.clientWidth; });
  5841  EM_JS(int, GetCanvasHeight, (), { return canvas.clientHeight; });
  5842  
  5843  // Register DOM element resize event
  5844  static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
  5845  {
  5846      // Don't resize non-resizeable windows
  5847      if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1;
  5848  
  5849      // This event is called whenever the window changes sizes,
  5850      // so the size of the canvas object is explicitly retrieved below
  5851      int width = GetCanvasWidth();
  5852      int height = GetCanvasHeight();
  5853      emscripten_set_canvas_element_size("#canvas",width,height);
  5854  
  5855      SetupViewport(width, height);    // Reset viewport and projection matrix for new size
  5856  
  5857      CORE.Window.currentFbo.width = width;
  5858      CORE.Window.currentFbo.height = height;
  5859      CORE.Window.resizedLastFrame = true;
  5860  
  5861      if (IsWindowFullscreen()) return 1;
  5862  
  5863      // Set current screen size
  5864      CORE.Window.screen.width = width;
  5865      CORE.Window.screen.height = height;
  5866  
  5867      // NOTE: Postprocessing texture is not scaled to new size
  5868  
  5869      return 0;
  5870  }
  5871  
  5872  // Register mouse input events
  5873  static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
  5874  {
  5875      // This is only for registering mouse click events with emscripten and doesn't need to do anything
  5876  
  5877      return 1;   // The event was consumed by the callback handler
  5878  }
  5879  
  5880  // Register connected/disconnected gamepads events
  5881  static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
  5882  {
  5883      /*
  5884      TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"",
  5885             eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
  5886             gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
  5887  
  5888      for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
  5889      for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
  5890      */
  5891  
  5892      if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS))
  5893      {
  5894          CORE.Input.Gamepad.ready[gamepadEvent->index] = true;
  5895          sprintf(CORE.Input.Gamepad.name[gamepadEvent->index],"%s",gamepadEvent->id);
  5896      }
  5897      else CORE.Input.Gamepad.ready[gamepadEvent->index] = false;
  5898  
  5899      return 1;   // The event was consumed by the callback handler
  5900  }
  5901  
  5902  // Register touch input events
  5903  static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
  5904  {
  5905      // Register touch points count
  5906      CORE.Input.Touch.pointCount = touchEvent->numTouches;
  5907  
  5908      double canvasWidth = 0.0;
  5909      double canvasHeight = 0.0;
  5910      // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but
  5911      // we are looking for actual CSS size: canvas.style.width and canvas.style.height
  5912      //EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight);
  5913      emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight);
  5914  
  5915      for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
  5916      {
  5917          // Register touch points id
  5918          CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier;
  5919  
  5920          // Register touch points position
  5921          CORE.Input.Touch.position[i] = (Vector2){ touchEvent->touches[i].targetX, touchEvent->touches[i].targetY };
  5922  
  5923          // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height
  5924          CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth);
  5925          CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight);
  5926  
  5927          if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1;
  5928          else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
  5929      }
  5930  
  5931  #if defined(SUPPORT_GESTURES_SYSTEM)        // PLATFORM_WEB
  5932      GestureEvent gestureEvent = { 0 };
  5933  
  5934      gestureEvent.pointCount = CORE.Input.Touch.pointCount;
  5935  
  5936      // Register touch actions
  5937      if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
  5938      else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP;
  5939      else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
  5940      else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
  5941  
  5942      for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
  5943      {
  5944          gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
  5945          gestureEvent.position[i] = CORE.Input.Touch.position[i];
  5946      }
  5947  
  5948      // Gesture data is sent to gestures system for processing
  5949      ProcessGestureEvent(gestureEvent);
  5950  #endif
  5951  
  5952      return 1;   // The event was consumed by the callback handler
  5953  }
  5954  #endif
  5955  
  5956  #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
  5957  // Initialize Keyboard system (using standard input)
  5958  static void InitKeyboard(void)
  5959  {
  5960      // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor,
  5961      // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
  5962  
  5963      // Save terminal keyboard settings
  5964      tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings);
  5965  
  5966      // Reconfigure terminal with new settings
  5967      struct termios keyboardNewSettings = { 0 };
  5968      keyboardNewSettings = CORE.Input.Keyboard.defaultSettings;
  5969  
  5970      // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
  5971      // NOTE: ISIG controls if ^C and ^Z generate break signals or not
  5972      keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG);
  5973      //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
  5974      keyboardNewSettings.c_cc[VMIN] = 1;
  5975      keyboardNewSettings.c_cc[VTIME] = 0;
  5976  
  5977      // Set new keyboard settings (change occurs immediately)
  5978      tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings);
  5979  
  5980      // Save old keyboard mode to restore it at the end
  5981      CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0);          // F_GETFL: Get the file access mode and the file status flags
  5982      fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified
  5983  
  5984      // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno)
  5985      int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode);
  5986  
  5987      // In case of failure, it could mean a remote keyboard is used (SSH)
  5988      if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used");
  5989      else
  5990      {
  5991          // Reconfigure keyboard mode to get:
  5992          //    - scancodes (K_RAW)
  5993          //    - keycodes (K_MEDIUMRAW)
  5994          //    - ASCII chars (K_XLATE)
  5995          //    - UNICODE chars (K_UNICODE)
  5996          ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE);  // ASCII chars
  5997      }
  5998  
  5999      // Register keyboard restore when program finishes
  6000      atexit(RestoreKeyboard);
  6001  }
  6002  
  6003  // Restore default keyboard input
  6004  static void RestoreKeyboard(void)
  6005  {
  6006      // Reset to default keyboard settings
  6007      tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings);
  6008  
  6009      // Reconfigure keyboard to default mode
  6010      fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags);
  6011      ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode);
  6012  }
  6013  
  6014  #if defined(SUPPORT_SSH_KEYBOARD_RPI)
  6015  // Process keyboard inputs
  6016  static void ProcessKeyboard(void)
  6017  {
  6018      #define MAX_KEYBUFFER_SIZE      32      // Max size in bytes to read
  6019  
  6020      // Keyboard input polling (fill keys[256] array with status)
  6021      int bufferByteCount = 0;                        // Bytes available on the buffer
  6022      char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 };    // Max keys to be read at a time
  6023  
  6024      // Read availables keycodes from stdin
  6025      bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE);     // POSIX system call
  6026  
  6027      // Reset pressed keys array (it will be filled below)
  6028      for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.currentKeyState[i] = 0;
  6029  
  6030      // Fill all read bytes (looking for keys)
  6031      for (int i = 0; i < bufferByteCount; i++)
  6032      {
  6033          // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code!
  6034          // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42
  6035          if (keysBuffer[i] == 0x1b)
  6036          {
  6037              // Check if ESCAPE key has been pressed to stop program
  6038              if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1;
  6039              else
  6040              {
  6041                  if (keysBuffer[i + 1] == 0x5b)    // Special function key
  6042                  {
  6043                      if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32))
  6044                      {
  6045                          // Process special function keys (F1 - F12)
  6046                          switch (keysBuffer[i + 3])
  6047                          {
  6048                              case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break;    // raylib KEY_F1
  6049                              case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break;    // raylib KEY_F2
  6050                              case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break;    // raylib KEY_F3
  6051                              case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break;    // raylib KEY_F4
  6052                              case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break;    // raylib KEY_F5
  6053                              case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break;    // raylib KEY_F6
  6054                              case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break;    // raylib KEY_F7
  6055                              case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break;    // raylib KEY_F8
  6056                              case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break;    // raylib KEY_F9
  6057                              case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break;    // raylib KEY_F10
  6058                              case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break;    // raylib KEY_F11
  6059                              case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break;    // raylib KEY_F12
  6060                              default: break;
  6061                          }
  6062  
  6063                          if (keysBuffer[i + 2] == 0x5b) i += 4;
  6064                          else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5;
  6065                      }
  6066                      else
  6067                      {
  6068                          switch (keysBuffer[i + 2])
  6069                          {
  6070                              case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break;    // raylib KEY_UP
  6071                              case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break;    // raylib KEY_DOWN
  6072                              case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break;    // raylib KEY_RIGHT
  6073                              case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break;    // raylib KEY_LEFT
  6074                              default: break;
  6075                          }
  6076  
  6077                          i += 3;  // Jump to next key
  6078                      }
  6079  
  6080                      // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT)
  6081                  }
  6082              }
  6083          }
  6084          else if (keysBuffer[i] == 0x0a)     // raylib KEY_ENTER (don't mix with <linux/input.h> KEY_*)
  6085          {
  6086              CORE.Input.Keyboard.currentKeyState[257] = 1;
  6087  
  6088              CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257;     // Add keys pressed into queue
  6089              CORE.Input.Keyboard.keyPressedQueueCount++;
  6090          }
  6091          else if (keysBuffer[i] == 0x7f)     // raylib KEY_BACKSPACE
  6092          {
  6093              CORE.Input.Keyboard.currentKeyState[259] = 1;
  6094  
  6095              CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257;     // Add keys pressed into queue
  6096              CORE.Input.Keyboard.keyPressedQueueCount++;
  6097          }
  6098          else
  6099          {
  6100              // Translate lowercase a-z letters to A-Z
  6101              if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122))
  6102              {
  6103                  CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1;
  6104              }
  6105              else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1;
  6106  
  6107              CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i];     // Add keys pressed into queue
  6108              CORE.Input.Keyboard.keyPressedQueueCount++;
  6109          }
  6110      }
  6111  
  6112      // Check exit key (same functionality as GLFW3 KeyCallback())
  6113      if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
  6114  
  6115  #if defined(SUPPORT_SCREEN_CAPTURE)
  6116      // Check screen capture key (raylib key: KEY_F12)
  6117      if (CORE.Input.Keyboard.currentKeyState[301] == 1)
  6118      {
  6119          TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
  6120          screenshotCounter++;
  6121      }
  6122  #endif
  6123  }
  6124  #endif  // SUPPORT_SSH_KEYBOARD_RPI
  6125  
  6126  // Initialise user input from evdev(/dev/input/event<N>) this means mouse, keyboard or gamepad devices
  6127  static void InitEvdevInput(void)
  6128  {
  6129      char path[MAX_FILEPATH_LENGTH] = { 0 };
  6130      DIR *directory = NULL;
  6131      struct dirent *entity = NULL;
  6132  
  6133      // Initialise keyboard file descriptor
  6134      CORE.Input.Keyboard.fd = -1;
  6135  
  6136      // Reset variables
  6137      for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
  6138      {
  6139          CORE.Input.Touch.position[i].x = -1;
  6140          CORE.Input.Touch.position[i].y = -1;
  6141      }
  6142  
  6143      // Reset keyboard key state
  6144      for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.currentKeyState[i] = 0;
  6145  
  6146      // Open the linux directory of "/dev/input"
  6147      directory = opendir(DEFAULT_EVDEV_PATH);
  6148  
  6149      if (directory)
  6150      {
  6151          while ((entity = readdir(directory)) != NULL)
  6152          {
  6153              if ((strncmp("event", entity->d_name, strlen("event")) == 0) ||     // Search for devices named "event*"
  6154                  (strncmp("mouse", entity->d_name, strlen("mouse")) == 0))       // Search for devices named "mouse*"
  6155              {
  6156                  sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
  6157                  ConfigureEvdevDevice(path);                                     // Configure the device if appropriate
  6158              }
  6159          }
  6160  
  6161          closedir(directory);
  6162      }
  6163      else TRACELOG(LOG_WARNING, "RPI: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH);
  6164  }
  6165  
  6166  // Identifies a input device and configures it for use if appropriate
  6167  static void ConfigureEvdevDevice(char *device)
  6168  {
  6169      #define BITS_PER_LONG   (8*sizeof(long))
  6170      #define NBITS(x)        ((((x) - 1)/BITS_PER_LONG) + 1)
  6171      #define OFF(x)          ((x)%BITS_PER_LONG)
  6172      #define BIT(x)          (1UL<<OFF(x))
  6173      #define LONG(x)         ((x)/BITS_PER_LONG)
  6174      #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
  6175  
  6176      struct input_absinfo absinfo = { 0 };
  6177      unsigned long evBits[NBITS(EV_MAX)] = { 0 };
  6178      unsigned long absBits[NBITS(ABS_MAX)] = { 0 };
  6179      unsigned long relBits[NBITS(REL_MAX)] = { 0 };
  6180      unsigned long keyBits[NBITS(KEY_MAX)] = { 0 };
  6181      bool hasAbs = false;
  6182      bool hasRel = false;
  6183      bool hasAbsMulti = false;
  6184      int freeWorkerId = -1;
  6185      int fd = -1;
  6186  
  6187      InputEventWorker *worker = NULL;
  6188  
  6189      // Open the device and allocate worker
  6190      //-------------------------------------------------------------------------------------------------------
  6191      // Find a free spot in the workers array
  6192      for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
  6193      {
  6194          if (CORE.Input.eventWorker[i].threadId == 0)
  6195          {
  6196              freeWorkerId = i;
  6197              break;
  6198          }
  6199      }
  6200  
  6201      // Select the free worker from array
  6202      if (freeWorkerId >= 0)
  6203      {
  6204          worker = &(CORE.Input.eventWorker[freeWorkerId]);       // Grab a pointer to the worker
  6205          memset(worker, 0, sizeof(InputEventWorker));  // Clear the worker
  6206      }
  6207      else
  6208      {
  6209          TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device);
  6210          return;
  6211      }
  6212  
  6213      // Open the device
  6214      fd = open(device, O_RDONLY | O_NONBLOCK);
  6215      if (fd < 0)
  6216      {
  6217          TRACELOG(LOG_WARNING, "RPI: Failed to open input device: %s", device);
  6218          return;
  6219      }
  6220      worker->fd = fd;
  6221  
  6222      // Grab number on the end of the devices name "event<N>"
  6223      int devNum = 0;
  6224      char *ptrDevName = strrchr(device, 't');
  6225      worker->eventNum = -1;
  6226  
  6227      if (ptrDevName != NULL)
  6228      {
  6229          if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum;
  6230      }
  6231      else worker->eventNum = 0;      // TODO: HACK: Grab number for mouse0 device!
  6232  
  6233      // At this point we have a connection to the device, but we don't yet know what the device is.
  6234      // It could be many things, even as simple as a power button...
  6235      //-------------------------------------------------------------------------------------------------------
  6236  
  6237      // Identify the device
  6238      //-------------------------------------------------------------------------------------------------------
  6239      ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits);    // Read a bitfield of the available device properties
  6240  
  6241      // Check for absolute input devices
  6242      if (TEST_BIT(evBits, EV_ABS))
  6243      {
  6244          ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
  6245  
  6246          // Check for absolute movement support (usualy touchscreens, but also joysticks)
  6247          if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y))
  6248          {
  6249              hasAbs = true;
  6250  
  6251              // Get the scaling values
  6252              ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
  6253              worker->absRange.x = absinfo.minimum;
  6254              worker->absRange.width = absinfo.maximum - absinfo.minimum;
  6255              ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
  6256              worker->absRange.y = absinfo.minimum;
  6257              worker->absRange.height = absinfo.maximum - absinfo.minimum;
  6258          }
  6259  
  6260          // Check for multiple absolute movement support (usualy multitouch touchscreens)
  6261          if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y))
  6262          {
  6263              hasAbsMulti = true;
  6264  
  6265              // Get the scaling values
  6266              ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
  6267              worker->absRange.x = absinfo.minimum;
  6268              worker->absRange.width = absinfo.maximum - absinfo.minimum;
  6269              ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
  6270              worker->absRange.y = absinfo.minimum;
  6271              worker->absRange.height = absinfo.maximum - absinfo.minimum;
  6272          }
  6273      }
  6274  
  6275      // Check for relative movement support (usualy mouse)
  6276      if (TEST_BIT(evBits, EV_REL))
  6277      {
  6278          ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
  6279  
  6280          if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true;
  6281      }
  6282  
  6283      // Check for button support to determine the device type(usualy on all input devices)
  6284      if (TEST_BIT(evBits, EV_KEY))
  6285      {
  6286          ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
  6287  
  6288          if (hasAbs || hasAbsMulti)
  6289          {
  6290              if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true;          // This is a touchscreen
  6291              if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true;    // This is a drawing tablet
  6292              if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true;       // This is a drawing tablet
  6293              if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true;         // This is a drawing tablet
  6294              if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true;   // This is a multitouch capable device
  6295          }
  6296  
  6297          if (hasRel)
  6298          {
  6299              if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true;           // This is a mouse
  6300              if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true;          // This is a mouse
  6301          }
  6302  
  6303          if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true;                // This is a gamepad
  6304          if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true;          // This is a gamepad
  6305          if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true;            // This is a gamepad
  6306          if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true;               // This is a gamepad
  6307          if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true;               // This is a gamepad
  6308  
  6309          if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true;           // This is a keyboard
  6310      }
  6311      //-------------------------------------------------------------------------------------------------------
  6312  
  6313      // Decide what to do with the device
  6314      //-------------------------------------------------------------------------------------------------------
  6315      if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1))
  6316      {
  6317          // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a
  6318          // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate
  6319          // threads so that they don't drop events when the frame rate is slow.
  6320          TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device);
  6321          CORE.Input.Keyboard.fd = worker->fd;
  6322      }
  6323      else if (worker->isTouch || worker->isMouse)
  6324      {
  6325          // Looks like an interesting device
  6326          TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device,
  6327              worker->isMouse? "mouse " : "",
  6328              worker->isMultitouch? "multitouch " : "",
  6329              worker->isTouch? "touchscreen " : "",
  6330              worker->isGamepad? "gamepad " : "");
  6331  
  6332          // Create a thread for this device
  6333          int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker);
  6334          if (error != 0)
  6335          {
  6336              TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error);
  6337              worker->threadId = 0;
  6338              close(fd);
  6339          }
  6340  
  6341  #if defined(USE_LAST_TOUCH_DEVICE)
  6342          // Find touchscreen with the highest index
  6343          int maxTouchNumber = -1;
  6344  
  6345          for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
  6346          {
  6347              if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum;
  6348          }
  6349  
  6350          // Find touchscreens with lower indexes
  6351          for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
  6352          {
  6353              if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber))
  6354              {
  6355                  if (CORE.Input.eventWorker[i].threadId != 0)
  6356                  {
  6357                      TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i);
  6358                      pthread_cancel(CORE.Input.eventWorker[i].threadId);
  6359                      close(CORE.Input.eventWorker[i].fd);
  6360                  }
  6361              }
  6362          }
  6363  #endif
  6364      }
  6365      else close(fd);  // We are not interested in this device
  6366      //-------------------------------------------------------------------------------------------------------
  6367  }
  6368  
  6369  static void PollKeyboardEvents(void)
  6370  {
  6371      // Scancode to keycode mapping for US keyboards
  6372      // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard:
  6373      // Currently non US keyboards will have the wrong mapping for some keys
  6374      static const int keymapUS[] = {
  6375          0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84,
  6376          89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96,
  6377          340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291,
  6378          292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325,
  6379          326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95,
  6380          335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261,
  6381          112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127,
  6382          128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
  6383          144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
  6384          160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
  6385          176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
  6386          192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
  6387          211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
  6388          227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
  6389          243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0
  6390      };
  6391  
  6392      int fd = CORE.Input.Keyboard.fd;
  6393      if (fd == -1) return;
  6394  
  6395      struct input_event event = { 0 };
  6396      int keycode = -1;
  6397  
  6398      // Try to read data from the keyboard and only continue if successful
  6399      while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
  6400      {
  6401          // Button parsing
  6402          if (event.type == EV_KEY)
  6403          {
  6404  #if defined(SUPPORT_SSH_KEYBOARD_RPI)
  6405              // Change keyboard mode to events
  6406              CORE.Input.Keyboard.evtMode = true;
  6407  #endif
  6408              // Keyboard button parsing
  6409              if ((event.code >= 1) && (event.code <= 255))     //Keyboard keys appear for codes 1 to 255
  6410              {
  6411                  keycode = keymapUS[event.code & 0xFF];     // The code we get is a scancode so we look up the apropriate keycode
  6412  
  6413                  // Make sure we got a valid keycode
  6414                  if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState)))
  6415                  {
  6416                      // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
  6417                      // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL,
  6418                      // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat
  6419                      CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0;
  6420                      if (event.value >= 1)
  6421                      {
  6422                          CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;     // Register last key pressed
  6423                          CORE.Input.Keyboard.keyPressedQueueCount++;
  6424                      }
  6425  
  6426                  #if defined(SUPPORT_SCREEN_CAPTURE)
  6427                      // Check screen capture key (raylib key: KEY_F12)
  6428                      if (CORE.Input.Keyboard.currentKeyState[301] == 1)
  6429                      {
  6430                          TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
  6431                          screenshotCounter++;
  6432                      }
  6433                  #endif
  6434  
  6435                      if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
  6436  
  6437                      TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", event.value == 0 ? "UP":"DOWN", event.code, keycode);
  6438                  }
  6439              }
  6440          }
  6441      }
  6442  }
  6443  
  6444  // Input device events reading thread
  6445  static void *EventThread(void *arg)
  6446  {
  6447      struct input_event event = { 0 };
  6448      InputEventWorker *worker = (InputEventWorker *)arg;
  6449  
  6450      int touchAction = -1;           // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
  6451      bool gestureUpdate = false;     // Flag to note gestures require to update
  6452  
  6453      while (!CORE.Window.shouldClose)
  6454      {
  6455          // Try to read data from the device and only continue if successful
  6456          while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event))
  6457          {
  6458              // Relative movement parsing
  6459              if (event.type == EV_REL)
  6460              {
  6461                  if (event.code == REL_X)
  6462                  {
  6463                      CORE.Input.Mouse.currentPosition.x += event.value;
  6464                      CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
  6465  
  6466                      touchAction = 2;    // TOUCH_ACTION_MOVE
  6467                      gestureUpdate = true;
  6468                  }
  6469  
  6470                  if (event.code == REL_Y)
  6471                  {
  6472                      CORE.Input.Mouse.currentPosition.y += event.value;
  6473                      CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
  6474  
  6475                      touchAction = 2;    // TOUCH_ACTION_MOVE
  6476                      gestureUpdate = true;
  6477                  }
  6478  
  6479                  if (event.code == REL_WHEEL) CORE.Input.Mouse.currentWheelMove.y += event.value;
  6480              }
  6481  
  6482              // Absolute movement parsing
  6483              if (event.type == EV_ABS)
  6484              {
  6485                  // Basic movement
  6486                  if (event.code == ABS_X)
  6487                  {
  6488                      CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width;    // Scale acording to absRange
  6489                      CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width;        // Scale acording to absRange
  6490  
  6491                      touchAction = 2;    // TOUCH_ACTION_MOVE
  6492                      gestureUpdate = true;
  6493                  }
  6494  
  6495                  if (event.code == ABS_Y)
  6496                  {
  6497                      CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height;  // Scale acording to absRange
  6498                      CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height;      // Scale acording to absRange
  6499  
  6500                      touchAction = 2;    // TOUCH_ACTION_MOVE
  6501                      gestureUpdate = true;
  6502                  }
  6503  
  6504                  // Multitouch movement
  6505                  if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value;   // Remember the slot number for the folowing events
  6506  
  6507                  if (event.code == ABS_MT_POSITION_X)
  6508                  {
  6509                      if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width;    // Scale acording to absRange
  6510                  }
  6511  
  6512                  if (event.code == ABS_MT_POSITION_Y)
  6513                  {
  6514                      if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height;  // Scale acording to absRange
  6515                  }
  6516  
  6517                  if (event.code == ABS_MT_TRACKING_ID)
  6518                  {
  6519                      if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS))
  6520                      {
  6521                          // Touch has ended for this point
  6522                          CORE.Input.Touch.position[worker->touchSlot].x = -1;
  6523                          CORE.Input.Touch.position[worker->touchSlot].y = -1;
  6524                      }
  6525                  }
  6526  
  6527                  // Touchscreen tap
  6528                  if (event.code == ABS_PRESSURE)
  6529                  {
  6530                      int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
  6531  
  6532                      if (!event.value && previousMouseLeftButtonState)
  6533                      {
  6534                          CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
  6535  
  6536                          touchAction = 0;    // TOUCH_ACTION_UP
  6537                          gestureUpdate = true;
  6538                      }
  6539  
  6540                      if (event.value && !previousMouseLeftButtonState)
  6541                      {
  6542                          CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
  6543  
  6544                          touchAction = 1;    // TOUCH_ACTION_DOWN
  6545                          gestureUpdate = true;
  6546                      }
  6547                  }
  6548  
  6549              }
  6550  
  6551              // Button parsing
  6552              if (event.type == EV_KEY)
  6553              {
  6554                  // Mouse button parsing
  6555                  if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
  6556                  {
  6557                      CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
  6558  
  6559                      if (event.value > 0) touchAction = 1;   // TOUCH_ACTION_DOWN
  6560                      else touchAction = 0;       // TOUCH_ACTION_UP
  6561                      gestureUpdate = true;
  6562                  }
  6563  
  6564                  if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
  6565                  if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value;
  6566                  if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value;
  6567                  if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value;
  6568                  if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value;
  6569                  if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value;
  6570              }
  6571  
  6572              // Screen confinement
  6573              if (!CORE.Input.Mouse.cursorHidden)
  6574              {
  6575                  if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
  6576                  if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x;
  6577  
  6578                  if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
  6579                  if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y;
  6580              }
  6581  
  6582  #if defined(SUPPORT_GESTURES_SYSTEM)        // PLATFORM_RPI, PLATFORM_DRM
  6583              if (gestureUpdate)
  6584              {
  6585                  GestureEvent gestureEvent = { 0 };
  6586  
  6587                  gestureEvent.pointCount = 0;
  6588                  gestureEvent.touchAction = touchAction;
  6589  
  6590                  if (CORE.Input.Touch.position[0].x >= 0) gestureEvent.pointCount++;
  6591                  if (CORE.Input.Touch.position[1].x >= 0) gestureEvent.pointCount++;
  6592                  if (CORE.Input.Touch.position[2].x >= 0) gestureEvent.pointCount++;
  6593                  if (CORE.Input.Touch.position[3].x >= 0) gestureEvent.pointCount++;
  6594  
  6595                  gestureEvent.pointId[0] = 0;
  6596                  gestureEvent.pointId[1] = 1;
  6597                  gestureEvent.pointId[2] = 2;
  6598                  gestureEvent.pointId[3] = 3;
  6599  
  6600                  gestureEvent.position[0] = CORE.Input.Touch.position[0];
  6601                  gestureEvent.position[1] = CORE.Input.Touch.position[1];
  6602                  gestureEvent.position[2] = CORE.Input.Touch.position[2];
  6603                  gestureEvent.position[3] = CORE.Input.Touch.position[3];
  6604  
  6605                  ProcessGestureEvent(gestureEvent);
  6606              }
  6607  #endif
  6608          }
  6609  
  6610          WaitTime(0.005);    // Sleep for 5ms to avoid hogging CPU time
  6611      }
  6612  
  6613      close(worker->fd);
  6614  
  6615      return NULL;
  6616  }
  6617  
  6618  // Initialize gamepad system
  6619  static void InitGamepad(void)
  6620  {
  6621      char gamepadDev[128] = { 0 };
  6622  
  6623      for (int i = 0; i < MAX_GAMEPADS; i++)
  6624      {
  6625          sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i);
  6626  
  6627          if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0)
  6628          {
  6629              // NOTE: Only show message for first gamepad
  6630              if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available");
  6631          }
  6632          else
  6633          {
  6634              CORE.Input.Gamepad.ready[i] = true;
  6635  
  6636              // NOTE: Only create one thread
  6637              if (i == 0)
  6638              {
  6639                  int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL);
  6640  
  6641                  if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread");
  6642                  else  TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully");
  6643              }
  6644          }
  6645      }
  6646  }
  6647  
  6648  // Process Gamepad (/dev/input/js0)
  6649  static void *GamepadThread(void *arg)
  6650  {
  6651      #define JS_EVENT_BUTTON         0x01    // Button pressed/released
  6652      #define JS_EVENT_AXIS           0x02    // Joystick axis moved
  6653      #define JS_EVENT_INIT           0x80    // Initial state of device
  6654  
  6655      struct js_event {
  6656          unsigned int time;      // event timestamp in milliseconds
  6657          short value;            // event value
  6658          unsigned char type;     // event type
  6659          unsigned char number;   // event axis/button number
  6660      };
  6661  
  6662      // Read gamepad event
  6663      struct js_event gamepadEvent = { 0 };
  6664  
  6665      while (!CORE.Window.shouldClose)
  6666      {
  6667          for (int i = 0; i < MAX_GAMEPADS; i++)
  6668          {
  6669              if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
  6670              {
  6671                  gamepadEvent.type &= ~JS_EVENT_INIT;     // Ignore synthetic events
  6672  
  6673                  // Process gamepad events by type
  6674                  if (gamepadEvent.type == JS_EVENT_BUTTON)
  6675                  {
  6676                      //TRACELOG(LOG_WARNING, "RPI: Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
  6677  
  6678                      if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
  6679                      {
  6680                          // 1 - button pressed, 0 - button released
  6681                          CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value;
  6682  
  6683                          if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number;
  6684                          else CORE.Input.Gamepad.lastButtonPressed = -1;
  6685                      }
  6686                  }
  6687                  else if (gamepadEvent.type == JS_EVENT_AXIS)
  6688                  {
  6689                      //TRACELOG(LOG_WARNING, "RPI: Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
  6690  
  6691                      if (gamepadEvent.number < MAX_GAMEPAD_AXIS)
  6692                      {
  6693                          // NOTE: Scaling of gamepadEvent.value to get values between -1..1
  6694                          CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768;
  6695                      }
  6696                  }
  6697              }
  6698              else WaitTime(0.001);    // Sleep for 1 ms to avoid hogging CPU time
  6699          }
  6700      }
  6701  
  6702      return NULL;
  6703  }
  6704  #endif  // PLATFORM_RPI || PLATFORM_DRM
  6705  
  6706  #if defined(PLATFORM_DRM)
  6707  // Search matching DRM mode in connector's mode list
  6708  static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode)
  6709  {
  6710      if (NULL == connector) return -1;
  6711      if (NULL == mode) return -1;
  6712  
  6713      // safe bitwise comparison of two modes
  6714      #define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b)) ? sizeof(a) : sizeof(b))
  6715  
  6716      for (size_t i = 0; i < connector->count_modes; i++)
  6717      {
  6718          TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
  6719              connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
  6720  
  6721          if (0 == BINCMP(&CORE.Window.crtc->mode, &CORE.Window.connector->modes[i])) return i;
  6722      }
  6723  
  6724      return -1;
  6725  
  6726      #undef BINCMP
  6727  }
  6728  
  6729  // Search exactly matching DRM connector mode in connector's list
  6730  static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
  6731  {
  6732      TRACELOG(LOG_TRACE, "DISPLAY: Searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no");
  6733  
  6734      if (NULL == connector) return -1;
  6735  
  6736      for (int i = 0; i < CORE.Window.connector->count_modes; i++)
  6737      {
  6738          const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
  6739  
  6740          TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
  6741  
  6742          if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) continue;
  6743  
  6744          if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i;
  6745      }
  6746  
  6747      TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found");
  6748      return -1;
  6749  }
  6750  
  6751  // Search the nearest matching DRM connector mode in connector's list
  6752  static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
  6753  {
  6754      TRACELOG(LOG_TRACE, "DISPLAY: Searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no");
  6755  
  6756      if (NULL == connector) return -1;
  6757  
  6758      int nearestIndex = -1;
  6759      for (int i = 0; i < CORE.Window.connector->count_modes; i++)
  6760      {
  6761          const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
  6762  
  6763          TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
  6764              (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
  6765  
  6766          if ((mode->hdisplay < width) || (mode->vdisplay < height))
  6767          {
  6768              TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small");
  6769              continue;
  6770          }
  6771  
  6772          if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced))
  6773          {
  6774              TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode");
  6775              continue;
  6776          }
  6777  
  6778          if (nearestIndex < 0)
  6779          {
  6780              nearestIndex = i;
  6781              continue;
  6782          }
  6783  
  6784          const int widthDiff = abs(mode->hdisplay - width);
  6785          const int heightDiff = abs(mode->vdisplay - height);
  6786          const int fpsDiff = abs(mode->vrefresh - fps);
  6787  
  6788          const int nearestWidthDiff = abs(CORE.Window.connector->modes[nearestIndex].hdisplay - width);
  6789          const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height);
  6790          const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps);
  6791  
  6792          if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) {
  6793              nearestIndex = i;
  6794          }
  6795      }
  6796  
  6797      return nearestIndex;
  6798  }
  6799  #endif
  6800  
  6801  #if defined(SUPPORT_EVENTS_AUTOMATION)
  6802  // NOTE: Loading happens over AutomationEvent *events
  6803  // TODO: This system should probably be redesigned
  6804  static void LoadAutomationEvents(const char *fileName)
  6805  {
  6806      //unsigned char fileId[4] = { 0 };
  6807  
  6808      // Load binary
  6809      /*
  6810      FILE *repFile = fopen(fileName, "rb");
  6811      fread(fileId, 4, 1, repFile);
  6812  
  6813      if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' '))
  6814      {
  6815          fread(&eventCount, sizeof(int), 1, repFile);
  6816          TraceLog(LOG_WARNING, "Events loaded: %i\n", eventCount);
  6817          fread(events, sizeof(AutomationEvent), eventCount, repFile);
  6818      }
  6819  
  6820      fclose(repFile);
  6821      */
  6822  
  6823      // Load events (text file)
  6824      FILE *repFile = fopen(fileName, "rt");
  6825  
  6826      if (repFile != NULL)
  6827      {
  6828          unsigned int count = 0;
  6829          char buffer[256] = { 0 };
  6830  
  6831          fgets(buffer, 256, repFile);
  6832  
  6833          while (!feof(repFile))
  6834          {
  6835              if (buffer[0] == 'c') sscanf(buffer, "c %i", &eventCount);
  6836              else if (buffer[0] == 'e')
  6837              {
  6838                  sscanf(buffer, "e %d %d %d %d %d", &events[count].frame, &events[count].type,
  6839                         &events[count].params[0], &events[count].params[1], &events[count].params[2]);
  6840  
  6841                  count++;
  6842              }
  6843  
  6844              fgets(buffer, 256, repFile);
  6845          }
  6846  
  6847          if (count != eventCount) TRACELOG(LOG_WARNING, "Events count provided is different than count");
  6848  
  6849          fclose(repFile);
  6850      }
  6851  
  6852      TRACELOG(LOG_WARNING, "Events loaded: %i", eventCount);
  6853  }
  6854  
  6855  // Export recorded events into a file
  6856  static void ExportAutomationEvents(const char *fileName)
  6857  {
  6858      unsigned char fileId[4] = "rEP ";
  6859  
  6860      // Save as binary
  6861      /*
  6862      FILE *repFile = fopen(fileName, "wb");
  6863      fwrite(fileId, 4, 1, repFile);
  6864      fwrite(&eventCount, sizeof(int), 1, repFile);
  6865      fwrite(events, sizeof(AutomationEvent), eventCount, repFile);
  6866      fclose(repFile);
  6867      */
  6868  
  6869      // Export events as text
  6870      FILE *repFile = fopen(fileName, "wt");
  6871  
  6872      if (repFile != NULL)
  6873      {
  6874          fprintf(repFile, "# Automation events list\n");
  6875          fprintf(repFile, "#    c <events_count>\n");
  6876          fprintf(repFile, "#    e <frame> <event_type> <param0> <param1> <param2> // <event_type_name>\n");
  6877  
  6878          fprintf(repFile, "c %i\n", eventCount);
  6879          for (int i = 0; i < eventCount; i++)
  6880          {
  6881              fprintf(repFile, "e %i %i %i %i %i // %s\n", events[i].frame, events[i].type,
  6882                      events[i].params[0], events[i].params[1], events[i].params[2], autoEventTypeName[events[i].type]);
  6883          }
  6884  
  6885          fclose(repFile);
  6886      }
  6887  }
  6888  
  6889  // EndDrawing() -> After PollInputEvents()
  6890  // Check event in current frame and save into the events[i] array
  6891  static void RecordAutomationEvent(unsigned int frame)
  6892  {
  6893      for (int key = 0; key < MAX_KEYBOARD_KEYS; key++)
  6894      {
  6895          // INPUT_KEY_UP (only saved once)
  6896          if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key])
  6897          {
  6898              events[eventCount].frame = frame;
  6899              events[eventCount].type = INPUT_KEY_UP;
  6900              events[eventCount].params[0] = key;
  6901              events[eventCount].params[1] = 0;
  6902              events[eventCount].params[2] = 0;
  6903  
  6904              TRACELOG(LOG_INFO, "[%i] INPUT_KEY_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  6905              eventCount++;
  6906          }
  6907  
  6908          // INPUT_KEY_DOWN
  6909          if (CORE.Input.Keyboard.currentKeyState[key])
  6910          {
  6911              events[eventCount].frame = frame;
  6912              events[eventCount].type = INPUT_KEY_DOWN;
  6913              events[eventCount].params[0] = key;
  6914              events[eventCount].params[1] = 0;
  6915              events[eventCount].params[2] = 0;
  6916  
  6917              TRACELOG(LOG_INFO, "[%i] INPUT_KEY_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  6918              eventCount++;
  6919          }
  6920      }
  6921  
  6922      for (int button = 0; button < MAX_MOUSE_BUTTONS; button++)
  6923      {
  6924          // INPUT_MOUSE_BUTTON_UP
  6925          if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button])
  6926          {
  6927              events[eventCount].frame = frame;
  6928              events[eventCount].type = INPUT_MOUSE_BUTTON_UP;
  6929              events[eventCount].params[0] = button;
  6930              events[eventCount].params[1] = 0;
  6931              events[eventCount].params[2] = 0;
  6932  
  6933              TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  6934              eventCount++;
  6935          }
  6936  
  6937          // INPUT_MOUSE_BUTTON_DOWN
  6938          if (CORE.Input.Mouse.currentButtonState[button])
  6939          {
  6940              events[eventCount].frame = frame;
  6941              events[eventCount].type = INPUT_MOUSE_BUTTON_DOWN;
  6942              events[eventCount].params[0] = button;
  6943              events[eventCount].params[1] = 0;
  6944              events[eventCount].params[2] = 0;
  6945  
  6946              TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  6947              eventCount++;
  6948          }
  6949      }
  6950  
  6951      // INPUT_MOUSE_POSITION (only saved if changed)
  6952      if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) ||
  6953          ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y))
  6954      {
  6955          events[eventCount].frame = frame;
  6956          events[eventCount].type = INPUT_MOUSE_POSITION;
  6957          events[eventCount].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
  6958          events[eventCount].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
  6959          events[eventCount].params[2] = 0;
  6960  
  6961          TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  6962          eventCount++;
  6963      }
  6964  
  6965      // INPUT_MOUSE_WHEEL_MOTION
  6966      if (((int)CORE.Input.Mouse.currentWheelMove.x != (int)CORE.Input.Mouse.previousWheelMove.x) ||
  6967          ((int)CORE.Input.Mouse.currentWheelMove.y != (int)CORE.Input.Mouse.previousWheelMove.y))
  6968      {
  6969          events[eventCount].frame = frame;
  6970          events[eventCount].type = INPUT_MOUSE_WHEEL_MOTION;
  6971          events[eventCount].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x;
  6972          events[eventCount].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;;
  6973          events[eventCount].params[2] = 0;
  6974  
  6975          TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_WHEEL_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  6976          eventCount++;
  6977      }
  6978  
  6979      for (int id = 0; id < MAX_TOUCH_POINTS; id++)
  6980      {
  6981          // INPUT_TOUCH_UP
  6982          if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id])
  6983          {
  6984              events[eventCount].frame = frame;
  6985              events[eventCount].type = INPUT_TOUCH_UP;
  6986              events[eventCount].params[0] = id;
  6987              events[eventCount].params[1] = 0;
  6988              events[eventCount].params[2] = 0;
  6989  
  6990              TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  6991              eventCount++;
  6992          }
  6993  
  6994          // INPUT_TOUCH_DOWN
  6995          if (CORE.Input.Touch.currentTouchState[id])
  6996          {
  6997              events[eventCount].frame = frame;
  6998              events[eventCount].type = INPUT_TOUCH_DOWN;
  6999              events[eventCount].params[0] = id;
  7000              events[eventCount].params[1] = 0;
  7001              events[eventCount].params[2] = 0;
  7002  
  7003              TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  7004              eventCount++;
  7005          }
  7006  
  7007          // INPUT_TOUCH_POSITION
  7008          // TODO: It requires the id!
  7009          /*
  7010          if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) ||
  7011              ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y))
  7012          {
  7013              events[eventCount].frame = frame;
  7014              events[eventCount].type = INPUT_TOUCH_POSITION;
  7015              events[eventCount].params[0] = id;
  7016              events[eventCount].params[1] = (int)CORE.Input.Touch.currentPosition[id].x;
  7017              events[eventCount].params[2] = (int)CORE.Input.Touch.currentPosition[id].y;
  7018  
  7019              TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  7020              eventCount++;
  7021          }
  7022          */
  7023      }
  7024  
  7025      for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++)
  7026      {
  7027          // INPUT_GAMEPAD_CONNECT
  7028          /*
  7029          if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
  7030              (CORE.Input.Gamepad.currentState[gamepad] == true)) // Check if changed to ready
  7031          {
  7032              // TODO: Save gamepad connect event
  7033          }
  7034          */
  7035  
  7036          // INPUT_GAMEPAD_DISCONNECT
  7037          /*
  7038          if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
  7039              (CORE.Input.Gamepad.currentState[gamepad] == false)) // Check if changed to not-ready
  7040          {
  7041              // TODO: Save gamepad disconnect event
  7042          }
  7043          */
  7044  
  7045          for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++)
  7046          {
  7047              // INPUT_GAMEPAD_BUTTON_UP
  7048              if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button])
  7049              {
  7050                  events[eventCount].frame = frame;
  7051                  events[eventCount].type = INPUT_GAMEPAD_BUTTON_UP;
  7052                  events[eventCount].params[0] = gamepad;
  7053                  events[eventCount].params[1] = button;
  7054                  events[eventCount].params[2] = 0;
  7055  
  7056                  TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  7057                  eventCount++;
  7058              }
  7059  
  7060              // INPUT_GAMEPAD_BUTTON_DOWN
  7061              if (CORE.Input.Gamepad.currentButtonState[gamepad][button])
  7062              {
  7063                  events[eventCount].frame = frame;
  7064                  events[eventCount].type = INPUT_GAMEPAD_BUTTON_DOWN;
  7065                  events[eventCount].params[0] = gamepad;
  7066                  events[eventCount].params[1] = button;
  7067                  events[eventCount].params[2] = 0;
  7068  
  7069                  TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  7070                  eventCount++;
  7071              }
  7072          }
  7073  
  7074          for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
  7075          {
  7076              // INPUT_GAMEPAD_AXIS_MOTION
  7077              if (CORE.Input.Gamepad.axisState[gamepad][axis] > 0.1f)
  7078              {
  7079                  events[eventCount].frame = frame;
  7080                  events[eventCount].type = INPUT_GAMEPAD_AXIS_MOTION;
  7081                  events[eventCount].params[0] = gamepad;
  7082                  events[eventCount].params[1] = axis;
  7083                  events[eventCount].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
  7084  
  7085                  TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_AXIS_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  7086                  eventCount++;
  7087              }
  7088          }
  7089      }
  7090  
  7091      // INPUT_GESTURE
  7092      if (GESTURES.current != GESTURE_NONE)
  7093      {
  7094          events[eventCount].frame = frame;
  7095          events[eventCount].type = INPUT_GESTURE;
  7096          events[eventCount].params[0] = GESTURES.current;
  7097          events[eventCount].params[1] = 0;
  7098          events[eventCount].params[2] = 0;
  7099  
  7100          TRACELOG(LOG_INFO, "[%i] INPUT_GESTURE: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
  7101          eventCount++;
  7102      }
  7103  }
  7104  
  7105  // Play automation event
  7106  static void PlayAutomationEvent(unsigned int frame)
  7107  {
  7108      for (unsigned int i = 0; i < eventCount; i++)
  7109      {
  7110          if (events[i].frame == frame)
  7111          {
  7112              switch (events[i].type)
  7113              {
  7114                  // Input events
  7115                  case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = false; break;             // param[0]: key
  7116                  case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = true; break;            // param[0]: key
  7117                  case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = false; break;    // param[0]: key
  7118                  case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = true; break;   // param[0]: key
  7119                  case INPUT_MOUSE_POSITION:      // param[0]: x, param[1]: y
  7120                  {
  7121                      CORE.Input.Mouse.currentPosition.x = (float)events[i].params[0];
  7122                      CORE.Input.Mouse.currentPosition.y = (float)events[i].params[1];
  7123                  } break;
  7124                  case INPUT_MOUSE_WHEEL_MOTION:  // param[0]: x delta, param[1]: y delta
  7125                  {
  7126                      CORE.Input.Mouse.currentWheelMove.x = (float)events[i].params[0]; break;
  7127                      CORE.Input.Mouse.currentWheelMove.y = (float)events[i].params[1]; break;
  7128                  } break;
  7129                  case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[events[i].params[0]] = false; break;            // param[0]: id
  7130                  case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[events[i].params[0]] = true; break;           // param[0]: id
  7131                  case INPUT_TOUCH_POSITION:      // param[0]: id, param[1]: x, param[2]: y
  7132                  {
  7133                      CORE.Input.Touch.position[events[i].params[0]].x = (float)events[i].params[1];
  7134                      CORE.Input.Touch.position[events[i].params[0]].y = (float)events[i].params[2];
  7135                  } break;
  7136                  case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = true; break;                // param[0]: gamepad
  7137                  case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = false; break;            // param[0]: gamepad
  7138                  case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = false; break;    // param[0]: gamepad, param[1]: button
  7139                  case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = true; break;   // param[0]: gamepad, param[1]: button
  7140                  case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
  7141                  {
  7142                      CORE.Input.Gamepad.axisState[events[i].params[0]][events[i].params[1]] = ((float)events[i].params[2]/32768.0f);
  7143                  } break;
  7144                  case INPUT_GESTURE: GESTURES.current = events[i].params[0]; break;     // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
  7145  
  7146                  // Window events
  7147                  case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
  7148                  case WINDOW_MAXIMIZE: MaximizeWindow(); break;
  7149                  case WINDOW_MINIMIZE: MinimizeWindow(); break;
  7150                  case WINDOW_RESIZE: SetWindowSize(events[i].params[0], events[i].params[1]); break;
  7151  
  7152                  // Custom events
  7153                  case ACTION_TAKE_SCREENSHOT:
  7154                  {
  7155                      TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
  7156                      screenshotCounter++;
  7157                  } break;
  7158                  case ACTION_SETTARGETFPS: SetTargetFPS(events[i].params[0]); break;
  7159                  default: break;
  7160              }
  7161          }
  7162      }
  7163  }
  7164  #endif
  7165  
  7166  #if !defined(SUPPORT_MODULE_RTEXT)
  7167  // Formatting of text with variables to 'embed'
  7168  // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
  7169  const char *TextFormat(const char *text, ...)
  7170  {
  7171  #ifndef MAX_TEXTFORMAT_BUFFERS
  7172      #define MAX_TEXTFORMAT_BUFFERS      4        // Maximum number of static buffers for text formatting
  7173  #endif
  7174  #ifndef MAX_TEXT_BUFFER_LENGTH
  7175      #define MAX_TEXT_BUFFER_LENGTH   1024        // Maximum size of static text buffer
  7176  #endif
  7177  
  7178      // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
  7179      static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
  7180      static int index = 0;
  7181  
  7182      char *currentBuffer = buffers[index];
  7183      memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH);   // Clear buffer before using
  7184  
  7185      va_list args;
  7186      va_start(args, text);
  7187      vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
  7188      va_end(args);
  7189  
  7190      index += 1;     // Move to next buffer for next function call
  7191      if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
  7192  
  7193      return currentBuffer;
  7194  }
  7195  #endif // !SUPPORT_MODULE_RTEXT