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