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